use crate::pure::{PureAttribute, PureItem, PureUse, PureVis};
#[derive(Debug, Clone, Default)]
pub struct ModuleTree {
pub name: String,
pub vis: PureVis,
pub uses: Vec<PureUse>,
pub inner_attrs: Vec<PureAttribute>,
pub items: Vec<PureItem>,
pub children: Vec<ModuleTree>,
}
impl ModuleTree {
pub fn new(name: impl Into<String>) -> Self {
Self {
name: name.into(),
vis: PureVis::Private,
uses: Vec::new(),
inner_attrs: Vec::new(),
items: Vec::new(),
children: Vec::new(),
}
}
pub fn crate_root() -> Self {
Self::new("")
}
pub fn with_vis(mut self, vis: PureVis) -> Self {
self.vis = vis;
self
}
pub fn with_use(mut self, use_stmt: PureUse) -> Self {
self.uses.push(use_stmt);
self
}
pub fn with_uses(mut self, uses: impl IntoIterator<Item = PureUse>) -> Self {
self.uses.extend(uses);
self
}
pub fn with_inner_attr(mut self, attr: PureAttribute) -> Self {
self.inner_attrs.push(attr);
self
}
pub fn with_item(mut self, item: PureItem) -> Self {
self.items.push(item);
self
}
pub fn with_items(mut self, items: impl IntoIterator<Item = PureItem>) -> Self {
self.items.extend(items);
self
}
pub fn with_child(mut self, child: ModuleTree) -> Self {
self.children.push(child);
self
}
pub fn with_children(mut self, children: impl IntoIterator<Item = ModuleTree>) -> Self {
self.children.extend(children);
self
}
pub fn is_crate_root(&self) -> bool {
self.name.is_empty() || self.name == "crate"
}
pub fn total_items(&self) -> usize {
self.items.len() + self.children.iter().map(|c| c.total_items()).sum::<usize>()
}
pub fn total_modules(&self) -> usize {
1 + self
.children
.iter()
.map(|c| c.total_modules())
.sum::<usize>()
}
pub fn find_child(&self, name: &str) -> Option<&ModuleTree> {
self.children.iter().find(|c| c.name == name)
}
pub fn find_child_mut(&mut self, name: &str) -> Option<&mut ModuleTree> {
self.children.iter_mut().find(|c| c.name == name)
}
pub fn get_or_create_child(&mut self, name: &str) -> &mut ModuleTree {
if !self.children.iter().any(|c| c.name == name) {
self.children.push(ModuleTree::new(name));
}
self.find_child_mut(name)
.expect("child with matching name was pushed above when absent")
}
pub fn navigate(&self, path: &[&str]) -> Option<&ModuleTree> {
if path.is_empty() {
return Some(self);
}
self.find_child(path[0])?.navigate(&path[1..])
}
pub fn navigate_mut(&mut self, path: &[&str]) -> Option<&mut ModuleTree> {
if path.is_empty() {
return Some(self);
}
self.find_child_mut(path[0])?.navigate_mut(&path[1..])
}
pub fn get_or_create_path(&mut self, path: &[&str]) -> &mut ModuleTree {
if path.is_empty() {
return self;
}
let child = self.get_or_create_child(path[0]);
child.get_or_create_path(&path[1..])
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::pure::{PureBlock, PureFields, PureFn, PureGenerics, PureStruct, PureVis};
fn make_struct(name: &str) -> PureItem {
PureItem::Struct(PureStruct {
attrs: vec![],
vis: PureVis::Public,
name: name.to_string(),
generics: PureGenerics::default(),
fields: PureFields::Unit,
})
}
fn make_fn(name: &str) -> PureItem {
PureItem::Fn(PureFn {
attrs: vec![],
vis: PureVis::Public,
is_async: false,
is_async_inferred: false,
is_const: false,
is_unsafe: false,
abi: None,
name: name.to_string(),
generics: PureGenerics::default(),
params: vec![],
ret: None,
body: PureBlock::default(),
})
}
#[test]
fn test_module_tree_basic() {
let tree = ModuleTree::new("utils")
.with_vis(PureVis::Public)
.with_item(make_fn("helper"));
assert_eq!(tree.name, "utils");
assert_eq!(tree.vis, PureVis::Public);
assert_eq!(tree.items.len(), 1);
assert_eq!(tree.total_items(), 1);
assert_eq!(tree.total_modules(), 1);
}
#[test]
fn test_module_tree_nested() {
let tree = ModuleTree::crate_root()
.with_item(make_struct("Config"))
.with_child(
ModuleTree::new("models")
.with_item(make_struct("User"))
.with_child(ModuleTree::new("nested").with_item(make_struct("Deep"))),
);
assert!(tree.is_crate_root());
assert_eq!(tree.total_items(), 3);
assert_eq!(tree.total_modules(), 3);
let models = tree.find_child("models").unwrap();
assert_eq!(models.items.len(), 1);
assert_eq!(models.children.len(), 1);
}
#[test]
fn test_navigate() {
let tree = ModuleTree::crate_root().with_child(
ModuleTree::new("foo").with_child(ModuleTree::new("bar").with_item(make_struct("Baz"))),
);
let bar = tree.navigate(&["foo", "bar"]).unwrap();
assert_eq!(bar.name, "bar");
assert_eq!(bar.items.len(), 1);
assert!(tree.navigate(&["nonexistent"]).is_none());
}
#[test]
fn test_get_or_create_path() {
let mut tree = ModuleTree::crate_root();
let nested = tree.get_or_create_path(&["a", "b", "c"]);
nested.items.push(make_struct("Deep"));
assert!(tree.navigate(&["a", "b", "c"]).is_some());
assert_eq!(tree.total_modules(), 4); }
}