diff options
Diffstat (limited to 'vendor/gix/src/config/tree/traits.rs')
-rw-r--r-- | vendor/gix/src/config/tree/traits.rs | 199 |
1 files changed, 199 insertions, 0 deletions
diff --git a/vendor/gix/src/config/tree/traits.rs b/vendor/gix/src/config/tree/traits.rs new file mode 100644 index 000000000..7cfd7aac4 --- /dev/null +++ b/vendor/gix/src/config/tree/traits.rs @@ -0,0 +1,199 @@ +use crate::{ + bstr::{BStr, BString, ByteVec}, + config::tree::key::validate_assignment, +}; + +/// Provide information about a configuration section. +pub trait Section { + /// The section name, like `remote` in `remote.origin.url`. + fn name(&self) -> &str; + /// The keys directly underneath it for carrying configuration values. + fn keys(&self) -> &[&dyn Key]; + /// The list of sub-section names, which may be empty if there are no statically known sub-sections. + fn sub_sections(&self) -> &[&dyn Section] { + &[] + } + /// The parent section if this is a statically known sub-section. + fn parent(&self) -> Option<&dyn Section> { + None + } +} + +/// Determine how subsections may be used with a given key, suitable for obtaining the full name for use in assignments. +#[derive(Debug, Copy, Clone)] +pub enum SubSectionRequirement { + /// Subsections must not be used, this key can only be below a section. + Never, + /// The sub-section is used as parameter with the given name. + Parameter(&'static str), +} + +/// A way to link a key with other resources. +#[derive(Debug, Copy, Clone)] +pub enum Link { + /// The environment variable of the given name will override the value of this key. + EnvironmentOverride(&'static str), + /// This config key is used as fallback if this key isn't set. + FallbackKey(&'static dyn Key), +} + +/// A note attached to a key. +#[derive(Debug, Copy, Clone)] +pub enum Note { + /// A piece of information related to a key to help the user. + Informative(&'static str), + /// This key works differently than is described by git, explaining the deviation further. + Deviation(&'static str), +} + +/// A leaf-level entry in the git configuration, like `url` in `remote.origin.url`. +pub trait Key: std::fmt::Debug { + /// The key's name, like `url` in `remote.origin.url`. + fn name(&self) -> &str; + /// See if `value` is allowed as value of this key, or return a descriptive error if it is not. + fn validate(&self, value: &BStr) -> Result<(), crate::config::tree::key::validate::Error>; + /// The section containing this key. Git configuration has no free-standing keys, they are always underneath a section. + fn section(&self) -> &dyn Section; + /// The return value encodes three possible states to indicate subsection requirements + /// * `None` = subsections may or may not be used, the most flexible setting. + /// * `Some([Requirement][SubSectionRequirement])` = subsections must or must not be used, depending on the value + fn subsection_requirement(&self) -> Option<&SubSectionRequirement> { + Some(&SubSectionRequirement::Never) + } + /// Return the link to other resources, if available. + fn link(&self) -> Option<&Link> { + None + } + /// Return a note about this key, if available. + fn note(&self) -> Option<&Note> { + None + } + + /// Return the name of an environment variable that would override this value (after following links until one is found). + fn environment_override(&self) -> Option<&str> { + let mut cursor = self.link()?; + loop { + match cursor { + Link::EnvironmentOverride(name) => return Some(name), + Link::FallbackKey(next) => { + cursor = next.link()?; + } + } + } + } + + /// Return the environment override that must be set on this key. + /// # Panics + /// If no environment variable is set + fn the_environment_override(&self) -> &str { + self.environment_override() + .expect("BUG: environment override must be set") + } + /// Produce a name that describes how the name is composed. This is `core.bare` for statically known keys, or `branch.<name>.key` + /// for complex ones. + fn logical_name(&self) -> String { + let section = self.section(); + let mut buf = String::new(); + let parameter = if let Some(parent) = section.parent() { + buf.push_str(parent.name()); + buf.push('.'); + None + } else { + self.subsection_requirement().and_then(|requirement| match requirement { + SubSectionRequirement::Parameter(name) => Some(name), + SubSectionRequirement::Never => None, + }) + }; + buf.push_str(section.name()); + buf.push('.'); + if let Some(parameter) = parameter { + buf.push('<'); + buf.push_str(parameter); + buf.push('>'); + buf.push('.'); + } + buf.push_str(self.name()); + buf + } + + /// The full name of the key for use in configuration overrides, like `core.bare`, or `remote.<subsection>.url` if `subsection` is + /// not `None`. + /// May fail if this key needs a subsection, or may not have a subsection. + fn full_name(&self, subsection: Option<&BStr>) -> Result<BString, String> { + let section = self.section(); + let mut buf = BString::default(); + let subsection = match self.subsection_requirement() { + None => subsection, + Some(requirement) => match (requirement, subsection) { + (SubSectionRequirement::Never, Some(_)) => { + return Err(format!( + "The key named '{}' cannot be used with non-static subsections.", + self.logical_name() + )); + } + (SubSectionRequirement::Parameter(_), None) => { + return Err(format!( + "The key named '{}' cannot be used without subsections.", + self.logical_name() + )) + } + _ => subsection, + }, + }; + + if let Some(parent) = section.parent() { + buf.push_str(parent.name()); + buf.push(b'.'); + } + buf.push_str(section.name()); + buf.push(b'.'); + if let Some(subsection) = subsection { + debug_assert!( + section.parent().is_none(), + "BUG: sections with parameterized sub-sections must be top-level sections" + ); + buf.push_str(subsection); + buf.push(b'.'); + } + buf.push_str(self.name()); + Ok(buf) + } + + /// Return an assignment with the keys full name to `value`, suitable for [configuration overrides][crate::open::Options::config_overrides()]. + /// Note that this will fail if the key requires a subsection name. + fn validated_assignment(&self, value: &BStr) -> Result<BString, validate_assignment::Error> { + self.validate(value)?; + let mut key = self + .full_name(None) + .map_err(|message| validate_assignment::Error::Name { message })?; + key.push(b'='); + key.push_str(value); + Ok(key) + } + + /// Return an assignment with the keys full name to `value`, suitable for [configuration overrides][crate::open::Options::config_overrides()]. + /// Note that this will fail if the key requires a subsection name. + fn validated_assignment_fmt( + &self, + value: &dyn std::fmt::Display, + ) -> Result<BString, crate::config::tree::key::validate_assignment::Error> { + let value = value.to_string(); + self.validated_assignment(value.as_str().into()) + } + + /// Return an assignment to `value` with the keys full name within `subsection`, suitable for [configuration overrides][crate::open::Options::config_overrides()]. + /// Note that this is only valid if this key supports parameterized sub-sections, or else an error is returned. + fn validated_assignment_with_subsection( + &self, + value: &BStr, + subsection: &BStr, + ) -> Result<BString, crate::config::tree::key::validate_assignment::Error> { + self.validate(value)?; + let mut key = self + .full_name(Some(subsection)) + .map_err(|message| validate_assignment::Error::Name { message })?; + key.push(b'='); + key.push_str(value); + Ok(key) + } +} |