use core::ops::Index;
use std::collections::HashMap;
use std::collections::HashSet;
use petgraph::graph::NodeIndex;
use petgraph::prelude::DiGraph;
use crate::types::cooked::Definition;
use alloc::collections::VecDeque;
use alloc::vec;
use petgraph::Direction;
#[derive(Debug, Default)]
pub struct DependencyGraph<'a> {
graph: DiGraph<&'a str, ()>,
ids: HashMap<&'a str, NodeIndex>,
}
impl<'a> DependencyGraph<'a> {
pub fn construct_for(definitions: &'a HashSet<Definition>, roots: &[&'a str]) -> Self {
let mut graph = Self::default();
let mut node_queue = VecDeque::from(roots.to_vec());
let mut edge_queue = vec![];
while let Some(id) = node_queue.pop_front() {
if graph.ids.contains_key(id) {
continue;
}
let from = graph.lookup_node(id);
for dependency in definitions
.get(id)
.unwrap_or_else(|| panic!("unknown definition for {id:?}"))
.dependencies()
{
node_queue.push_back(dependency);
edge_queue.push((from, dependency));
}
}
for (from, to_id) in edge_queue {
let to = graph.ids.get(to_id).unwrap_or_else(|| {
todo!("handle component {to_id}: {:?}", &definitions.get(to_id))
});
graph.graph.add_edge(from, *to, ());
}
graph
}
}
impl<'a> DependencyGraph<'a> {
pub fn bottom_up(&self) -> impl Iterator<Item = &'a str> + '_ {
self.iter().rev()
}
pub fn roots(&self) -> impl Iterator<Item = &'a str> + '_ {
self.graph
.externals(Direction::Incoming)
.map(|index| self[index])
}
pub fn top_down(&self) -> impl Iterator<Item = &'a str> + '_ {
self.iter()
}
}
impl<'a> DependencyGraph<'a> {
fn iter(&self) -> impl DoubleEndedIterator<Item = &'a str> + '_ {
let order = petgraph::algo::toposort(&self.graph, None).unwrap();
order.into_iter().map(move |index| self[index])
}
}
impl<'a> DependencyGraph<'a> {
fn lookup_node(&mut self, id: &'a str) -> NodeIndex {
*self
.ids
.entry(id)
.or_insert_with(|| self.graph.add_node(id))
}
}
impl<'a> Index<NodeIndex> for DependencyGraph<'a> {
type Output = &'a str;
fn index(&self, index: NodeIndex) -> &Self::Output {
&self.graph[index]
}
}