aster/map/
view_builder.rs1use std::collections::HashMap;
6
7use super::layer_classifier::LayerClassifier;
8use super::types::ModuleNode;
9use super::types_enhanced::*;
10
11pub struct ViewBuilder {
13 classifier: LayerClassifier,
14}
15
16impl ViewBuilder {
17 pub fn new() -> Self {
18 Self {
19 classifier: LayerClassifier::new(),
20 }
21 }
22
23 pub fn build_views(&self, modules: &[ModuleNode]) -> Views {
25 Views {
26 directory_tree: self.build_directory_tree(modules),
27 architecture_layers: self.build_architecture_layers(modules),
28 }
29 }
30
31 pub fn build_directory_tree(&self, modules: &[ModuleNode]) -> DirectoryNode {
33 let mut root = DirectoryNode {
34 name: "src".to_string(),
35 path: "src".to_string(),
36 node_type: DirectoryNodeType::Directory,
37 description: None,
38 purpose: None,
39 module_id: None,
40 children: Some(Vec::new()),
41 };
42
43 let mut sorted_modules: Vec<_> = modules.iter().collect();
44 sorted_modules.sort_by(|a, b| a.id.cmp(&b.id));
45
46 let mut dir_cache: HashMap<String, usize> = HashMap::new();
47
48 for module in sorted_modules {
49 let path = &module.id;
50 if !path.starts_with("src/") && !path.starts_with("src\\") {
51 continue;
52 }
53
54 let parts: Vec<&str> = path.split(&['/', '\\'][..]).collect();
55 self.insert_module_node(&mut root, &parts, 1, module, &mut dir_cache);
56 }
57
58 self.sort_directory_children(&mut root);
59 root
60 }
61
62 fn insert_module_node(
63 &self,
64 parent: &mut DirectoryNode,
65 parts: &[&str],
66 index: usize,
67 module: &ModuleNode,
68 _dir_cache: &mut HashMap<String, usize>,
69 ) {
70 if index >= parts.len() {
71 return;
72 }
73
74 let part = parts[index];
75 let is_last = index == parts.len() - 1;
76 let current_path = parts[..=index].join("/");
77
78 let children = parent.children.get_or_insert_with(Vec::new);
79
80 if is_last {
81 children.push(DirectoryNode {
82 name: part.to_string(),
83 path: current_path,
84 node_type: DirectoryNodeType::File,
85 description: None,
86 purpose: None,
87 module_id: Some(module.id.clone()),
88 children: None,
89 });
90 } else {
91 let dir_idx = children
92 .iter()
93 .position(|c| c.name == part && c.node_type == DirectoryNodeType::Directory);
94
95 if let Some(idx) = dir_idx {
96 self.insert_module_node(&mut children[idx], parts, index + 1, module, _dir_cache);
97 } else {
98 let mut new_dir = DirectoryNode {
99 name: part.to_string(),
100 path: current_path,
101 node_type: DirectoryNodeType::Directory,
102 description: None,
103 purpose: None,
104 module_id: None,
105 children: Some(Vec::new()),
106 };
107 self.insert_module_node(&mut new_dir, parts, index + 1, module, _dir_cache);
108 children.push(new_dir);
109 }
110 }
111 }
112
113 fn sort_directory_children(&self, node: &mut DirectoryNode) {
114 if let Some(ref mut children) = node.children {
115 children.sort_by(|a, b| match (&a.node_type, &b.node_type) {
116 (DirectoryNodeType::Directory, DirectoryNodeType::File) => std::cmp::Ordering::Less,
117 (DirectoryNodeType::File, DirectoryNodeType::Directory) => {
118 std::cmp::Ordering::Greater
119 }
120 _ => a.name.cmp(&b.name),
121 });
122
123 for child in children.iter_mut() {
124 if child.node_type == DirectoryNodeType::Directory {
125 self.sort_directory_children(child);
126 }
127 }
128 }
129 }
130
131 pub fn build_architecture_layers(&self, modules: &[ModuleNode]) -> ArchitectureLayers {
133 let mut layers = ArchitectureLayers::default();
134
135 for module in modules {
136 let result = self.classifier.classify(module);
137
138 let layer_info = match result.layer {
139 ArchitectureLayer::Presentation => &mut layers.presentation,
140 ArchitectureLayer::Business => &mut layers.business,
141 ArchitectureLayer::Data => &mut layers.data,
142 ArchitectureLayer::Infrastructure => &mut layers.infrastructure,
143 ArchitectureLayer::CrossCutting => &mut layers.cross_cutting,
144 };
145
146 layer_info.modules.push(module.id.clone());
147
148 if let Some(sub) = result.sub_layer {
149 let sub_layers = layer_info.sub_layers.get_or_insert_with(HashMap::new);
150 sub_layers.entry(sub).or_default().push(module.id.clone());
151 }
152 }
153
154 layers.presentation.modules.sort();
156 layers.business.modules.sort();
157 layers.data.modules.sort();
158 layers.infrastructure.modules.sort();
159 layers.cross_cutting.modules.sort();
160
161 layers
162 }
163}
164
165impl Default for ViewBuilder {
166 fn default() -> Self {
167 Self::new()
168 }
169}
170
171pub fn count_tree_nodes(node: &DirectoryNode) -> (usize, usize) {
173 let mut dirs = 0;
174 let mut files = 0;
175
176 fn count(n: &DirectoryNode, dirs: &mut usize, files: &mut usize) {
177 match n.node_type {
178 DirectoryNodeType::Directory => *dirs += 1,
179 DirectoryNodeType::File => *files += 1,
180 }
181 if let Some(ref children) = n.children {
182 for child in children {
183 count(child, dirs, files);
184 }
185 }
186 }
187
188 count(node, &mut dirs, &mut files);
189 (dirs, files)
190}
191
192pub fn get_tree_depth(node: &DirectoryNode) -> usize {
194 fn depth(n: &DirectoryNode, current: usize) -> usize {
195 if let Some(ref children) = n.children {
196 children
197 .iter()
198 .map(|c| depth(c, current + 1))
199 .max()
200 .unwrap_or(current)
201 } else {
202 current
203 }
204 }
205 depth(node, 0)
206}
207
208pub fn build_views(modules: &[ModuleNode]) -> Views {
210 ViewBuilder::new().build_views(modules)
211}
212
213pub fn build_directory_tree(modules: &[ModuleNode]) -> DirectoryNode {
215 ViewBuilder::new().build_directory_tree(modules)
216}
217
218pub fn build_architecture_layers(modules: &[ModuleNode]) -> ArchitectureLayers {
220 ViewBuilder::new().build_architecture_layers(modules)
221}