use super::parser::{parse_lino, LinoNode};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct OverrideFact {
pub section: String,
pub key: String,
pub value: String,
}
#[must_use]
pub fn parse_record(text: &str) -> Option<LinoNode> {
parse_lino(text).children.into_iter().next()
}
#[must_use]
pub fn override_reason(record: &LinoNode) -> Option<String> {
record
.children
.iter()
.find(|child| child.name == "reason")
.map(|child| child.id.clone())
.filter(|reason| !reason.trim().is_empty())
}
#[must_use]
pub fn override_facts(record: &LinoNode) -> Vec<OverrideFact> {
let mut facts = Vec::new();
for section in &record.children {
if section.name == "reason" {
continue;
}
for entry in §ion.children {
facts.push(OverrideFact {
section: section.name.clone(),
key: entry.name.clone(),
value: entry.id.clone(),
});
}
}
facts
}
#[must_use]
pub fn cache_contains(cache: &LinoNode, fact: &OverrideFact) -> bool {
cache.children.iter().any(|section| {
section.name == fact.section
&& section
.children
.iter()
.any(|entry| entry.name == fact.key && entry.id == fact.value)
})
}
#[must_use]
pub fn resolve(cache: &LinoNode, over: &LinoNode) -> LinoNode {
let mut merged = cache.clone();
for fact in override_facts(over) {
let section_index = merged
.children
.iter()
.position(|section| section.name == fact.section)
.unwrap_or_else(|| {
merged.children.push(LinoNode {
name: fact.section.clone(),
id: String::new(),
children: Vec::new(),
});
merged.children.len() - 1
});
let section = &mut merged.children[section_index];
if let Some(entry) = section
.children
.iter_mut()
.find(|entry| entry.name == fact.key)
{
entry.id = fact.value;
} else {
section.children.push(LinoNode {
name: fact.key,
id: fact.value,
children: Vec::new(),
});
}
}
merged
}