Mercurial > public > mercurial-scm > hg
view rust/hg-core/src/config/values.rs @ 47379:f6bb181c75f8
rust: Parse "subinclude"d files along the way, not later
When parsing a `.hgignore` file and encountering an `include:` line,
the included file is parsed recursively right then in a depth-first fashion.
With `subinclude:` however included files were parsed (recursively) much later.
This changes it to be expanded during parsing, like `.hgignore`.
The motivation for this is an upcoming changeset that needs to detect changes
in which files are ignored or not. The plan is to hash all ignore files while
they are being read, and store that hash in the dirstate (in v2 format).
In order to allow a potential alternative implementations to read that format,
the algorithm to compute that hash must be documented. Having a well-defined
depth-first ordering for the tree of (sub-)included files makes that easier.
Differential Revision: https://phab.mercurial-scm.org/D10834
author | Simon Sapin <simon.sapin@octobus.net> |
---|---|
date | Wed, 02 Jun 2021 18:03:43 +0200 |
parents | 1b7c0b10d930 |
children | 6961eca0b3ee |
line wrap: on
line source
//! 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<bool> { 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<u64> { 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() } #[test] fn test_parse_byte_size() { assert_eq!(parse_byte_size(b""), None); assert_eq!(parse_byte_size(b"b"), None); assert_eq!(parse_byte_size(b"12"), Some(12)); assert_eq!(parse_byte_size(b"12b"), Some(12)); assert_eq!(parse_byte_size(b"12 b"), Some(12)); assert_eq!(parse_byte_size(b"12.1 b"), Some(12)); assert_eq!(parse_byte_size(b"1.1 K"), Some(1126)); assert_eq!(parse_byte_size(b"1.1 kB"), Some(1126)); assert_eq!(parse_byte_size(b"-12 b"), None); assert_eq!(parse_byte_size(b"-0.1 b"), None); assert_eq!(parse_byte_size(b"0.1 b"), Some(0)); assert_eq!(parse_byte_size(b"12.1 b"), Some(12)); }