use {
crate::{
document::{Document, MergeTypeMismatch, Section, SectionKey, option::ConfigOption},
syntax::Directive,
},
alloc::{string::String, vec::Vec},
core::mem::take,
derive_more::{Display, Error, From},
hashbrown::{DefaultHashBuilder, hash_map::EntryRef},
};
pub struct Importer<'doc> {
doc: &'doc mut Document,
current_section: Option<SectionKey>,
}
impl<'doc> Importer<'doc> {
pub(super) const fn new(doc: &'doc mut Document) -> Self {
Self {
doc,
current_section: None,
}
}
pub fn import<'d>(&mut self, directive: Directive<'d>) -> Result<(), ImportError>
where
'doc: 'd,
{
match directive {
Directive::Package(_) => (),
Directive::Section { type_, name } => {
let key = self
.doc
.merge_or_insert(&type_, name.as_deref(), Section::default())?;
self.current_section = Some(key);
}
Directive::Option { key, value } => {
if let Some(value) = value {
let value = value.into_owned();
self.option_entry(&key)?.insert(ConfigOption::Scalar(value));
}
}
Directive::List { key, value } => {
let entry = self
.option_entry(&key)?
.or_insert(ConfigOption::List(Vec::new()));
let value = value.into_owned();
match entry {
ConfigOption::List(list) => list.push(value),
ConfigOption::Scalar(current) => {
let current = take(current);
*entry = ConfigOption::List(alloc::vec![current, value]);
}
}
}
}
Ok(())
}
fn option_entry<'key>(
&mut self,
option_key: &'key str,
) -> Result<EntryRef<'_, 'key, String, str, ConfigOption, DefaultHashBuilder>, ImportError> {
let section_key = self.current_section.ok_or(ImportError::OutsideSection)?;
let section = self
.doc
.get_mut(§ion_key)
.expect("section has to exist as we just used its key");
Ok(section.options.entry_ref(option_key))
}
}
#[derive(Clone, Copy, Display, Debug, PartialEq, Eq, Error, From)]
#[non_exhaustive]
pub enum ImportError {
#[display("found `option` or `list` directive outside section")]
OutsideSection,
MergeTypeMismatch(MergeTypeMismatch),
}