1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 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)
}
}