use super::*;
impl<'a> InheritLinearized<'a> {
fn new(graph: &'a InheritGraph) -> Self {
Self { graph, maps: BTreeMap::new() }
}
fn insert(&mut self, name: &'a str, value: Vec<&'a str>) {
self.maps.insert(name, value.to_vec());
}
pub fn mro(&self, name: &str) -> Option<&[&'a str]> {
Some(self.maps.get(name)?.as_slice())
}
pub fn is_ancestor(&self, this: &str, ancestor: &str) -> Option<bool> {
Some(self.maps.get(this)?.contains(&ancestor))
}
}
impl InheritGraph {
pub fn linearize(&self) -> LinearizeResult<InheritLinearized> {
let mut results = InheritLinearized::new(self);
for (head, _) in &self.base {
c3_linearize(self, head, &mut results, &mut BTreeMap::new())?;
}
Ok(results)
}
fn get_base(&self, name: &str) -> Option<&[BaseClass]> {
Some(self.base.get(name)?.base.as_slice())
}
pub fn has_base(&self, class: &str) -> Option<bool> {
Some(!self.base.get(class)?.base.is_empty())
}
}
fn c3_linearize<'a>(
graph: &'a InheritGraph,
head: &'a str,
results: &mut InheritLinearized<'a>,
visiting: &mut BTreeMap<&'a str, bool>,
) -> LinearizeResult<Vec<&'a str>> {
if let Some(res) = results.mro(head) {
return Ok(res.to_vec());
}
check_circle(head, visiting)?;
visiting.insert(head, true);
let parents = graph.get_base(head).unwrap();
if parents.is_empty() {
let res = vec![head];
results.insert(head, res.clone());
return Ok(res);
}
let mut sequences = Vec::new();
for parent in parents {
let sequence = c3_linearize(graph, &parent.class, results, visiting)?;
sequences.push(sequence);
}
if let Some(false) = graph.has_base(head) {
sequences.push(vec![head]);
}
let res = vec![head].into_iter().chain(merge(sequences)?).collect::<Vec<_>>();
results.insert(head, res.clone());
visiting.remove(head);
Ok(res)
}
fn merge(sequences: Vec<Vec<&str>>) -> LinearizeResult<Vec<&str>> {
let mut result = Vec::new();
let mut sequences = sequences.into_iter().map(|s| s.into_iter().collect::<Vec<&str>>()).collect::<Vec<_>>();
while !sequences.is_empty() {
let mut found = false;
let mut head = None;
for seq in &sequences {
head = seq.first().cloned();
fn is_bad_head(seq: &&Vec<&str>, s: &&Vec<&str>) -> bool {
s != seq && s[1..].contains(&seq[0])
}
if !sequences.iter().any(|s| is_bad_head(&seq, &s)) {
found = true;
if let Some(head) = head {
result.push(head);
for seq in &mut sequences {
if let Some(index) = seq.iter().position(|&x| x == head) {
seq.remove(index);
}
}
break;
}
}
}
sequences = sequences.into_iter().filter(|s| !s.is_empty()).collect::<Vec<_>>();
if !found {
return Err(LinearizeError::NotFound { base: head.unwrap().to_string() });
}
}
Ok(result)
}
fn check_circle(head: &str, visiting: &BTreeMap<&str, bool>) -> LinearizeResult<()> {
for item in visiting.keys() {
if head.eq(*item) {
return Err(LinearizeError::Circular { class: head.to_string() });
}
}
Ok(())
}