use std::collections::HashMap;
use syn::spanned::Spanned;
use syn::UseTree;
pub type AliasMap = HashMap<String, Vec<String>>;
pub type ScopedAliasMap = HashMap<Vec<String>, AliasMap>;
fn for_each_use_tree<F: FnMut(&UseTree)>(ast: &syn::File, mut f: F) {
ast.items
.iter()
.filter_map(|item| match item {
syn::Item::Use(u) => Some(u),
_ => None,
})
.for_each(|u| f(&u.tree));
}
pub fn gather_imports(ast: &syn::File) -> Vec<(Vec<String>, proc_macro2::Span)> {
let mut out = Vec::new();
for_each_use_tree(ast, |tree| collect_use_paths(&[], tree, &mut out));
out
}
pub fn collect_use_paths(
prefix: &[String],
tree: &UseTree,
out: &mut Vec<(Vec<String>, proc_macro2::Span)>,
) {
match tree {
UseTree::Path(p) => {
let mut next = prefix.to_vec();
next.push(p.ident.to_string());
collect_use_paths(&next, &p.tree, out);
}
UseTree::Name(n) => {
let mut full = prefix.to_vec();
full.push(n.ident.to_string());
out.push((full, n.ident.span()));
}
UseTree::Rename(r) => {
let mut full = prefix.to_vec();
full.push(r.ident.to_string());
out.push((full, r.ident.span()));
}
UseTree::Glob(g) => {
out.push((prefix.to_vec(), g.span()));
}
UseTree::Group(g) => {
for sub in &g.items {
collect_use_paths(prefix, sub, out);
}
}
}
}
pub fn gather_alias_map(ast: &syn::File) -> HashMap<String, Vec<String>> {
let mut out = HashMap::new();
for_each_use_tree(ast, |tree| collect_alias_entries(&[], tree, &mut out));
out
}
pub fn gather_alias_map_scoped(ast: &syn::File) -> ScopedAliasMap {
let mut out = ScopedAliasMap::new();
walk_scoped_aliases(&ast.items, &mut Vec::new(), &mut out);
out
}
fn walk_scoped_aliases(items: &[syn::Item], mod_stack: &mut Vec<String>, out: &mut ScopedAliasMap) {
let walk = |inner: &[syn::Item], stack: &mut Vec<String>, out: &mut ScopedAliasMap| {
walk_scoped_aliases(inner, stack, out);
};
{
let scope_map = out.entry(mod_stack.clone()).or_default();
for item in items {
if let syn::Item::Use(u) = item {
collect_alias_entries(&[], &u.tree, scope_map);
}
}
}
for item in items {
if let syn::Item::Mod(m) = item {
if let Some((_, inner)) = m.content.as_ref() {
mod_stack.push(m.ident.to_string());
walk(inner, mod_stack, out);
mod_stack.pop();
}
}
}
}
fn collect_alias_entries(
prefix: &[String],
tree: &UseTree,
out: &mut HashMap<String, Vec<String>>,
) {
match tree {
UseTree::Path(p) => {
let mut next = prefix.to_vec();
next.push(p.ident.to_string());
collect_alias_entries(&next, &p.tree, out);
}
UseTree::Name(n) => {
let ident = n.ident.to_string();
if ident == "self" {
if let Some(last) = prefix.last().cloned() {
out.insert(last, prefix.to_vec());
}
} else {
let mut full = prefix.to_vec();
full.push(ident.clone());
out.insert(ident, full);
}
}
UseTree::Rename(r) => {
if r.ident == "self" {
if prefix.is_empty() {
out.insert(r.rename.to_string(), vec!["self".to_string()]);
} else {
out.insert(r.rename.to_string(), prefix.to_vec());
}
} else {
let mut full = prefix.to_vec();
full.push(r.ident.to_string());
out.insert(r.rename.to_string(), full);
}
}
UseTree::Glob(_) => {
}
UseTree::Group(g) => {
for sub in &g.items {
collect_alias_entries(prefix, sub, out);
}
}
}
}