use crate::wiki::model::{LineageEntry, PageLink};
use provenance_core::{Requirement, Resolution, Rule, StableId};
use std::collections::{BTreeMap, BTreeSet};
use super::context::Assembler;
use super::page_links::requirement_link;
impl<'a> Assembler<'a> {
pub(super) fn parent_id_of(&self, requirement_id: &StableId) -> Option<&'a StableId> {
self.find_requirement(requirement_id)
.and_then(|requirement| requirement.refines.as_ref())
}
pub(super) fn parent_of(&self, requirement_id: &StableId) -> Option<&'a Requirement> {
self.parent_id_of(requirement_id)
.and_then(|id| self.find_requirement(id))
}
pub(super) fn children_of(&self, requirement_id: &StableId) -> Vec<&'a Requirement> {
self.state
.requirements
.iter()
.filter(|child| child.refines.as_ref() == Some(requirement_id))
.collect()
}
pub(super) fn resolving_resolutions(&self, requirement_id: &StableId) -> Vec<&'a Resolution> {
self.query.resolving_resolutions(requirement_id)
}
pub(super) fn produced_rules_for_requirement(
&self,
requirement_id: &StableId,
) -> Vec<&'a Rule> {
self.query.produced_rules_for_requirement(requirement_id)
}
pub(super) fn produced_rules_for_resolution(&self, resolution_id: &StableId) -> Vec<&'a Rule> {
self.query.produced_rules_for_resolution(resolution_id)
}
pub(super) fn requirements_behind_rule(&self, rule_id: &StableId) -> &[&'a Requirement] {
self.rule_requirements
.get_or_init(|| {
let mut attribution: BTreeMap<&'a str, Vec<&'a Requirement>> = BTreeMap::new();
for requirement in &self.state.requirements {
for rule in self.produced_rules_for_requirement(&requirement.id) {
let attributed = attribution.entry(rule.id.as_str()).or_default();
if attributed
.last()
.is_none_or(|last| last.id != requirement.id)
{
attributed.push(requirement);
}
}
}
attribution
})
.get(rule_id.as_str())
.map_or(&[], Vec::as_slice)
}
pub(super) fn sibling_requirements(&self, requirement_id: &StableId) -> Vec<PageLink> {
let Some(parent_id) = self.parent_id_of(requirement_id) else {
return Vec::new();
};
self.children_of(parent_id)
.into_iter()
.filter(|candidate| candidate.id != *requirement_id)
.map(requirement_link)
.collect()
}
pub(super) fn lineage(&self, requirement: &'a Requirement) -> Vec<LineageEntry> {
let mut chain = vec![requirement];
let mut visited: BTreeSet<&str> = BTreeSet::from([requirement.id.as_str()]);
let mut current = requirement;
while let Some(parent) = self.parent_of(¤t.id) {
if !visited.insert(parent.id.as_str()) {
break;
}
chain.push(parent);
current = parent;
}
chain.reverse();
let last = chain.len() - 1;
chain
.into_iter()
.enumerate()
.map(|(index, entry)| LineageEntry {
link: requirement_link(entry),
is_current: index == last,
})
.collect()
}
}