blob: 7cfd7aac495b516d37db2424fc73c3027b75f37f [file] [log] [blame]
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)
}
}