use std::collections::{HashMap, HashSet};
use crate::adapters::shared::file_to_module::file_to_module;
use super::ModuleGraph;
pub(super) fn build_module_graph(parsed: &[(String, String, syn::File)]) -> ModuleGraph {
let (modules, index) = collect_modules(parsed);
let mut forward = vec![HashSet::<usize>::new(); modules.len()];
for (path, _, syntax) in parsed {
let source_idx = index[&file_to_module(path)];
add_edges(
&mut forward[source_idx],
source_idx,
&crate_dep_modules(syntax),
&index,
);
}
ModuleGraph {
modules,
forward: sort_adjacency(forward),
}
}
fn collect_modules(
parsed: &[(String, String, syn::File)],
) -> (Vec<String>, HashMap<String, usize>) {
let mut modules: Vec<String> = parsed
.iter()
.map(|(path, _, _)| file_to_module(path))
.collect::<HashSet<String>>()
.into_iter()
.collect();
modules.sort();
let index = modules
.iter()
.enumerate()
.map(|(i, name)| (name.clone(), i))
.collect();
(modules, index)
}
fn crate_dep_modules(syntax: &syn::File) -> Vec<String> {
use_paths(syntax)
.into_iter()
.filter_map(|segs| {
(segs.first().is_some_and(|s| s == "crate") && segs.len() >= 2).then(|| segs[1].clone())
})
.collect()
}
fn use_paths(syntax: &syn::File) -> Vec<Vec<String>> {
let mut stack: Vec<(&syn::UseTree, Vec<String>)> = syntax
.items
.iter()
.filter_map(|item| match item {
syn::Item::Use(u) => Some((&u.tree, Vec::new())),
_ => None,
})
.collect();
let mut paths = Vec::new();
while let Some((tree, segments)) = stack.pop() {
match tree {
syn::UseTree::Path(p) => stack.push((&p.tree, pushed(&segments, &p.ident))),
syn::UseTree::Group(g) => g
.items
.iter()
.for_each(|sub| stack.push((sub, segments.clone()))),
syn::UseTree::Name(name) => paths.push(pushed(&segments, &name.ident)),
syn::UseTree::Rename(r) => paths.push(pushed(&segments, &r.ident)),
syn::UseTree::Glob(_) => paths.push(segments),
}
}
paths
}
fn pushed(segments: &[String], ident: &syn::Ident) -> Vec<String> {
let mut s = segments.to_vec();
s.push(ident.to_string());
s
}
fn add_edges(
row: &mut HashSet<usize>,
source_idx: usize,
deps: &[String],
index: &HashMap<String, usize>,
) {
deps.iter()
.filter_map(|dep| index.get(dep.as_str()).copied())
.filter(|&dep_idx| dep_idx != source_idx)
.for_each(|dep_idx| {
row.insert(dep_idx);
});
}
fn sort_adjacency(forward: Vec<HashSet<usize>>) -> Vec<Vec<usize>> {
forward
.into_iter()
.map(|set| {
let mut v: Vec<usize> = set.into_iter().collect();
v.sort();
v
})
.collect()
}