use ego_tree::{NodeId, NodeMut, NodeRef, Tree};
use proc_macro2::Ident;
use syn::Item;
use crate::{
syn_helpers::{ident_crate, ident_self, ident_super},
ItemId, Module, ModuleOrItem, ResolveError,
};
#[derive(Clone, Debug)]
pub struct ModuleInformation {
pub tree: Tree<Module>,
}
impl From<Tree<Module>> for ModuleInformation {
fn from(them: Tree<Module>) -> Self {
Self { tree: them }
}
}
#[derive(Debug)]
pub struct ItemRefMut<'a> {
node: NodeMut<'a, Module>,
id: usize,
}
impl<'a> ItemRefMut<'a> {
pub fn value(&'a mut self) -> &'a mut syn::Item {
self.node.value().items.get_mut(self.id).unwrap()
}
}
impl ModuleInformation {
pub fn get_crate<T>(&self, crat: T) -> Option<NodeRef<Module>>
where
Ident: PartialEq<T>,
{
self.tree
.root()
.children()
.find(|x| x.value().ident == crat)
}
pub fn get<'a>(&'a self, id: &ItemId) -> Option<&'a Item> {
self.tree
.get(id.node)
.and_then(|x| x.value().items.get(id.item_id))
}
pub fn get_mut<'a>(&'a mut self, id: &ItemId) -> Option<ItemRefMut<'a>> {
self.tree.get_mut(id.node).and_then(|mut x| {
if x.value().items.len() > id.item_id {
Some(ItemRefMut {
node: x,
id: id.item_id,
})
} else {
None
}
})
}
pub fn path(
&self,
relative_to: NodeId,
path: &syn::Path,
) -> Result<ModuleOrItem, ResolveError> {
let mut iterator = path.segments.iter().enumerate();
let mut module = if path.leading_colon.is_some() {
self.tree.root()
} else {
let mut module = self.tree.get(relative_to).ok_or(ResolveError::NotFound)?;
if let Some((_, seg1)) = iterator.next() {
if seg1.ident == ident_self() {
} else if seg1.ident == ident_crate() {
module = Module::get_crate(module).ok_or(ResolveError::NotFound)?;
} else if seg1.ident == ident_super() {
module = module.parent().ok_or(ResolveError::TooManySupers)?;
} else if let Some(child) = Module::get_item(module, self, &seg1.ident)? {
match child {
ModuleOrItem::Module(x) => module = self.tree.get(x).unwrap(),
ModuleOrItem::Item(_) if path.segments.len() > 1 => {
return Err(ResolveError::PrematureItem)
}
ModuleOrItem::Item(item) => return Ok(item.into()),
}
} else if let Some(child) = Module::get_item(self.tree.root(), self, &seg1.ident)? {
match child {
ModuleOrItem::Module(x) => module = self.tree.get(x).unwrap(),
ModuleOrItem::Item(_) => return Err(ResolveError::RootItem),
}
} else {
return Err(ResolveError::NotFound);
}
}
module
};
for (i, segment) in iterator {
if segment.ident == ident_self() {
} else if segment.ident == ident_super() {
module = module.parent().ok_or(ResolveError::TooManySupers)?;
} else {
match Module::get_item(module, self, &segment.ident)? {
Some(child) => match child {
ModuleOrItem::Module(x) => module = self.tree.get(x).unwrap(),
ModuleOrItem::Item(_) if path.segments.len() > i + 1 => {
return Err(ResolveError::PrematureItem)
}
ModuleOrItem::Item(item) => return Ok(item.into()),
},
None => {
return Err(ResolveError::NotFound);
}
}
}
}
Ok(module.id().into())
}
}