use crate::bstr::{BStr, BString, ByteVec};
use crate::config::tree::key::validate_assignment;
pub trait Section {
fn name(&self) -> &str;
fn keys(&self) -> &[&dyn Key];
fn sub_sections(&self) -> &[&dyn Section] {
&[]
}
fn parent(&self) -> Option<&dyn Section> {
None
}
}
#[derive(Debug, Copy, Clone)]
pub enum SubSectionRequirement {
Never,
Parameter(&'static str),
}
#[derive(Debug, Copy, Clone)]
pub enum Link {
EnvironmentOverride(&'static str),
FallbackKey(&'static dyn Key),
}
#[derive(Debug, Copy, Clone)]
pub enum Note {
Informative(&'static str),
Deviation(&'static str),
}
pub trait Key: std::fmt::Debug {
fn name(&self) -> &str;
fn validate(&self, value: &BStr) -> Result<(), crate::config::tree::key::validate::Error>;
fn section(&self) -> &dyn Section;
fn subsection_requirement(&self) -> Option<&SubSectionRequirement> {
Some(&SubSectionRequirement::Never)
}
fn link(&self) -> Option<&Link> {
None
}
fn note(&self) -> Option<&Note> {
None
}
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()?;
}
}
}
}
fn the_environment_override(&self) -> &str {
self.environment_override()
.expect("BUG: environment override must be set")
}
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
}
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)
}
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)
}
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())
}
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)
}
}