210 lines
4.9 KiB
Rust
210 lines
4.9 KiB
Rust
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<String>) {
|
|
let name = self.name.clone();
|
|
|
|
let deps: Vec<_> = self
|
|
.ast
|
|
.dependencies()
|
|
.into_iter()
|
|
.map(|(dep, _span)| dep)
|
|
.collect();
|
|
|
|
(name, deps)
|
|
}
|
|
}
|
|
|
|
pub struct ParsedModules(HashMap<String, ParsedModule>);
|
|
|
|
impl ParsedModules {
|
|
pub fn sequence(&self) -> Result<Vec<String>, Error> {
|
|
let inputs = self
|
|
.0
|
|
.values()
|
|
.map(|m| m.deps_for_graph())
|
|
.collect::<Vec<(String, Vec<String>)>>();
|
|
|
|
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<HashMap<String, ParsedModule>> for ParsedModules {
|
|
fn from(parsed_modules: HashMap<String, ParsedModule>) -> Self {
|
|
ParsedModules(parsed_modules)
|
|
}
|
|
}
|
|
|
|
impl From<ParsedModules> for HashMap<String, ParsedModule> {
|
|
fn from(parsed_modules: ParsedModules) -> Self {
|
|
parsed_modules.0
|
|
}
|
|
}
|
|
|
|
impl Deref for ParsedModules {
|
|
type Target = HashMap<String, ParsedModule>;
|
|
|
|
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<NodeIndex>,
|
|
seen: &mut HashSet<NodeIndex>,
|
|
) -> 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<String, CheckedModule>);
|
|
|
|
impl From<HashMap<String, CheckedModule>> for CheckedModules {
|
|
fn from(checked_modules: HashMap<String, CheckedModule>) -> Self {
|
|
CheckedModules(checked_modules)
|
|
}
|
|
}
|
|
|
|
impl From<CheckedModules> for HashMap<String, CheckedModule> {
|
|
fn from(checked_modules: CheckedModules) -> Self {
|
|
checked_modules.0
|
|
}
|
|
}
|
|
|
|
impl CheckedModules {
|
|
pub fn validators(&mut self) -> impl Iterator<Item = &mut CheckedModule> {
|
|
self.0
|
|
.values_mut()
|
|
.filter(|module| module.kind.is_validator())
|
|
}
|
|
|
|
pub fn into_validators(self) -> impl Iterator<Item = CheckedModule> {
|
|
self.0
|
|
.into_values()
|
|
.filter(|module| module.kind.is_validator())
|
|
}
|
|
}
|
|
|
|
impl Deref for CheckedModules {
|
|
type Target = HashMap<String, CheckedModule>;
|
|
|
|
fn deref(&self) -> &Self::Target {
|
|
&self.0
|
|
}
|
|
}
|
|
|
|
impl DerefMut for CheckedModules {
|
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
&mut self.0
|
|
}
|
|
}
|