##// 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 b' use crate::utils::files::get_bytes_from_'
15 use format_bytes::{write_bytes, DisplayBytes};
15 use format_bytes::{write_bytes, DisplayBytes};
16 use std::env;
16 use std::env;
17 use std::path::{Path, PathBuf};
17 use std::path::{Path, PathBuf};
18 use std::str;
18
19
19 use crate::errors::{HgResultExt, IoResultExt};
20 use crate::errors::{HgResultExt, IoResultExt};
20
21
@@ -61,6 +62,32 b' 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 impl Config {
91 impl Config {
65 /// Load system and user configuration from various files.
92 /// Load system and user configuration from various files.
66 ///
93 ///
@@ -231,16 +258,14 b' impl Config {'
231 Ok(repo_config)
258 Ok(repo_config)
232 }
259 }
233
260
234 /// Returns an `Err` if the first value found is not a valid boolean.
261 fn get_parse<'config, T: 'config>(
235 /// Otherwise, returns an `Ok(option)`, where `option` is the boolean if
262 &'config self,
236 /// found, or `None`.
237 pub fn get_option(
238 &self,
239 section: &[u8],
263 section: &[u8],
240 item: &[u8],
264 item: &[u8],
241 ) -> Result<Option<bool>, ConfigParseError> {
265 parse: impl Fn(&'config [u8]) -> Option<T>,
266 ) -> Result<Option<T>, ConfigParseError> {
242 match self.get_inner(&section, &item) {
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 Some(b) => Ok(Some(b)),
269 Some(b) => Ok(Some(b)),
245 None => Err(ConfigParseError {
270 None => Err(ConfigParseError {
246 origin: layer.origin.to_owned(),
271 origin: layer.origin.to_owned(),
@@ -252,6 +277,50 b' 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 /// Returns the corresponding boolean in the config. Returns `Ok(false)`
324 /// Returns the corresponding boolean in the config. Returns `Ok(false)`
256 /// if the value is not found, an `Err` if it's not a valid boolean.
325 /// if the value is not found, an `Err` if it's not a valid boolean.
257 pub fn get_bool(
326 pub fn get_bool(
@@ -317,7 +386,8 b' mod tests {'
317 let base_config_path = tmpdir_path.join("base.rc");
386 let base_config_path = tmpdir_path.join("base.rc");
318 let mut config_file = File::create(&base_config_path).unwrap();
387 let mut config_file = File::create(&base_config_path).unwrap();
319 let data =
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 config_file.write_all(data).unwrap();
391 config_file.write_all(data).unwrap();
322
392
323 let sources = vec![ConfigSource::AbsPath(base_config_path)];
393 let sources = vec![ConfigSource::AbsPath(base_config_path)];
@@ -339,5 +409,13 b' mod tests {'
339 config.get_all(b"section", b"item"),
409 config.get_all(b"section", b"item"),
340 [b"value2", b"value1", b"value0"]
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