| 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) |
| } |
| } |