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(§ion, &item) { |
|
267 | match self.get_inner(§ion, &item) { | |
243 |
Some((layer, v)) => match parse |
|
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