1use std::path::PathBuf;
2
3#[derive(Debug, Clone, PartialEq, Eq)]
5pub enum NodeType {
6 Root,
8 Translation,
10 KeyPath,
12 CodeRef,
14}
15
16#[derive(Debug, Clone, PartialEq, Eq)]
18pub struct Location {
19 pub file: PathBuf,
20 pub line: usize,
21}
22
23impl Location {
24 pub fn new(file: PathBuf, line: usize) -> Self {
25 Self { file, line }
26 }
27}
28
29#[derive(Debug, Clone)]
31pub struct TreeNode {
32 pub node_type: NodeType,
33 pub content: String,
34 pub location: Option<Location>,
35 pub children: Vec<TreeNode>,
36}
37
38impl TreeNode {
39 pub fn new(node_type: NodeType, content: String) -> Self {
41 Self {
42 node_type,
43 content,
44 location: None,
45 children: Vec::new(),
46 }
47 }
48
49 pub fn with_location(
51 node_type: NodeType,
52 content: String,
53 location: Location,
54 ) -> Self {
55 Self {
56 node_type,
57 content,
58 location: Some(location),
59 children: Vec::new(),
60 }
61 }
62
63 pub fn add_child(&mut self, child: TreeNode) {
65 self.children.push(child);
66 }
67
68 pub fn has_children(&self) -> bool {
70 !self.children.is_empty()
71 }
72
73 pub fn child_count(&self) -> usize {
75 self.children.len()
76 }
77
78 pub fn node_count(&self) -> usize {
80 1 + self.children.iter().map(|c| c.node_count()).sum::<usize>()
81 }
82
83 pub fn max_depth(&self) -> usize {
85 if self.children.is_empty() {
86 1
87 } else {
88 1 + self.children.iter().map(|c| c.max_depth()).max().unwrap_or(0)
89 }
90 }
91}
92
93#[derive(Debug)]
95pub struct ReferenceTree {
96 pub root: TreeNode,
97}
98
99impl ReferenceTree {
100 pub fn new(root: TreeNode) -> Self {
102 Self { root }
103 }
104
105 pub fn with_search_text(search_text: String) -> Self {
107 Self {
108 root: TreeNode::new(NodeType::Root, search_text),
109 }
110 }
111
112 pub fn node_count(&self) -> usize {
114 self.root.node_count()
115 }
116
117 pub fn max_depth(&self) -> usize {
119 self.root.max_depth()
120 }
121
122 pub fn has_results(&self) -> bool {
124 self.root.has_children()
125 }
126}
127
128#[cfg(test)]
129mod tests {
130 use super::*;
131
132 #[test]
133 fn test_create_tree_node() {
134 let node = TreeNode::new(NodeType::Root, "add new".to_string());
135 assert_eq!(node.content, "add new");
136 assert_eq!(node.node_type, NodeType::Root);
137 assert!(node.location.is_none());
138 assert!(node.children.is_empty());
139 }
140
141 #[test]
142 fn test_create_tree_node_with_location() {
143 let location = Location::new(PathBuf::from("test.yml"), 10);
144 let node = TreeNode::with_location(
145 NodeType::Translation,
146 "add_new: 'add new'".to_string(),
147 location.clone(),
148 );
149
150 assert_eq!(node.content, "add_new: 'add new'");
151 assert_eq!(node.node_type, NodeType::Translation);
152 assert!(node.location.is_some());
153 assert_eq!(node.location.unwrap().line, 10);
154 }
155
156 #[test]
157 fn test_add_child() {
158 let mut parent = TreeNode::new(NodeType::Root, "root".to_string());
159 let child = TreeNode::new(NodeType::Translation, "child".to_string());
160
161 assert_eq!(parent.child_count(), 0);
162 parent.add_child(child);
163 assert_eq!(parent.child_count(), 1);
164 assert!(parent.has_children());
165 }
166
167 #[test]
168 fn test_node_count() {
169 let mut root = TreeNode::new(NodeType::Root, "root".to_string());
170 let mut child1 = TreeNode::new(NodeType::Translation, "child1".to_string());
171 let child2 = TreeNode::new(NodeType::Translation, "child2".to_string());
172 let grandchild = TreeNode::new(NodeType::KeyPath, "grandchild".to_string());
173
174 child1.add_child(grandchild);
175 root.add_child(child1);
176 root.add_child(child2);
177
178 assert_eq!(root.node_count(), 4);
180 }
181
182 #[test]
183 fn test_max_depth() {
184 let mut root = TreeNode::new(NodeType::Root, "root".to_string());
185 let mut child = TreeNode::new(NodeType::Translation, "child".to_string());
186 let grandchild = TreeNode::new(NodeType::KeyPath, "grandchild".to_string());
187
188 assert_eq!(root.max_depth(), 1);
190
191 root.add_child(child.clone());
193 assert_eq!(root.max_depth(), 2);
194
195 child.add_child(grandchild);
197 root.children[0] = child;
198 assert_eq!(root.max_depth(), 3);
199 }
200
201 #[test]
202 fn test_reference_tree_creation() {
203 let tree = ReferenceTree::with_search_text("add new".to_string());
204 assert_eq!(tree.root.content, "add new");
205 assert_eq!(tree.root.node_type, NodeType::Root);
206 assert!(!tree.has_results());
207 }
208
209 #[test]
210 fn test_reference_tree_with_results() {
211 let mut root = TreeNode::new(NodeType::Root, "add new".to_string());
212 let child = TreeNode::new(NodeType::Translation, "translation".to_string());
213 root.add_child(child);
214
215 let tree = ReferenceTree::new(root);
216 assert!(tree.has_results());
217 assert_eq!(tree.node_count(), 2);
218 assert_eq!(tree.max_depth(), 2);
219 }
220
221 #[test]
222 fn test_location_creation() {
223 let location = Location::new(PathBuf::from("test.yml"), 42);
224 assert_eq!(location.file, PathBuf::from("test.yml"));
225 assert_eq!(location.line, 42);
226 }
227
228 #[test]
229 fn test_node_types() {
230 let root = NodeType::Root;
231 let translation = NodeType::Translation;
232 let key_path = NodeType::KeyPath;
233 let code_ref = NodeType::CodeRef;
234
235 assert_eq!(root, NodeType::Root);
236 assert_eq!(translation, NodeType::Translation);
237 assert_eq!(key_path, NodeType::KeyPath);
238 assert_eq!(code_ref, NodeType::CodeRef);
239 }
240
241 #[test]
242 fn test_complex_tree_structure() {
243 let mut root = TreeNode::new(NodeType::Root, "add new".to_string());
245
246 let mut translation = TreeNode::with_location(
247 NodeType::Translation,
248 "add_new: 'add new'".to_string(),
249 Location::new(PathBuf::from("en.yml"), 4),
250 );
251
252 let mut key_path = TreeNode::new(
253 NodeType::KeyPath,
254 "invoice.labels.add_new".to_string(),
255 );
256
257 let code_ref = TreeNode::with_location(
258 NodeType::CodeRef,
259 "I18n.t('invoice.labels.add_new')".to_string(),
260 Location::new(PathBuf::from("invoices.ts"), 14),
261 );
262
263 key_path.add_child(code_ref);
264 translation.add_child(key_path);
265 root.add_child(translation);
266
267 let tree = ReferenceTree::new(root);
268
269 assert_eq!(tree.node_count(), 4);
270 assert_eq!(tree.max_depth(), 4);
271 assert!(tree.has_results());
272 }
273}