use std::collections::{HashMap, HashSet};
pub struct CallGraph(pub HashMap<u32, HashSet<u32>>);
impl CallGraph {
pub fn new() -> CallGraph {
CallGraph(HashMap::new())
}
pub fn iter<'a>(&'a self) -> std::collections::hash_map::Iter<'a, u32, HashSet<u32>> {
self.0.iter()
}
pub fn get<'a>(&'a self, id: u32) -> Option<&'a HashSet<u32>> {
self.0.get(&id)
}
pub fn all_funcs(&self) -> HashSet<u32> {
self.iter()
.fold(HashSet::new(), |mut all_funcs, (_id, deps)| {
deps.iter().for_each(|&v| {
all_funcs.insert(v);
});
all_funcs
})
}
fn flatten_for_id(&self, id: u32, flat: &mut CallGraph) -> Option<()> {
if flat.0.contains_key(&id) {
return None;
}
let direct_funcs = self.0.get(&id)?;
flat.0.insert(id, HashSet::new());
let mut result = HashSet::new();
direct_funcs.iter().for_each(|&direct_func| {
result.insert(direct_func);
});
direct_funcs
.iter()
.filter(|&direct_func| direct_func != &id)
.map(|&direct_func| -> Option<()> {
self.flatten_for_id(direct_func, flat);
flat.0.get(&direct_func)?.iter().for_each(|&indirect_func| {
result.insert(indirect_func);
});
Some(())
})
.for_each(drop);
flat.0.insert(id, result);
Some(())
}
pub fn flatten(&self) -> CallGraph {
let mut flat = CallGraph::new();
self.0.keys().for_each(|&id| {
self.flatten_for_id(id, &mut flat);
});
flat
}
}
impl std::fmt::Display for CallGraph {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
self.0
.iter()
.map(|(id, calls)| write!(f, "{} => {:?}\n", id, calls))
.fold(Ok(()), |result, v| result.and(v))
}
}