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