diff --git a/rust/hg-core/src/config.rs b/rust/hg-core/src/config.rs --- a/rust/hg-core/src/config.rs +++ b/rust/hg-core/src/config.rs @@ -11,5 +11,6 @@ mod config; mod layer; +mod values; pub use config::{Config, ConfigValueParseError}; pub use layer::{ConfigError, ConfigParseError}; diff --git a/rust/hg-core/src/config/config.rs b/rust/hg-core/src/config/config.rs --- a/rust/hg-core/src/config/config.rs +++ b/rust/hg-core/src/config/config.rs @@ -8,6 +8,7 @@ // GNU General Public License version 2 or any later version. use super::layer; +use super::values; use crate::config::layer::{ ConfigError, ConfigLayer, ConfigOrigin, ConfigValue, }; @@ -64,40 +65,6 @@ pub expected_type: &'static str, } -pub fn parse_bool(v: &[u8]) -> Option { - match v.to_ascii_lowercase().as_slice() { - b"1" | b"yes" | b"true" | b"on" | b"always" => Some(true), - b"0" | b"no" | b"false" | b"off" | b"never" => Some(false), - _ => None, - } -} - -pub fn parse_byte_size(value: &[u8]) -> Option { - let value = str::from_utf8(value).ok()?.to_ascii_lowercase(); - const UNITS: &[(&str, u64)] = &[ - ("g", 1 << 30), - ("gb", 1 << 30), - ("m", 1 << 20), - ("mb", 1 << 20), - ("k", 1 << 10), - ("kb", 1 << 10), - ("b", 1 << 0), // Needs to be last - ]; - for &(unit, multiplier) in UNITS { - // TODO: use `value.strip_suffix(unit)` when we require Rust 1.45+ - if value.ends_with(unit) { - let value_before_unit = &value[..value.len() - unit.len()]; - let float: f64 = value_before_unit.trim().parse().ok()?; - if float >= 0.0 { - return Some((float * multiplier as f64).round() as u64); - } else { - return None; - } - } - } - value.parse().ok() -} - impl Config { /// Load system and user configuration from various files. /// @@ -324,7 +291,7 @@ section: &[u8], item: &[u8], ) -> Result, ConfigValueParseError> { - self.get_parse(section, item, "byte quantity", parse_byte_size) + self.get_parse(section, item, "byte quantity", values::parse_byte_size) } /// Returns an `Err` if the first value found is not a valid boolean. @@ -335,7 +302,7 @@ section: &[u8], item: &[u8], ) -> Result, ConfigValueParseError> { - self.get_parse(section, item, "boolean", parse_bool) + self.get_parse(section, item, "boolean", values::parse_bool) } /// Returns the corresponding boolean in the config. Returns `Ok(false)` diff --git a/rust/hg-core/src/config/values.rs b/rust/hg-core/src/config/values.rs new file mode 100644 --- /dev/null +++ b/rust/hg-core/src/config/values.rs @@ -0,0 +1,43 @@ +//! Parsing functions for various type of configuration values. +//! +//! Returning `None` indicates a syntax error. Using a `Result` would be more +//! correct but would take more boilerplate for converting between error types, +//! compared to using `.ok()` on inner results of various error types to +//! convert them all to options. The `Config::get_parse` method later converts +//! those options to results with `ConfigValueParseError`, which contains +//! details about where the value came from (but omits details of what’s +//! invalid inside the value). + +pub(super) fn parse_bool(v: &[u8]) -> Option { + match v.to_ascii_lowercase().as_slice() { + b"1" | b"yes" | b"true" | b"on" | b"always" => Some(true), + b"0" | b"no" | b"false" | b"off" | b"never" => Some(false), + _ => None, + } +} + +pub(super) fn parse_byte_size(value: &[u8]) -> Option { + let value = std::str::from_utf8(value).ok()?.to_ascii_lowercase(); + const UNITS: &[(&str, u64)] = &[ + ("g", 1 << 30), + ("gb", 1 << 30), + ("m", 1 << 20), + ("mb", 1 << 20), + ("k", 1 << 10), + ("kb", 1 << 10), + ("b", 1 << 0), // Needs to be last + ]; + for &(unit, multiplier) in UNITS { + // TODO: use `value.strip_suffix(unit)` when we require Rust 1.45+ + if value.ends_with(unit) { + let value_before_unit = &value[..value.len() - unit.len()]; + let float: f64 = value_before_unit.trim().parse().ok()?; + if float >= 0.0 { + return Some((float * multiplier as f64).round() as u64); + } else { + return None; + } + } + } + value.parse().ok() +}