use proc_macro::Span;
use std::fs::read_to_string;
use syn::{
parse_str,
spanned::Spanned,
visit::{visit_item_mod, Visit},
File, Ident, ItemMod,
};
struct ModVisitor<'span, 'ast> {
target: &'span Span,
stack: Vec<&'ast ItemMod>,
result: Option<Vec<Ident>>,
}
impl<'span, 'ast> Visit<'ast> for ModVisitor<'span, 'ast> {
fn visit_item_mod(&mut self, module: &'ast ItemMod) {
if contains(&module.span().unwrap(), self.target) {
self.stack.push(module);
visit_item_mod(self, module);
if self.result.is_none() {
self.result = Some(
self.stack
.iter()
.map(|module| module.ident.clone())
.collect(),
)
}
self.stack.pop();
}
}
}
fn contains(left: &Span, right: &Span) -> bool {
left.join(*right).map_or(false, |join| join.eq(left))
}
pub fn module_path(span: &Span) -> Vec<Ident> {
let source = span.source_file();
let path = source.path();
let contents =
read_to_string(&path).expect(&format!("`read_to_string` failed for `{:?}`", path));
let file: File =
parse_str(&contents).expect(&format!("Could not parse `{:?}` contents", source));
let mut visitor = ModVisitor {
target: span,
stack: Vec::new(),
result: None,
};
visitor.visit_file(&file);
visitor.result.unwrap_or_default()
}