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    /// Outer attributes on the module declaration itself
41    /// (e.g., `#[cfg(test)] mod tests;` or `#[cfg(test)] mod tests { ... }`).
42    /// Elements should carry `is_inner: false`.
43    pub outer_attrs: Vec<PureAttribute>,
44    /// Inner attributes (e.g., `//! doc`, `#![allow(...)]`).
45    /// Elements should carry `is_inner: true`.
46    pub inner_attrs: Vec<PureAttribute>,
47    /// Items in this module (structs, functions, etc.).
48    pub items: Vec<PureItem>,
49    /// Child modules.
50    pub children: Vec<ModuleTree>,
51}
52
53impl ModuleTree {
54    /// Create a new module tree with the given name.
55    ///
56    /// For crate root, use an empty string or "crate".
57    pub fn new(name: impl Into<String>) -> Self {
58        Self {
59            name: name.into(),
60            vis: PureVis::Private,
61            uses: Vec::new(),
62            outer_attrs: Vec::new(),
63            inner_attrs: Vec::new(),
64            items: Vec::new(),
65            children: Vec::new(),
66        }
67    }
68
69    /// Create a crate root module tree.
70    pub fn crate_root() -> Self {
71        Self::new("")
72    }
73
74    /// Set visibility.
75    pub fn with_vis(mut self, vis: PureVis) -> Self {
76        self.vis = vis;
77        self
78    }
79
80    /// Add a use statement.
81    pub fn with_use(mut self, use_stmt: PureUse) -> Self {
82        self.uses.push(use_stmt);
83        self
84    }
85
86    /// Add multiple use statements.
87    pub fn with_uses(mut self, uses: impl IntoIterator<Item = PureUse>) -> Self {
88        self.uses.extend(uses);
89        self
90    }
91
92    /// Add an outer attribute (`#[...]` on the module declaration).
93    pub fn with_outer_attr(mut self, attr: PureAttribute) -> Self {
94        self.outer_attrs.push(attr);
95        self
96    }
97
98    /// Add multiple outer attributes.
99    pub fn with_outer_attrs(mut self, attrs: impl IntoIterator<Item = PureAttribute>) -> Self {
100        self.outer_attrs.extend(attrs);
101        self
102    }
103
104    /// Add an inner attribute.
105    pub fn with_inner_attr(mut self, attr: PureAttribute) -> Self {
106        self.inner_attrs.push(attr);
107        self
108    }
109
110    /// Add an item.
111    pub fn with_item(mut self, item: PureItem) -> Self {
112        self.items.push(item);
113        self
114    }
115
116    /// Add multiple items.
117    pub fn with_items(mut self, items: impl IntoIterator<Item = PureItem>) -> Self {
118        self.items.extend(items);
119        self
120    }
121
122    /// Add a child module.
123    pub fn with_child(mut self, child: ModuleTree) -> Self {
124        self.children.push(child);
125        self
126    }
127
128    /// Add multiple child modules.
129    pub fn with_children(mut self, children: impl IntoIterator<Item = ModuleTree>) -> Self {
130        self.children.extend(children);
131        self
132    }
133
134    /// Check if this is the crate root.
135    pub fn is_crate_root(&self) -> bool {
136        self.name.is_empty() || self.name == "crate"
137    }
138
139    /// Get total item count (including children).
140    pub fn total_items(&self) -> usize {
141        self.items.len() + self.children.iter().map(|c| c.total_items()).sum::<usize>()
142    }
143
144    /// Get total module count (including self and children).
145    pub fn total_modules(&self) -> usize {
146        1 + self
147            .children
148            .iter()
149            .map(|c| c.total_modules())
150            .sum::<usize>()
151    }
152
153    /// Find a child module by name.
154    pub fn find_child(&self, name: &str) -> Option<&ModuleTree> {
155        self.children.iter().find(|c| c.name == name)
156    }
157
158    /// Find a child module by name (mutable).
159    pub fn find_child_mut(&mut self, name: &str) -> Option<&mut ModuleTree> {
160        self.children.iter_mut().find(|c| c.name == name)
161    }
162
163    /// Get or create a child module by name.
164    ///
165    /// If a child with the given name exists, returns a mutable reference to it.
166    /// Otherwise, creates a new child module and returns a mutable reference.
167    pub fn get_or_create_child(&mut self, name: &str) -> &mut ModuleTree {
168        if !self.children.iter().any(|c| c.name == name) {
169            self.children.push(ModuleTree::new(name));
170        }
171        self.find_child_mut(name)
172            .expect("child with matching name was pushed above when absent")
173    }
174
175    /// Navigate to a nested module by path.
176    ///
177    /// Path is a slice of module names, e.g., `["foo", "bar"]` for `foo::bar`.
178    pub fn navigate(&self, path: &[&str]) -> Option<&ModuleTree> {
179        if path.is_empty() {
180            return Some(self);
181        }
182        self.find_child(path[0])?.navigate(&path[1..])
183    }
184
185    /// Navigate to a nested module by path (mutable).
186    pub fn navigate_mut(&mut self, path: &[&str]) -> Option<&mut ModuleTree> {
187        if path.is_empty() {
188            return Some(self);
189        }
190        self.find_child_mut(path[0])?.navigate_mut(&path[1..])
191    }
192
193    /// Get or create nested modules by path.
194    ///
195    /// Creates any missing intermediate modules.
196    pub fn get_or_create_path(&mut self, path: &[&str]) -> &mut ModuleTree {
197        if path.is_empty() {
198            return self;
199        }
200        let child = self.get_or_create_child(path[0]);
201        child.get_or_create_path(&path[1..])
202    }
203}
204
205#[cfg(test)]
206mod tests {
207    use super::*;
208    use crate::pure::{
209        PureAttrMeta, PureBlock, PureFields, PureFn, PureGenerics, PureStruct, PureVis,
210    };
211
212    fn make_struct(name: &str) -> PureItem {
213        PureItem::Struct(PureStruct {
214            attrs: vec![],
215            vis: PureVis::Public,
216            name: name.to_string(),
217            generics: PureGenerics::default(),
218            fields: PureFields::Unit,
219        })
220    }
221
222    fn make_fn(name: &str) -> PureItem {
223        PureItem::Fn(PureFn {
224            attrs: vec![],
225            vis: PureVis::Public,
226            is_async: false,
227            is_async_inferred: false,
228            is_const: false,
229            is_unsafe: false,
230            abi: None,
231            name: name.to_string(),
232            generics: PureGenerics::default(),
233            params: vec![],
234            ret: None,
235            body: PureBlock::default(),
236        })
237    }
238
239    #[test]
240    fn test_module_tree_basic() {
241        let tree = ModuleTree::new("utils")
242            .with_vis(PureVis::Public)
243            .with_item(make_fn("helper"));
244
245        assert_eq!(tree.name, "utils");
246        assert_eq!(tree.vis, PureVis::Public);
247        assert_eq!(tree.items.len(), 1);
248        assert_eq!(tree.total_items(), 1);
249        assert_eq!(tree.total_modules(), 1);
250    }
251
252    #[test]
253    fn test_module_tree_nested() {
254        let tree = ModuleTree::crate_root()
255            .with_item(make_struct("Config"))
256            .with_child(
257                ModuleTree::new("models")
258                    .with_item(make_struct("User"))
259                    .with_child(ModuleTree::new("nested").with_item(make_struct("Deep"))),
260            );
261
262        assert!(tree.is_crate_root());
263        assert_eq!(tree.total_items(), 3);
264        assert_eq!(tree.total_modules(), 3);
265
266        let models = tree.find_child("models").unwrap();
267        assert_eq!(models.items.len(), 1);
268        assert_eq!(models.children.len(), 1);
269    }
270
271    #[test]
272    fn test_navigate() {
273        let tree = ModuleTree::crate_root().with_child(
274            ModuleTree::new("foo").with_child(ModuleTree::new("bar").with_item(make_struct("Baz"))),
275        );
276
277        let bar = tree.navigate(&["foo", "bar"]).unwrap();
278        assert_eq!(bar.name, "bar");
279        assert_eq!(bar.items.len(), 1);
280
281        assert!(tree.navigate(&["nonexistent"]).is_none());
282    }
283
284    #[test]
285    fn test_outer_attrs_builder() {
286        // Boundary: ModuleTree must be able to carry outer attrs on `mod foo;`
287        // declarations (R9-class regression prevention at the public API layer).
288        let cfg_test = PureAttribute {
289            path: "cfg".to_string(),
290            meta: PureAttrMeta::List("test".to_string()),
291            is_inner: false,
292        };
293        let tree = ModuleTree::new("tests")
294            .with_outer_attr(cfg_test.clone())
295            .with_item(make_fn("inner_check"));
296
297        assert_eq!(tree.outer_attrs.len(), 1);
298        assert_eq!(tree.outer_attrs[0].path, "cfg");
299        assert!(!tree.outer_attrs[0].is_inner);
300        assert_eq!(tree.inner_attrs.len(), 0);
301    }
302
303    #[test]
304    fn test_get_or_create_path() {
305        let mut tree = ModuleTree::crate_root();
306
307        let nested = tree.get_or_create_path(&["a", "b", "c"]);
308        nested.items.push(make_struct("Deep"));
309
310        assert!(tree.navigate(&["a", "b", "c"]).is_some());
311        assert_eq!(tree.total_modules(), 4); // root + a + b + c
312    }
313}