ryo_source/generator/
tree.rs1use crate::pure::{PureAttribute, PureItem, PureUse, PureVis};
12
13#[derive(Debug, Clone, Default)]
33pub struct ModuleTree {
34 pub name: String,
36 pub vis: PureVis,
38 pub uses: Vec<PureUse>,
40 pub outer_attrs: Vec<PureAttribute>,
44 pub inner_attrs: Vec<PureAttribute>,
47 pub items: Vec<PureItem>,
49 pub children: Vec<ModuleTree>,
51}
52
53impl ModuleTree {
54 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 pub fn crate_root() -> Self {
71 Self::new("")
72 }
73
74 pub fn with_vis(mut self, vis: PureVis) -> Self {
76 self.vis = vis;
77 self
78 }
79
80 pub fn with_use(mut self, use_stmt: PureUse) -> Self {
82 self.uses.push(use_stmt);
83 self
84 }
85
86 pub fn with_uses(mut self, uses: impl IntoIterator<Item = PureUse>) -> Self {
88 self.uses.extend(uses);
89 self
90 }
91
92 pub fn with_outer_attr(mut self, attr: PureAttribute) -> Self {
94 self.outer_attrs.push(attr);
95 self
96 }
97
98 pub fn with_outer_attrs(mut self, attrs: impl IntoIterator<Item = PureAttribute>) -> Self {
100 self.outer_attrs.extend(attrs);
101 self
102 }
103
104 pub fn with_inner_attr(mut self, attr: PureAttribute) -> Self {
106 self.inner_attrs.push(attr);
107 self
108 }
109
110 pub fn with_item(mut self, item: PureItem) -> Self {
112 self.items.push(item);
113 self
114 }
115
116 pub fn with_items(mut self, items: impl IntoIterator<Item = PureItem>) -> Self {
118 self.items.extend(items);
119 self
120 }
121
122 pub fn with_child(mut self, child: ModuleTree) -> Self {
124 self.children.push(child);
125 self
126 }
127
128 pub fn with_children(mut self, children: impl IntoIterator<Item = ModuleTree>) -> Self {
130 self.children.extend(children);
131 self
132 }
133
134 pub fn is_crate_root(&self) -> bool {
136 self.name.is_empty() || self.name == "crate"
137 }
138
139 pub fn total_items(&self) -> usize {
141 self.items.len() + self.children.iter().map(|c| c.total_items()).sum::<usize>()
142 }
143
144 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 pub fn find_child(&self, name: &str) -> Option<&ModuleTree> {
155 self.children.iter().find(|c| c.name == name)
156 }
157
158 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 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 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 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 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 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); }
313}