use std::{ collections::{HashMap, HashSet}, ops::{Deref, DerefMut}, path::PathBuf, }; use aiken_lang::ast::{ModuleKind, TypedModule, UntypedModule}; use petgraph::{algo, graph::NodeIndex, Direction, Graph}; use crate::error::Error; #[derive(Debug)] pub struct ParsedModule { pub path: PathBuf, pub name: String, pub code: String, pub kind: ModuleKind, pub package: String, pub ast: UntypedModule, // extra: ModuleExtra, } impl ParsedModule { pub fn deps_for_graph(&self) -> (String, Vec) { let name = self.name.clone(); let deps: Vec<_> = self .ast .dependencies() .into_iter() .map(|(dep, _span)| dep) .collect(); (name, deps) } } pub struct ParsedModules(HashMap); impl ParsedModules { pub fn sequence(&self) -> Result, Error> { let inputs = self .0 .values() .map(|m| m.deps_for_graph()) .collect::)>>(); let capacity = inputs.len(); let mut graph = Graph::<(), ()>::with_capacity(capacity, capacity * 5); // TODO: maybe use a bimap? let mut indices = HashMap::with_capacity(capacity); let mut values = HashMap::with_capacity(capacity); for (value, _) in &inputs { let index = graph.add_node(()); indices.insert(value.clone(), index); values.insert(index, value.clone()); } for (value, deps) in inputs { if let Some(from_index) = indices.get(&value) { let deps = deps.into_iter().filter_map(|dep| indices.get(&dep)); for to_index in deps { graph.add_edge(*from_index, *to_index, ()); } } } match algo::toposort(&graph, None) { Ok(sequence) => { let sequence = sequence .iter() .filter_map(|i| values.remove(i)) .rev() .collect(); Ok(sequence) } Err(cycle) => { let origin = cycle.node_id(); let mut path = vec![]; find_cycle(origin, origin, &graph, &mut path, &mut HashSet::new()); let modules = path .iter() .filter_map(|index| values.remove(index)) .collect(); Err(Error::ImportCycle { modules }) } } } } impl From> for ParsedModules { fn from(parsed_modules: HashMap) -> Self { ParsedModules(parsed_modules) } } impl From for HashMap { fn from(parsed_modules: ParsedModules) -> Self { parsed_modules.0 } } impl Deref for ParsedModules { type Target = HashMap; fn deref(&self) -> &Self::Target { &self.0 } } impl DerefMut for ParsedModules { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } } fn find_cycle( origin: NodeIndex, parent: NodeIndex, graph: &petgraph::Graph<(), ()>, path: &mut Vec, seen: &mut HashSet, ) -> bool { seen.insert(parent); for node in graph.neighbors_directed(parent, Direction::Outgoing) { if node == origin { path.push(node); return true; } if seen.contains(&node) { continue; } if find_cycle(origin, node, graph, path, seen) { path.push(node); return true; } } false } #[derive(Debug, Clone)] pub struct CheckedModule { pub name: String, pub code: String, pub input_path: PathBuf, pub kind: ModuleKind, pub ast: TypedModule, // pub extra: ModuleExtra, } #[derive(Debug, Clone)] pub struct CheckedModules(HashMap); impl From> for CheckedModules { fn from(checked_modules: HashMap) -> Self { CheckedModules(checked_modules) } } impl From for HashMap { fn from(checked_modules: CheckedModules) -> Self { checked_modules.0 } } impl CheckedModules { pub fn validators(&mut self) -> impl Iterator { self.0 .values_mut() .filter(|module| module.kind.is_validator()) } pub fn into_validators(self) -> impl Iterator { self.0 .into_values() .filter(|module| module.kind.is_validator()) } } impl Deref for CheckedModules { type Target = HashMap; fn deref(&self) -> &Self::Target { &self.0 } } impl DerefMut for CheckedModules { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } }