diff --git a/rust/hg-core/src/configparser/c_api.rs b/rust/hg-core/src/configparser/c_api.rs --- a/rust/hg-core/src/configparser/c_api.rs +++ b/rust/hg-core/src/configparser/c_api.rs @@ -5,7 +5,8 @@ * GNU General Public License version 2. */ -//! This module exports some symbols to allow calling the config parser from C/C++ +//! This module exports some symbols to allow calling the config parser from +//! C/C++ use std::ffi::{CStr, OsStr}; use std::os::raw::c_char; use std::path::Path; @@ -64,7 +65,10 @@ /// error object is UTF-8 encoded text, and errors can span multiple lines. #[cfg(unix)] #[no_mangle] -pub extern "C" fn hgrc_configset_load_path(cfg: *mut ConfigSet, path: *const c_char) -> *mut Bytes { +pub extern "C" fn hgrc_configset_load_path( + cfg: *mut ConfigSet, + path: *const c_char, +) -> *mut Bytes { debug_assert!(!path.is_null()); debug_assert!(!cfg.is_null()); @@ -81,7 +85,9 @@ /// Load system config files #[no_mangle] -pub extern "C" fn hgrc_configset_load_system(cfg: *mut ConfigSet) -> *mut Bytes { +pub extern "C" fn hgrc_configset_load_system( + cfg: *mut ConfigSet, +) -> *mut Bytes { debug_assert!(!cfg.is_null()); let cfg = unsafe { &mut *cfg }; @@ -100,8 +106,9 @@ errors_to_bytes(cfg.load_user()) } -/// Returns a Bytes object holding the configuration value for the corresponding -/// section name and key. If there is no matching section/key pair, returns nullptr. +/// Returns a Bytes object holding the configuration value for the +/// corresponding section name and key. If there is no matching section/key +/// pair, returns nullptr. #[no_mangle] pub extern "C" fn hgrc_configset_get( cfg: *const ConfigSet, @@ -130,8 +137,8 @@ len: usize, } -/// Returns the data pointer and length for a Bytes object, suitable for constructing -/// a folly::ByteRange. +/// Returns the data pointer and length for a Bytes object, suitable for +/// constructing a folly::ByteRange. #[no_mangle] pub extern "C" fn hgrc_bytes_data(bytes: *const Bytes) -> ByteData { debug_assert!(!bytes.is_null()); diff --git a/rust/hg-core/src/configparser/config.rs b/rust/hg-core/src/configparser/config.rs --- a/rust/hg-core/src/configparser/config.rs +++ b/rust/hg-core/src/configparser/config.rs @@ -40,7 +40,8 @@ #[derive(Clone, Debug)] pub struct ValueSource { value: Option, - source: Bytes, // global, user, repo, "--config", or an extension name, etc. + source: Bytes, /* global, user, repo, "--config", or an extension name, + * etc. */ location: Option, } @@ -58,7 +59,15 @@ #[derive(Default)] pub struct Options { source: Bytes, - filters: Vec) -> Option<(Bytes, Bytes, Option)>>>, + filters: Vec< + Box< + dyn Fn( + Bytes, + Bytes, + Option, + ) -> Option<(Bytes, Bytes, Option)>, + >, + >, } impl ConfigSet { @@ -72,9 +81,10 @@ /// If `path` is a directory, it is ignored. /// If `path` is a file, it will be loaded directly. /// - /// A config file can use `%include` to load other paths (directories or files). They will - /// be loaded recursively. Includes take effect in place, instead of deferred. For example, - /// with the following two files: + /// A config file can use `%include` to load other paths (directories or + /// files). They will be loaded recursively. Includes take effect in + /// place, instead of deferred. For example, with the following two + /// files: /// /// ```plain,ignore /// # This is 1.rc @@ -91,28 +101,43 @@ /// /// After loading `1.rc`. `x` is set to 3 and `y` is set to 2. /// - /// Loading a file that is already parsed or being parsed by this `load_path` call is ignored, - /// to avoid infinite loop. A separate `load_path` call would not ignore files loaded by - /// other `load_path` calls. + /// Loading a file that is already parsed or being parsed by this + /// `load_path` call is ignored, to avoid infinite loop. A separate + /// `load_path` call would not ignore files loaded by other `load_path` + /// calls. /// - /// Return a list of errors. An error pasing a file will stop that file from loading, without - /// affecting other files. - pub fn load_path>(&mut self, path: P, opts: &Options) -> Vec { + /// Return a list of errors. An error pasing a file will stop that file + /// from loading, without affecting other files. + pub fn load_path>( + &mut self, + path: P, + opts: &Options, + ) -> Vec { let mut visited = HashSet::new(); let mut errors = Vec::new(); self.load_file(path.as_ref(), opts, &mut visited, &mut errors); errors } - /// Load content of an unnamed config file. The `ValueLocation`s of loaded config items will - /// have an empty `path`. + /// Load content of an unnamed config file. The `ValueLocation`s of loaded + /// config items will have an empty `path`. /// /// Return a list of errors. - pub fn parse>(&mut self, content: B, opts: &Options) -> Vec { + pub fn parse>( + &mut self, + content: B, + opts: &Options, + ) -> Vec { let mut visited = HashSet::new(); let mut errors = Vec::new(); let buf = content.into(); - self.load_file_content(Path::new(""), buf, opts, &mut visited, &mut errors); + self.load_file_content( + Path::new(""), + buf, + opts, + &mut visited, + &mut errors, + ); errors } @@ -131,17 +156,21 @@ /// Get config value for a given config. /// Return `None` if the config item does not exist or is unset. - pub fn get, N: Into>(&self, section: S, name: N) -> Option { + pub fn get, N: Into>( + &self, + section: S, + name: N, + ) -> Option { self.sections.get(§ion.into()).and_then(|section| { - section - .items - .get(&name.into()) - .and_then(|values| values.last().and_then(|value| value.value.clone())) + section.items.get(&name.into()).and_then(|values| { + values.last().and_then(|value| value.value.clone()) + }) }) } - /// Get detailed sources of a given config, including overrides, and source information. - /// The last item in the returned vector is the latest value that is considered effective. + /// Get detailed sources of a given config, including overrides, and source + /// information. The last item in the returned vector is the latest + /// value that is considered effective. /// /// Return an emtpy vector if the config does not exist. pub fn get_sources, N: Into>( @@ -151,12 +180,15 @@ ) -> Vec { self.sections .get(§ion.into()) - .and_then(|section| section.items.get(&name.into()).map(|values| values.clone())) + .and_then(|section| { + section.items.get(&name.into()).map(|values| values.clone()) + }) .unwrap_or(Vec::new()) } - /// Set a config item directly. `section`, `name` locates the config. `value` is the new value. - /// `source` is some annotation about who set it, ex. "reporc", "userrc", "--config", etc. + /// Set a config item directly. `section`, `name` locates the config. + /// `value` is the new value. `source` is some annotation about who set + /// it, ex. "reporc", "userrc", "--config", etc. pub fn set, N: Into>( &mut self, section: T, @@ -178,12 +210,14 @@ location: Option, opts: &Options, ) { - let filtered = opts - .filters - .iter() - .fold(Some((section, name, value)), move |acc, func| { - acc.and_then(|(section, name, value)| func(section, name, value)) - }); + let filtered = opts.filters.iter().fold( + Some((section, name, value)), + move |acc, func| { + acc.and_then(|(section, name, value)| { + func(section, name, value) + }) + }, + ); if let Some((section, name, value)) = filtered { self.sections .entry(section) @@ -227,15 +261,18 @@ self.load_file_content(path, buf, opts, visited, errors); } - Err(error) => errors.push(Error::Io(path.to_path_buf(), error)), + Err(error) => { + errors.push(Error::Io(path.to_path_buf(), error)) + } } } else { - // On Windows, a UNC path `\\?\C:\foo\.\x` will fail to canonicalize - // because it contains `.`. That path can be constructed by using - // `PathBuf::join` to concatenate a UNC path `\\?\C:\foo` with - // a "normal" path `.\x`. - // Try to fix it automatically by stripping the UNC prefix and retry - // `canonicalize`. `C:\foo\.\x` would be canonicalized without errors. + // On Windows, a UNC path `\\?\C:\foo\.\x` will fail to + // canonicalize because it contains `.`. That path can + // be constructed by using `PathBuf::join` to + // concatenate a UNC path `\\?\C:\foo` with a "normal" + // path `.\x`. Try to fix it automatically by stripping + // the UNC prefix and retry `canonicalize`. + // `C:\foo\.\x` would be canonicalized without errors. #[cfg(windows)] { if let Some(path_str) = path.to_str() { @@ -247,9 +284,9 @@ } } - // If `path.canonicalize` reports an error. It's usually the path cannot - // be resolved (ex. does not exist). It is considered normal and is not - // reported in `errors`. + // If `path.canonicalize` reports an error. It's usually the path + // cannot be resolved (ex. does not exist). It is considered + // normal and is not reported in `errors`. } fn load_file_content( @@ -265,50 +302,62 @@ let skip_include = path.parent().is_none(); // skip handling %include if path is empty // Utilities to avoid too much indentation. - let handle_value = |this: &mut ConfigSet, - pair: Pair, - section: Bytes, - name: Bytes, - location: ValueLocation| { - let pairs = pair.into_inner(); - let mut lines = Vec::with_capacity(1); - for pair in pairs { - if Rule::line == pair.as_rule() { - lines.push(extract(&buf, pair.as_span())); + let handle_value = + |this: &mut ConfigSet, + pair: Pair, + section: Bytes, + name: Bytes, + location: ValueLocation| { + let pairs = pair.into_inner(); + let mut lines = Vec::with_capacity(1); + for pair in pairs { + if Rule::line == pair.as_rule() { + lines.push(extract(&buf, pair.as_span())); + } } - } + + let value = match lines.len() { + 1 => lines[0].clone(), + _ => Bytes::from(lines.join(&b'\n')), + }; - let value = match lines.len() { - 1 => lines[0].clone(), - _ => Bytes::from(lines.join(&b'\n')), + let (start, end) = strip_offsets(&value, 0, value.len()); + let value = value.slice(start, end); + + this.set_internal( + section, + name, + value.into(), + location.into(), + opts, + ) }; - let (start, end) = strip_offsets(&value, 0, value.len()); - let value = value.slice(start, end); - - this.set_internal(section, name, value.into(), location.into(), opts) - }; - - let handle_config_item = |this: &mut ConfigSet, pair: Pair, section: Bytes| { - let pairs = pair.into_inner(); - let mut name = Bytes::new(); - for pair in pairs { - match pair.as_rule() { - Rule::config_name => name = extract(&buf, pair.as_span()), - Rule::value => { - let span = pair.as_span(); - let location = ValueLocation { - path: shared_path.clone(), - content: buf.clone(), - location: span.start()..span.end(), - }; - return handle_value(this, pair, section, name, location); + let handle_config_item = + |this: &mut ConfigSet, pair: Pair, section: Bytes| { + let pairs = pair.into_inner(); + let mut name = Bytes::new(); + for pair in pairs { + match pair.as_rule() { + Rule::config_name => { + name = extract(&buf, pair.as_span()) + } + Rule::value => { + let span = pair.as_span(); + let location = ValueLocation { + path: shared_path.clone(), + content: buf.clone(), + location: span.start()..span.end(), + }; + return handle_value( + this, pair, section, name, location, + ); + } + _ => (), } - _ => (), } - } - unreachable!(); - }; + unreachable!(); + }; let handle_section = |pair: Pair, section: &mut Bytes| { let pairs = pair.into_inner(); @@ -324,51 +373,63 @@ unreachable!(); }; - let mut handle_include = |this: &mut ConfigSet, pair: Pair, errors: &mut Vec| { - let pairs = pair.into_inner(); - for pair in pairs { - match pair.as_rule() { - Rule::line => { - if !skip_include { - let include_path = pair.as_str(); - let full_include_path = - path.parent().unwrap().join(expand_path(include_path)); - this.load_file(&full_include_path, opts, visited, errors); + let mut handle_include = + |this: &mut ConfigSet, pair: Pair, errors: &mut Vec| { + let pairs = pair.into_inner(); + for pair in pairs { + match pair.as_rule() { + Rule::line => { + if !skip_include { + let include_path = pair.as_str(); + let full_include_path = path + .parent() + .unwrap() + .join(expand_path(include_path)); + this.load_file( + &full_include_path, + opts, + visited, + errors, + ); + } } + _ => (), } - _ => (), } - } - }; + }; - let handle_unset = |this: &mut ConfigSet, pair: Pair, section: &Bytes| { - let unset_span = pair.as_span(); - let pairs = pair.into_inner(); - for pair in pairs { - match pair.as_rule() { - Rule::config_name => { - let name = extract(&buf, pair.as_span()); - let location = ValueLocation { - path: shared_path.clone(), - content: buf.clone(), - location: unset_span.start()..unset_span.end(), - }; - return this.set_internal( - section.clone(), - name, - None, - location.into(), - opts, - ); + let handle_unset = + |this: &mut ConfigSet, pair: Pair, section: &Bytes| { + let unset_span = pair.as_span(); + let pairs = pair.into_inner(); + for pair in pairs { + match pair.as_rule() { + Rule::config_name => { + let name = extract(&buf, pair.as_span()); + let location = ValueLocation { + path: shared_path.clone(), + content: buf.clone(), + location: unset_span.start()..unset_span.end(), + }; + return this.set_internal( + section.clone(), + name, + None, + location.into(), + opts, + ); + } + _ => (), } - _ => (), } - } - unreachable!(); - }; + unreachable!(); + }; let mut handle_directive = - |this: &mut ConfigSet, pair: Pair, section: &Bytes, errors: &mut Vec| { + |this: &mut ConfigSet, + pair: Pair, + section: &Bytes, + errors: &mut Vec| { let pairs = pair.into_inner(); for pair in pairs { match pair.as_rule() { @@ -381,22 +442,34 @@ let text = match str::from_utf8(&buf) { Ok(text) => text, - Err(error) => return errors.push(Error::Utf8(path.to_path_buf(), error)), + Err(error) => { + return errors.push(Error::Utf8(path.to_path_buf(), error)) + } }; let pairs = match ConfigParser::parse(Rule::file, &text) { Ok(pairs) => pairs, Err(error) => { - return errors.push(Error::Parse(path.to_path_buf(), format!("{}", error))); + return errors.push(Error::Parse( + path.to_path_buf(), + format!("{}", error), + )); } }; for pair in pairs { match pair.as_rule() { - Rule::config_item => handle_config_item(self, pair, section.clone()), + Rule::config_item => { + handle_config_item(self, pair, section.clone()) + } Rule::section => handle_section(pair, &mut section), - Rule::directive => handle_directive(self, pair, §ion, errors), - Rule::blank_line | Rule::comment_line | Rule::new_line | Rule::EOI => (), + Rule::directive => { + handle_directive(self, pair, §ion, errors) + } + Rule::blank_line + | Rule::comment_line + | Rule::new_line + | Rule::EOI => (), Rule::comment_start | Rule::compound @@ -422,8 +495,8 @@ &self.value } - /// Return the "source" information for the config value. It's usually who sets the config, - /// like "--config", "user_hgrc", "system_hgrc", etc. + /// Return the "source" information for the config value. It's usually who + /// sets the config, like "--config", "user_hgrc", "system_hgrc", etc. pub fn source(&self) -> &Bytes { &self.source } @@ -434,7 +507,9 @@ /// If the value is `None`, the byte range is for the "%unset" statement. pub fn location(&self) -> Option<(PathBuf, Range)> { match self.location { - Some(ref src) => Some((src.path.as_ref().to_path_buf(), src.location.clone())), + Some(ref src) => { + Some((src.path.as_ref().to_path_buf(), src.location.clone())) + } None => None, } } @@ -454,23 +529,31 @@ Self::default() } - /// Append a filter. A filter can decide to ignore a config item, or change its section, - /// config name, or even value. The filter function takes a tuple of `(section, name, value)` - /// and outputs `None` to prevent inserting that value, or `Some((section, name, value))` to + /// Append a filter. A filter can decide to ignore a config item, or change + /// its section, config name, or even value. The filter function takes + /// a tuple of `(section, name, value)` and outputs `None` to prevent + /// inserting that value, or `Some((section, name, value))` to /// insert it with optionally different name or values. /// /// Filters inserted first will be executed first. pub fn append_filter( mut self, - filter: Box) -> Option<(Bytes, Bytes, Option)>>, + filter: Box< + dyn Fn( + Bytes, + Bytes, + Option, + ) -> Option<(Bytes, Bytes, Option)>, + >, ) -> Self { self.filters.push(filter); self } - /// Set `source` information. It is about who initialized the config loading. For example, - /// "user_hgrc" indicates it is from the user config file, "--config" indicates it is from the - /// global "--config" command line flag, "env" indicates it is translated from an environment + /// Set `source` information. It is about who initialized the config + /// loading. For example, "user_hgrc" indicates it is from the user + /// config file, "--config" indicates it is from the global "--config" + /// command line flag, "env" indicates it is translated from an environment /// variable (ex. "PAGER"), etc. pub fn source>(mut self, source: B) -> Self { self.source = source.into(); @@ -485,8 +568,8 @@ } } -/// Remove space characters from both ends. Remove newline characters from the end. -/// `start` position is inclusive, `end` is exclusive. +/// Remove space characters from both ends. Remove newline characters from the +/// end. `start` position is inclusive, `end` is exclusive. /// Return the stripped `start` and `end` offsets. #[inline] fn strip_offsets(buf: &Bytes, start: usize, end: usize) -> (usize, usize) { @@ -899,7 +982,10 @@ ); let mut cfg = ConfigSet::new(); - let errors = cfg.load_path(dir.path().join("rootrc"), &"test_parse_include".into()); + let errors = cfg.load_path( + dir.path().join("rootrc"), + &"test_parse_include".into(), + ); assert!(errors.is_empty()); assert_eq!(cfg.sections(), vec![Bytes::from("x"), Bytes::from("y")]); @@ -935,7 +1021,8 @@ write_file(dir.path().join("f2/f/4.rc"), "[y]\nb=2\n"); let mut cfg = ConfigSet::new(); - let errors = cfg.load_path(dir.path().join("rootrc"), &"include_expand".into()); + let errors = + cfg.load_path(dir.path().join("rootrc"), &"include_expand".into()); assert!(errors.is_empty()); assert_eq!(cfg.get("x", "a"), Some(Bytes::from("1"))); diff --git a/rust/hg-core/src/configparser/hg.rs b/rust/hg-core/src/configparser/hg.rs --- a/rust/hg-core/src/configparser/hg.rs +++ b/rust/hg-core/src/configparser/hg.rs @@ -28,13 +28,17 @@ /// Drop configs according to `$HGPLAIN` and `$HGPLAINEXCEPT`. fn process_hgplain(self) -> Self; - /// Set read-only config items. `items` contains a list of tuple `(section, name)`. - /// Setting those items to new value will be ignored. - fn readonly_items, N: Into>(self, items: Vec<(S, N)>) -> Self; + /// Set read-only config items. `items` contains a list of tuple `(section, + /// name)`. Setting those items to new value will be ignored. + fn readonly_items, N: Into>( + self, + items: Vec<(S, N)>, + ) -> Self; - /// Set section remap. If a section name matches an entry key, it will be treated as if the - /// name is the entry value. The remap wouldn't happen recursively. For example, with a - /// `{"A": "B", "B": "C"}` map, section name "A" will be treated as "B", not "C". + /// Set section remap. If a section name matches an entry key, it will be + /// treated as if the name is the entry value. The remap wouldn't + /// happen recursively. For example, with a `{"A": "B", "B": "C"}` map, + /// section name "A" will be treated as "B", not "C". /// This is implemented via `append_filter`. fn remap_sections, V: Into>( self, @@ -43,7 +47,10 @@ /// Set section whitelist. Sections outside the whitelist won't be loaded. /// This is implemented via `append_filter`. - fn whitelist_sections>(self, sections: Vec) -> Self; + fn whitelist_sections>( + self, + sections: Vec, + ) -> Self; } pub trait ConfigSetHgExt { @@ -58,10 +65,18 @@ /// Load a specified config file. Respect HGPLAIN environment variables. /// Return errors parsing files. - fn load_hgrc(&mut self, path: impl AsRef, source: &'static str) -> Vec; + fn load_hgrc( + &mut self, + path: impl AsRef, + source: &'static str, + ) -> Vec; /// Get a config item. Convert to type `T`. - fn get_opt(&self, section: &str, name: &str) -> Result>; + fn get_opt( + &self, + section: &str, + name: &str, + ) -> Result>; /// Get a config item. Convert to type `T`. /// @@ -78,7 +93,11 @@ /// Get a config item. Convert to type `T`. /// /// If the config item is not set, return `T::default()`. - fn get_or_default(&self, section: &str, name: &str) -> Result { + fn get_or_default( + &self, + section: &str, + name: &str, + ) -> Result { self.get_or(section, name, Default::default) } } @@ -113,10 +132,13 @@ // [defaults] and [commands] are always blacklisted. let mut section_blacklist: HashSet = - ["defaults", "commands"].iter().map(|&s| s.into()).collect(); + ["defaults", "commands"] + .iter() + .map(|&s| s.into()) + .collect(); - // [alias], [revsetalias], [templatealias] are blacklisted if they are outside - // HGPLAINEXCEPT. + // [alias], [revsetalias], [templatealias] are blacklisted if + // they are outside HGPLAINEXCEPT. for &name in ["alias", "revsetalias", "templatealias"].iter() { if !plain_exceptions.contains(name) { section_blacklist.insert(Bytes::from(name)); @@ -138,7 +160,8 @@ .iter() .map(|&s| s.into()) .collect(); - // exitcodemask is blacklisted if exitcode is outside HGPLAINEXCEPT. + // exitcodemask is blacklisted if exitcode is outside + // HGPLAINEXCEPT. if !plain_exceptions.contains("exitcode") { ui_blacklist.insert("exitcodemask".into()); } @@ -146,15 +169,17 @@ (section_blacklist, ui_blacklist) }; - let filter = move |section: Bytes, name: Bytes, value: Option| { - if section_blacklist.contains(§ion) - || (section.as_ref() == b"ui" && ui_blacklist.contains(&name)) - { - None - } else { - Some((section, name, value)) - } - }; + let filter = + move |section: Bytes, name: Bytes, value: Option| { + if section_blacklist.contains(§ion) + || (section.as_ref() == b"ui" + && ui_blacklist.contains(&name)) + { + None + } else { + Some((section, name, value)) + } + }; self.append_filter(Box::new(filter)) } else { @@ -164,27 +189,32 @@ /// Set section whitelist. Sections outside the whitelist won't be loaded. /// This is implemented via `append_filter`. - fn whitelist_sections>(self, sections: Vec) -> Self { + fn whitelist_sections>( + self, + sections: Vec, + ) -> Self { let whitelist: HashSet = sections .iter() .cloned() .map(|section| section.into()) .collect(); - let filter = move |section: Bytes, name: Bytes, value: Option| { - if whitelist.contains(§ion) { - Some((section, name, value)) - } else { - None - } - }; + let filter = + move |section: Bytes, name: Bytes, value: Option| { + if whitelist.contains(§ion) { + Some((section, name, value)) + } else { + None + } + }; self.append_filter(Box::new(filter)) } - /// Set section remap. If a section name matches an entry key, it will be treated as if the - /// name is the entry value. The remap wouldn't happen recursively. For example, with a - /// `{"A": "B", "B": "C"}` map, section name "A" will be treated as "B", not "C". + /// Set section remap. If a section name matches an entry key, it will be + /// treated as if the name is the entry value. The remap wouldn't + /// happen recursively. For example, with a `{"A": "B", "B": "C"}` map, + /// section name "A" will be treated as "B", not "C". /// This is implemented via `append_filter`. fn remap_sections(self, remap: HashMap) -> Self where @@ -196,27 +226,32 @@ .map(|(k, v)| (k.into(), v.into())) .collect(); - let filter = move |section: Bytes, name: Bytes, value: Option| { - let section = remap.get(§ion).cloned().unwrap_or(section); - Some((section, name, value)) - }; + let filter = + move |section: Bytes, name: Bytes, value: Option| { + let section = remap.get(§ion).cloned().unwrap_or(section); + Some((section, name, value)) + }; self.append_filter(Box::new(filter)) } - fn readonly_items, N: Into>(self, items: Vec<(S, N)>) -> Self { + fn readonly_items, N: Into>( + self, + items: Vec<(S, N)>, + ) -> Self { let readonly_items: HashSet<(Bytes, Bytes)> = items .into_iter() .map(|(section, name)| (section.into(), name.into())) .collect(); - let filter = move |section: Bytes, name: Bytes, value: Option| { - if readonly_items.contains(&(section.clone(), name.clone())) { - None - } else { - Some((section, name, value)) - } - }; + let filter = + move |section: Bytes, name: Bytes, value: Option| { + if readonly_items.contains(&(section.clone(), name.clone())) { + None + } else { + Some((section, name, value)) + } + }; self.append_filter(Box::new(filter)) } @@ -230,23 +265,37 @@ if env::var(HGRCPATH).is_err() { #[cfg(unix)] { - errors.append(&mut self.load_path("/etc/mercurial/system.rc", &opts)); - // TODO(T40519286): Remove this after the tupperware overrides move out of hgrc.d errors.append( - &mut self.load_path("/etc/mercurial/hgrc.d/tupperware_overrides.rc", &opts), + &mut self.load_path("/etc/mercurial/system.rc", &opts), ); - // TODO(quark): Remove this after packages using system.rc are rolled out - errors.append(&mut self.load_path("/etc/mercurial/hgrc.d/include.rc", &opts)); + // TODO(T40519286): Remove this after the tupperware overrides + // move out of hgrc.d + errors.append(&mut self.load_path( + "/etc/mercurial/hgrc.d/tupperware_overrides.rc", + &opts, + )); + // TODO(quark): Remove this after packages using system.rc are + // rolled out + errors.append( + &mut self + .load_path("/etc/mercurial/hgrc.d/include.rc", &opts), + ); } #[cfg(windows)] { if let Ok(program_data_path) = env::var("PROGRAMDATA") { use std::path::Path; - let hgrc_dir = Path::new(&program_data_path).join("Facebook\\Mercurial"); - errors.append(&mut self.load_path(hgrc_dir.join("system.rc"), &opts)); - // TODO(quark): Remove this after packages using system.rc are rolled out - errors.append(&mut self.load_path(hgrc_dir.join("hgrc"), &opts)); + let hgrc_dir = Path::new(&program_data_path) + .join("Facebook\\Mercurial"); + errors.append( + &mut self.load_path(hgrc_dir.join("system.rc"), &opts), + ); + // TODO(quark): Remove this after packages using system.rc + // are rolled out + errors.append( + &mut self.load_path(hgrc_dir.join("hgrc"), &opts), + ); } } } @@ -302,27 +351,42 @@ } } else { if let Some(home_dir) = dirs::home_dir() { - errors.append(&mut self.load_path(home_dir.join(".hgrc"), &opts)); + errors.append( + &mut self.load_path(home_dir.join(".hgrc"), &opts), + ); #[cfg(windows)] { - errors.append(&mut self.load_path(home_dir.join("mercurial.ini"), &opts)); + errors.append( + &mut self + .load_path(home_dir.join("mercurial.ini"), &opts), + ); } } if let Some(config_dir) = dirs::config_dir() { - errors.append(&mut self.load_path(config_dir.join("hg/hgrc"), &opts)); + errors.append( + &mut self.load_path(config_dir.join("hg/hgrc"), &opts), + ); } } errors } - fn load_hgrc(&mut self, path: impl AsRef, source: &'static str) -> Vec { + fn load_hgrc( + &mut self, + path: impl AsRef, + source: &'static str, + ) -> Vec { let opts = Options::new().source(source).process_hgplain(); self.load_path(path, &opts) } - fn get_opt(&self, section: &str, name: &str) -> Result> { + fn get_opt( + &self, + section: &str, + name: &str, + ) -> Result> { ConfigSet::get(self, section, name) .map(|bytes| T::try_from_bytes(&bytes)) .transpose() @@ -335,7 +399,9 @@ match value.as_ref() { "1" | "yes" | "true" | "on" | "always" => Ok(true), "0" | "no" | "false" | "off" | "never" => Ok(false), - _ => Err(Error::Convert(format!("invalid bool: {}", value)).into()), + _ => { + Err(Error::Convert(format!("invalid bool: {}", value)).into()) + } } } } @@ -412,8 +478,9 @@ impl FromConfigValue for String { fn try_from_bytes(bytes: &[u8]) -> Result { - String::from_utf8(bytes.to_vec()) - .map_err(|_| Error::Convert(format!("{:?} is not utf8 encoded", bytes)).into()) + String::from_utf8(bytes.to_vec()).map_err(|_| { + Error::Convert(format!("{:?} is not utf8 encoded", bytes)).into() + }) } } @@ -453,7 +520,8 @@ let value = std::str::from_utf8(bytes)?.to_lowercase(); for (suffix, unit) in sizeunits.iter() { if value.ends_with(suffix) { - let number_str: &str = value[..value.len() - suffix.len()].trim(); + let number_str: &str = + value[..value.len() - suffix.len()].trim(); let number: f64 = number_str.parse()?; if number < 0.0 { return Err(Error::Convert(format!( @@ -467,7 +535,11 @@ } } - Err(Error::Convert(format!("'{:?}' cannot be parsed as a byte size", value)).into()) + Err(Error::Convert(format!( + "'{:?}' cannot be parsed as a byte size", + value + )) + .into()) } } @@ -580,7 +652,9 @@ // ``` State::Plain => { let mut whitespace = false; - while offset < value.len() && b" \n\r\t,".contains(&value[offset]) { + while offset < value.len() + && b" \n\r\t,".contains(&value[offset]) + { whitespace = true; offset += 1; } @@ -667,7 +741,9 @@ if offset < value.len() && value[offset] == b'"' { parts.push(Vec::new()); offset += 1; - while offset < value.len() && b" \n\r\t,".contains(&value[offset]) { + while offset < value.len() + && b" \n\r\t,".contains(&value[offset]) + { offset += 1; } state = State::Plain; @@ -686,10 +762,11 @@ offset += 1; } if offset >= value.len() { - let mut real_parts: Vec> = parse_list_internal(parts.last().unwrap()) - .iter() - .map(|b| b.to_vec()) - .collect(); + let mut real_parts: Vec> = + parse_list_internal(parts.last().unwrap()) + .iter() + .map(|b| b.to_vec()) + .collect(); if real_parts.is_empty() { parts.pop(); parts.push(vec![b'"']); @@ -928,7 +1005,10 @@ parse_list(b"Do\"Not\"Separate"), vec![b("Do\"Not\"Separate")] ); - assert_eq!(parse_list(b"\"Do\"Separate"), vec![b("Do"), b("Separate")]); + assert_eq!( + parse_list(b"\"Do\"Separate"), + vec![b("Do"), b("Separate")] + ); assert_eq!( parse_list(b"\"Do\\\"NotSeparate\""), vec![b("Do\"NotSeparate")] @@ -954,7 +1034,9 @@ vec![b("\""), b("just"), b("with"), b("starting"), b("quotation")] ); assert_eq!( - parse_list(&b"\"longer quotation\" with \"no ending quotation"[..]), + parse_list( + &b"\"longer quotation\" with \"no ending quotation"[..] + ), vec![ b("longer quotation"), b("with"), @@ -967,7 +1049,10 @@ parse_list(&b"this is \\\" \"not a quotation mark\""[..]), vec![b("this"), b("is"), b("\""), b("not a quotation mark")] ); - assert_eq!(parse_list(b"\n \n\nding\ndong"), vec![b("ding"), b("dong")]); + assert_eq!( + parse_list(b"\n \n\nding\ndong"), + vec![b("ding"), b("dong")] + ); // Other manually written cases assert_eq!(parse_list("a,b,,c"), vec![b("a"), b("b"), b("c")]); diff --git a/rust/hg-core/src/configparser/lib.rs b/rust/hg-core/src/configparser/lib.rs --- a/rust/hg-core/src/configparser/lib.rs +++ b/rust/hg-core/src/configparser/lib.rs @@ -12,8 +12,8 @@ //! ## Features //! //! - Parse valid hgrc-like config files efficiently. -//! - Track source locations of config values. Keep multiple locations of -//! a same config if it is overridden. +//! - Track source locations of config values. Keep multiple locations of a +//! same config if it is overridden. //! //! ## Config Format //!