Skip to main content

ryo_source/generator/
tree.rs

1//! Module tree structure for source generation.
2//!
3//! `ModuleTree` represents a hierarchical module structure that can be
4//! converted into Rust source code. It captures:
5//! - Module name and visibility
6//! - Use statements
7//! - Inner attributes (module-level doc comments)
8//! - Items (structs, functions, etc.)
9//! - Child modules (nested `mod {}` blocks)
10
11use crate::pure::{PureAttribute, PureItem, PureUse, PureVis};
12
13/// A hierarchical module tree for source generation.
14///
15/// This structure represents a complete module hierarchy that can be
16/// transformed into source code by a `SourceGenerator`.
17///
18/// # Example
19///
20/// ```ignore
21/// // Build a tree representing:
22/// // mod utils {
23/// //     use std::io;
24/// //     pub fn helper() {}
25/// // }
26///
27/// let tree = ModuleTree::new("utils")
28///     .with_vis(PureVis::Public)
29///     .with_use(PureUse { ... })
30///     .with_item(PureItem::Fn(helper_fn));
31/// ```
32#[derive(Debug, Clone, Default)]
33pub struct ModuleTree {
34    /// Module name (empty string for crate root).
35    pub name: String,
36    /// Module visibility.
37    pub vis: PureVis,
38    /// Use statements for this module.
39    pub uses: Vec<PureUse>,
40    /// Inner attributes (e.g., `//! doc`, `#![allow(...)]`).
41    pub inner_attrs: Vec<PureAttribute>,
42    /// Items in this module (structs, functions, etc.).
43    pub items: Vec<PureItem>,
44    /// Child modules.
45    pub children: Vec<ModuleTree>,
46}
47
48impl ModuleTree {
49    /// Create a new module tree with the given name.
50    ///
51    /// For crate root, use an empty string or "crate".
52    pub fn new(name: impl Into<String>) -> Self {
53        Self {
54            name: name.into(),
55            vis: PureVis::Private,
56            uses: Vec::new(),
57            inner_attrs: Vec::new(),
58            items: Vec::new(),
59            children: Vec::new(),
60        }
61    }
62
63    /// Create a crate root module tree.
64    pub fn crate_root() -> Self {
65        Self::new("")
66    }
67
68    /// Set visibility.
69    pub fn with_vis(mut self, vis: PureVis) -> Self {
70        self.vis = vis;
71        self
72    }
73
74    /// Add a use statement.
75    pub fn with_use(mut self, use_stmt: PureUse) -> Self {
76        self.uses.push(use_stmt);
77        self
78    }
79
80    /// Add multiple use statements.
81    pub fn with_uses(mut self, uses: impl IntoIterator<Item = PureUse>) -> Self {
82        self.uses.extend(uses);
83        self
84    }
85
86    /// Add an inner attribute.
87    pub fn with_inner_attr(mut self, attr: PureAttribute) -> Self {
88        self.inner_attrs.push(attr);
89        self
90    }
91
92    /// Add an item.
93    pub fn with_item(mut self, item: PureItem) -> Self {
94        self.items.push(item);
95        self
96    }
97
98    /// Add multiple items.
99    pub fn with_items(mut self, items: impl IntoIterator<Item = PureItem>) -> Self {
100        self.items.extend(items);
101        self
102    }
103
104    /// Add a child module.
105    pub fn with_child(mut self, child: ModuleTree) -> Self {
106        self.children.push(child);
107        self
108    }
109
110    /// Add multiple child modules.
111    pub fn with_children(mut self, children: impl IntoIterator<Item = ModuleTree>) -> Self {
112        self.children.extend(children);
113        self
114    }
115
116    /// Check if this is the crate root.
117    pub fn is_crate_root(&self) -> bool {
118        self.name.is_empty() || self.name == "crate"
119    }
120
121    /// Get total item count (including children).
122    pub fn total_items(&self) -> usize {
123        self.items.len() + self.children.iter().map(|c| c.total_items()).sum::<usize>()
124    }
125
126    /// Get total module count (including self and children).
127    pub fn total_modules(&self) -> usize {
128        1 + self
129            .children
130            .iter()
131            .map(|c| c.total_modules())
132            .sum::<usize>()
133    }
134
135    /// Find a child module by name.
136    pub fn find_child(&self, name: &str) -> Option<&ModuleTree> {
137        self.children.iter().find(|c| c.name == name)
138    }
139
140    /// Find a child module by name (mutable).
141    pub fn find_child_mut(&mut self, name: &str) -> Option<&mut ModuleTree> {
142        self.children.iter_mut().find(|c| c.name == name)
143    }
144
145    /// Get or create a child module by name.
146    ///
147    /// If a child with the given name exists, returns a mutable reference to it.
148    /// Otherwise, creates a new child module and returns a mutable reference.
149    pub fn get_or_create_child(&mut self, name: &str) -> &mut ModuleTree {
150        if !self.children.iter().any(|c| c.name == name) {
151            self.children.push(ModuleTree::new(name));
152        }
153        self.find_child_mut(name)
154            .expect("child with matching name was pushed above when absent")
155    }
156
157    /// Navigate to a nested module by path.
158    ///
159    /// Path is a slice of module names, e.g., `["foo", "bar"]` for `foo::bar`.
160    pub fn navigate(&self, path: &[&str]) -> Option<&ModuleTree> {
161        if path.is_empty() {
162            return Some(self);
163        }
164        self.find_child(path[0])?.navigate(&path[1..])
165    }
166
167    /// Navigate to a nested module by path (mutable).
168    pub fn navigate_mut(&mut self, path: &[&str]) -> Option<&mut ModuleTree> {
169        if path.is_empty() {
170            return Some(self);
171        }
172        self.find_child_mut(path[0])?.navigate_mut(&path[1..])
173    }
174
175    /// Get or create nested modules by path.
176    ///
177    /// Creates any missing intermediate modules.
178    pub fn get_or_create_path(&mut self, path: &[&str]) -> &mut ModuleTree {
179        if path.is_empty() {
180            return self;
181        }
182        let child = self.get_or_create_child(path[0]);
183        child.get_or_create_path(&path[1..])
184    }
185}
186
187#[cfg(test)]
188mod tests {
189    use super::*;
190    use crate::pure::{PureBlock, PureFields, PureFn, PureGenerics, PureStruct, PureVis};
191
192    fn make_struct(name: &str) -> PureItem {
193        PureItem::Struct(PureStruct {
194            attrs: vec![],
195            vis: PureVis::Public,
196            name: name.to_string(),
197            generics: PureGenerics::default(),
198            fields: PureFields::Unit,
199        })
200    }
201
202    fn make_fn(name: &str) -> PureItem {
203        PureItem::Fn(PureFn {
204            attrs: vec![],
205            vis: PureVis::Public,
206            is_async: false,
207            is_async_inferred: false,
208            is_const: false,
209            is_unsafe: false,
210            abi: None,
211            name: name.to_string(),
212            generics: PureGenerics::default(),
213            params: vec![],
214            ret: None,
215            body: PureBlock::default(),
216        })
217    }
218
219    #[test]
220    fn test_module_tree_basic() {
221        let tree = ModuleTree::new("utils")
222            .with_vis(PureVis::Public)
223            .with_item(make_fn("helper"));
224
225        assert_eq!(tree.name, "utils");
226        assert_eq!(tree.vis, PureVis::Public);
227        assert_eq!(tree.items.len(), 1);
228        assert_eq!(tree.total_items(), 1);
229        assert_eq!(tree.total_modules(), 1);
230    }
231
232    #[test]
233    fn test_module_tree_nested() {
234        let tree = ModuleTree::crate_root()
235            .with_item(make_struct("Config"))
236            .with_child(
237                ModuleTree::new("models")
238                    .with_item(make_struct("User"))
239                    .with_child(ModuleTree::new("nested").with_item(make_struct("Deep"))),
240            );
241
242        assert!(tree.is_crate_root());
243        assert_eq!(tree.total_items(), 3);
244        assert_eq!(tree.total_modules(), 3);
245
246        let models = tree.find_child("models").unwrap();
247        assert_eq!(models.items.len(), 1);
248        assert_eq!(models.children.len(), 1);
249    }
250
251    #[test]
252    fn test_navigate() {
253        let tree = ModuleTree::crate_root().with_child(
254            ModuleTree::new("foo").with_child(ModuleTree::new("bar").with_item(make_struct("Baz"))),
255        );
256
257        let bar = tree.navigate(&["foo", "bar"]).unwrap();
258        assert_eq!(bar.name, "bar");
259        assert_eq!(bar.items.len(), 1);
260
261        assert!(tree.navigate(&["nonexistent"]).is_none());
262    }
263
264    #[test]
265    fn test_get_or_create_path() {
266        let mut tree = ModuleTree::crate_root();
267
268        let nested = tree.get_or_create_path(&["a", "b", "c"]);
269        nested.items.push(make_struct("Deep"));
270
271        assert!(tree.navigate(&["a", "b", "c"]).is_some());
272        assert_eq!(tree.total_modules(), 4); // root + a + b + c
273    }
274}