##// END OF EJS Templates
rust: Add config parsing support for more value types...
Simon Sapin -
r47339:305d74c2 default
parent child Browse files
Show More
@@ -15,6 +15,7 use crate::utils::files::get_bytes_from_
15 15 use format_bytes::{write_bytes, DisplayBytes};
16 16 use std::env;
17 17 use std::path::{Path, PathBuf};
18 use std::str;
18 19
19 20 use crate::errors::{HgResultExt, IoResultExt};
20 21
@@ -61,6 +62,32 pub fn parse_bool(v: &[u8]) -> Option<bo
61 62 }
62 63 }
63 64
65 pub fn parse_byte_size(value: &[u8]) -> Option<u64> {
66 let value = str::from_utf8(value).ok()?.to_ascii_lowercase();
67 const UNITS: &[(&str, u64)] = &[
68 ("g", 1 << 30),
69 ("gb", 1 << 30),
70 ("m", 1 << 20),
71 ("mb", 1 << 20),
72 ("k", 1 << 10),
73 ("kb", 1 << 10),
74 ("b", 1 << 0), // Needs to be last
75 ];
76 for &(unit, multiplier) in UNITS {
77 // TODO: use `value.strip_suffix(unit)` when we require Rust 1.45+
78 if value.ends_with(unit) {
79 let value_before_unit = &value[..value.len() - unit.len()];
80 let float: f64 = value_before_unit.trim().parse().ok()?;
81 if float >= 0.0 {
82 return Some((float * multiplier as f64).round() as u64);
83 } else {
84 return None;
85 }
86 }
87 }
88 value.parse().ok()
89 }
90
64 91 impl Config {
65 92 /// Load system and user configuration from various files.
66 93 ///
@@ -231,16 +258,14 impl Config {
231 258 Ok(repo_config)
232 259 }
233 260
234 /// Returns an `Err` if the first value found is not a valid boolean.
235 /// Otherwise, returns an `Ok(option)`, where `option` is the boolean if
236 /// found, or `None`.
237 pub fn get_option(
238 &self,
261 fn get_parse<'config, T: 'config>(
262 &'config self,
239 263 section: &[u8],
240 264 item: &[u8],
241 ) -> Result<Option<bool>, ConfigParseError> {
265 parse: impl Fn(&'config [u8]) -> Option<T>,
266 ) -> Result<Option<T>, ConfigParseError> {
242 267 match self.get_inner(&section, &item) {
243 Some((layer, v)) => match parse_bool(&v.bytes) {
268 Some((layer, v)) => match parse(&v.bytes) {
244 269 Some(b) => Ok(Some(b)),
245 270 None => Err(ConfigParseError {
246 271 origin: layer.origin.to_owned(),
@@ -252,6 +277,50 impl Config {
252 277 }
253 278 }
254 279
280 /// Returns an `Err` if the first value found is not a valid UTF-8 string.
281 /// Otherwise, returns an `Ok(value)` if found, or `None`.
282 pub fn get_str(
283 &self,
284 section: &[u8],
285 item: &[u8],
286 ) -> Result<Option<&str>, ConfigParseError> {
287 self.get_parse(section, item, |value| str::from_utf8(value).ok())
288 }
289
290 /// Returns an `Err` if the first value found is not a valid unsigned
291 /// integer. Otherwise, returns an `Ok(value)` if found, or `None`.
292 pub fn get_u32(
293 &self,
294 section: &[u8],
295 item: &[u8],
296 ) -> Result<Option<u32>, ConfigParseError> {
297 self.get_parse(section, item, |value| {
298 str::from_utf8(value).ok()?.parse().ok()
299 })
300 }
301
302 /// Returns an `Err` if the first value found is not a valid file size
303 /// value such as `30` (default unit is bytes), `7 MB`, or `42.5 kb`.
304 /// Otherwise, returns an `Ok(value_in_bytes)` if found, or `None`.
305 pub fn get_byte_size(
306 &self,
307 section: &[u8],
308 item: &[u8],
309 ) -> Result<Option<u64>, ConfigParseError> {
310 self.get_parse(section, item, parse_byte_size)
311 }
312
313 /// Returns an `Err` if the first value found is not a valid boolean.
314 /// Otherwise, returns an `Ok(option)`, where `option` is the boolean if
315 /// found, or `None`.
316 pub fn get_option(
317 &self,
318 section: &[u8],
319 item: &[u8],
320 ) -> Result<Option<bool>, ConfigParseError> {
321 self.get_parse(section, item, parse_bool)
322 }
323
255 324 /// Returns the corresponding boolean in the config. Returns `Ok(false)`
256 325 /// if the value is not found, an `Err` if it's not a valid boolean.
257 326 pub fn get_bool(
@@ -317,7 +386,8 mod tests {
317 386 let base_config_path = tmpdir_path.join("base.rc");
318 387 let mut config_file = File::create(&base_config_path).unwrap();
319 388 let data =
320 b"[section]\nitem=value0\n%include included.rc\nitem=value2";
389 b"[section]\nitem=value0\n%include included.rc\nitem=value2\n\
390 [section2]\ncount = 4\nsize = 1.5 KB\nnot-count = 1.5\nnot-size = 1 ub";
321 391 config_file.write_all(data).unwrap();
322 392
323 393 let sources = vec![ConfigSource::AbsPath(base_config_path)];
@@ -339,5 +409,13 mod tests {
339 409 config.get_all(b"section", b"item"),
340 410 [b"value2", b"value1", b"value0"]
341 411 );
412
413 assert_eq!(config.get_u32(b"section2", b"count").unwrap(), Some(4));
414 assert_eq!(
415 config.get_byte_size(b"section2", b"size").unwrap(),
416 Some(1024 + 512)
417 );
418 assert!(config.get_u32(b"section2", b"not-count").is_err());
419 assert!(config.get_byte_size(b"section2", b"not-size").is_err());
342 420 }
343 421 }
General Comments 0
You need to be logged in to leave comments. Login now