use std::collections::{BTreeMap, BTreeSet};
use std::convert::Infallible;
use crate::build::Type;
use crate::error::Error;
fn walk_type_graph<Id, E, F>(roots: Vec<Id>, mut visit: F) -> Result<(), E>
where
Id: Clone + Ord,
F: FnMut(&Id, &BTreeSet<Id>) -> Result<Vec<Id>, E>,
{
enum Node<Id> {
Start { type_id: Id },
Processing { type_id: Id, children_ids: Vec<Id> },
}
let mut visited = BTreeSet::<Id>::new();
for type_id in roots {
if visited.contains(&type_id) {
continue;
}
let mut active = BTreeSet::<Id>::new();
let mut stack = Vec::<Node<Id>>::new();
active.insert(type_id.clone());
stack.push(Node::Start { type_id });
while let Some(top) = stack.last_mut() {
match top {
Node::Start { type_id } if visited.contains(type_id) => {
assert!(active.contains(type_id));
let type_id = type_id.clone();
*top = Node::Processing {
type_id,
children_ids: Vec::new(),
};
}
Node::Start { type_id } => {
assert!(active.contains(type_id));
let type_id = type_id.clone();
visited.insert(type_id.clone());
let children_ids = visit(&type_id, &active)?;
*top = Node::Processing {
type_id,
children_ids,
};
}
Node::Processing {
type_id,
children_ids: children,
} => {
if let Some(child) = children.pop() {
active.insert(child.clone());
stack.push(Node::Start { type_id: child });
} else {
let type_id = type_id.clone();
active.remove(&type_id);
stack.pop();
}
}
}
}
}
Ok(())
}
pub(crate) fn break_cycles<Id, F>(types: &mut BTreeMap<Id, Type<Id>>, mut make_box_id: F)
where
Id: Clone + Ord + std::fmt::Debug + std::fmt::Display,
F: FnMut(&Id) -> Id,
{
let roots = types.keys().cloned().collect();
walk_type_graph(roots, |type_id, active| -> Result<Vec<Id>, Infallible> {
let (snip, descend) = {
let typ = types.get_mut(type_id).unwrap();
let child_ids = typ
.contained_children_mut()
.into_iter()
.map(|child_id| child_id.clone());
child_ids.partition::<Vec<_>, _>(|child_id| active.contains(child_id))
};
let replace = snip
.into_iter()
.map(|type_id| {
let box_id = make_box_id(&type_id);
let box_typ = Type::Box(type_id.clone());
types.insert(box_id.clone(), box_typ);
(type_id, box_id)
})
.collect::<BTreeMap<Id, Id>>();
let typ = types.get_mut(type_id).unwrap();
let child_ids = typ.contained_children_mut();
for child_id in child_ids {
if let Some(replace_id) = replace.get(child_id) {
*child_id = replace_id.clone();
}
}
Ok(descend)
})
.unwrap()
}
pub(crate) fn check_anonymous_cycles<Id>(types: &BTreeMap<Id, Type<Id>>) -> Result<(), Error<Id>>
where
Id: Clone + Ord + std::fmt::Debug + std::fmt::Display,
{
let roots = types
.iter()
.filter(|(_, typ)| !typ.is_named())
.map(|(type_id, _)| type_id.clone())
.collect();
walk_type_graph(roots, |type_id, active| {
let children_ids = types[type_id]
.children()
.into_iter()
.filter(|child_id| !types[child_id].is_named())
.collect::<Vec<_>>();
match children_ids
.iter()
.find(|child_id| active.contains(*child_id))
{
Some(child_id) => Err(Error::AnonymousCycle {
type_id: type_id.clone(),
child_id: child_id.clone(),
}),
None => Ok(children_ids),
}
})
}