diff --git a/rust/hg-core/src/config.rs b/rust/hg-core/src/config.rs --- a/rust/hg-core/src/config.rs +++ b/rust/hg-core/src/config.rs @@ -11,6 +11,8 @@ mod config; mod layer; +mod plain_info; mod values; -pub use config::{Config, ConfigSource, ConfigValueParseError, PlainInfo}; +pub use config::{Config, ConfigSource, ConfigValueParseError}; pub use layer::{ConfigError, ConfigOrigin, ConfigParseError}; +pub use plain_info::PlainInfo; diff --git a/rust/hg-core/src/config/config.rs b/rust/hg-core/src/config/config.rs --- a/rust/hg-core/src/config/config.rs +++ b/rust/hg-core/src/config/config.rs @@ -12,6 +12,7 @@ use super::values; use crate::config::layer::{ ConfigError, ConfigLayer, ConfigOrigin, ConfigValue, }; +use crate::config::plain_info::PlainInfo; use crate::utils::files::get_bytes_from_os_str; use format_bytes::{write_bytes, DisplayBytes}; use std::collections::HashSet; @@ -22,14 +23,6 @@ use std::str; use crate::errors::{HgResultExt, IoResultExt}; -#[derive(Clone)] -pub struct PlainInfo { - pub plain: bool, - pub plainalias: bool, - pub plainrevsetalias: bool, - pub plaintemplatealias: bool, -} - /// Holds the config values for the current repository /// TODO update this docstring once we support more sources #[derive(Clone)] @@ -92,21 +85,21 @@ impl fmt::Display for ConfigValueParseEr } } +/// Returns true if the config item is disabled by PLAIN or PLAINEXCEPT fn should_ignore(plain: &PlainInfo, section: &[u8], item: &[u8]) -> bool { // duplication with [_applyconfig] in [ui.py], - if !plain.plain { + if !plain.is_plain() { return false; } if section == b"alias" { - return plain.plainalias; + return plain.plainalias(); } if section == b"revsetalias" { - return plain.plainrevsetalias; + return plain.plainrevsetalias(); } if section == b"templatealias" { - return plain.plaintemplatealias; + return plain.plaintemplatealias(); } - if section == b"ui" { let to_delete: &[&[u8]] = &[ b"debug", @@ -127,16 +120,6 @@ fn should_ignore(plain: &PlainInfo, sect return sections_to_delete.contains(§ion); } -impl PlainInfo { - pub fn empty() -> Self { - Self { - plain: false, - plainalias: false, - plainrevsetalias: false, - plaintemplatealias: false, - } - } -} impl Config { /// The configuration to use when printing configuration-loading errors pub fn empty() -> Self { @@ -478,6 +461,8 @@ impl Config { section: &[u8], item: &[u8], ) -> Option<(&ConfigLayer, &ConfigValue)> { + // Filter out the config items that are hidden by [PLAIN]. + // This differs from python hg where we delete them from the config. if should_ignore(&self.plain, §ion, &item) { return None; } diff --git a/rust/hg-core/src/config/plain_info.rs b/rust/hg-core/src/config/plain_info.rs new file mode 100644 --- /dev/null +++ b/rust/hg-core/src/config/plain_info.rs @@ -0,0 +1,79 @@ +use crate::utils::files::get_bytes_from_os_string; +use std::env; + +/// Keeps information on whether plain mode is active. +/// +/// Plain mode means that all configuration variables which affect +/// the behavior and output of Mercurial should be +/// ignored. Additionally, the output should be stable, +/// reproducible and suitable for use in scripts or applications. +/// +/// The only way to trigger plain mode is by setting either the +/// `HGPLAIN' or `HGPLAINEXCEPT' environment variables. +/// +/// The return value can either be +/// - False if HGPLAIN is not set, or feature is in HGPLAINEXCEPT +/// - False if feature is disabled by default and not included in HGPLAIN +/// - True otherwise +#[derive(Clone)] +pub struct PlainInfo { + is_plain: bool, + except: Vec>, +} + +impl PlainInfo { + fn plain_except(except: Vec>) -> Self { + PlainInfo { + is_plain: true, + except, + } + } + + pub fn empty() -> PlainInfo { + PlainInfo { + is_plain: false, + except: vec![], + } + } + + pub fn from_env() -> PlainInfo { + if let Some(except) = env::var_os("HGPLAINEXCEPT") { + PlainInfo::plain_except( + get_bytes_from_os_string(except) + .split(|&byte| byte == b',') + .map(|x| x.to_vec()) + .collect(), + ) + } else { + PlainInfo { + is_plain: env::var_os("HGPLAIN").is_some(), + except: vec![], + } + } + } + + pub fn is_feature_plain(&self, feature: &str) -> bool { + return self.is_plain + && !self + .except + .iter() + .any(|exception| exception.as_slice() == feature.as_bytes()); + } + + pub fn is_plain(&self) -> bool { + self.is_plain + } + + pub fn plainalias(&self) -> bool { + self.is_feature_plain("alias") + } + pub fn plainrevsetalias(&self) -> bool { + self.is_feature_plain("revsetalias") + } + pub fn plaintemplatealias(&self) -> bool { + self.is_feature_plain("templatealias") + } + pub fn plaintweakdefaults(&self) -> bool { + self.is_feature_plain("tweakdefaults") + } +} diff --git a/rust/rhg/src/main.rs b/rust/rhg/src/main.rs --- a/rust/rhg/src/main.rs +++ b/rust/rhg/src/main.rs @@ -329,14 +329,7 @@ fn rhg_main(argv: Vec) -> ! { }; let mut config_cow = Cow::Borrowed(config); - if ui::plain(None) { - config_cow.to_mut().apply_plain(PlainInfo { - plain: true, - plainalias: ui::plain(Some("alias")), - plainrevsetalias: ui::plain(Some("revsetalias")), - plaintemplatealias: ui::plain(Some("templatealias")), - }) - }; + config_cow.to_mut().apply_plain(PlainInfo::from_env()); let config = config_cow.as_ref(); let ui = Ui::new(&config).unwrap_or_else(|error| { diff --git a/rust/rhg/src/ui.rs b/rust/rhg/src/ui.rs --- a/rust/rhg/src/ui.rs +++ b/rust/rhg/src/ui.rs @@ -3,10 +3,9 @@ use crate::color::Effect; use format_bytes::format_bytes; use format_bytes::write_bytes; use hg::config::Config; +use hg::config::PlainInfo; use hg::errors::HgError; -use hg::utils::files::get_bytes_from_os_string; use std::borrow::Cow; -use std::env; use std::io; use std::io::{ErrorKind, Write}; @@ -129,29 +128,13 @@ impl Ui { } } -/// Return whether plain mode is active. -/// -/// Plain mode means that all configuration variables which affect -/// the behavior and output of Mercurial should be -/// ignored. Additionally, the output should be stable, -/// reproducible and suitable for use in scripts or applications. -/// -/// The only way to trigger plain mode is by setting either the -/// `HGPLAIN' or `HGPLAINEXCEPT' environment variables. -/// -/// The return value can either be -/// - False if HGPLAIN is not set, or feature is in HGPLAINEXCEPT -/// - False if feature is disabled by default and not included in HGPLAIN -/// - True otherwise +// TODO: pass the PlainInfo to call sites directly and +// delete this function pub fn plain(opt_feature: Option<&str>) -> bool { - if let Some(except) = env::var_os("HGPLAINEXCEPT") { - opt_feature.map_or(true, |feature| { - get_bytes_from_os_string(except) - .split(|&byte| byte == b',') - .all(|exception| exception != feature.as_bytes()) - }) - } else { - env::var_os("HGPLAIN").is_some() + let plain_info = PlainInfo::from_env(); + match opt_feature { + None => plain_info.is_plain(), + Some(feature) => plain_info.is_feature_plain(feature), } }