cs/tree/node.rs
1//! # Traits and Derive Macros - Rust Book Chapter 10
2//!
3//! This module demonstrates traits and derive macros from
4//! [The Rust Book Chapter 10](https://doc.rust-lang.org/book/ch10-00-generics.html).
5//!
6//! ## Key Concepts Demonstrated
7//!
8//! 1. **Derive Macros for Common Traits** (Chapter 10.2)
9//! - `#[derive(Debug)]` - Automatic debug formatting
10//! - `#[derive(Clone)]` - Automatic cloning implementation
11//! - `#[derive(PartialEq, Eq)]` - Equality comparisons
12//!
13//! 2. **Trait Bounds and Requirements** (Chapter 10.2)
14//! - Why certain traits require others (e.g., `Eq` requires `PartialEq`)
15//! - How derive macros generate implementations
16//!
17//! 3. **Enums with Derive** (Chapter 6.1 + 10.2)
18//! - Deriving traits for enums
19//! - All variants must support the derived trait
20//!
21//! ## Learning Notes
22//!
23//! **What are derive macros?**
24//! - Compiler-generated trait implementations
25//! - Reduce boilerplate code
26//! - Only work for types where all fields implement the trait
27//!
28//! **Common derive traits:**
29//! - `Debug` - For `{:?}` formatting and debugging
30//! - `Clone` - For `.clone()` method
31//! - `PartialEq` - For `==` and `!=` operators
32//! - `Eq` - For full equality (requires `PartialEq`)
33//! - `Copy` - For implicit copying (only for stack types)
34
35use std::path::PathBuf;
36
37/// Type of node in the reference tree.
38///
39/// # Rust Book Reference
40///
41/// **Chapter 10.2: Traits - Deriving Traits**
42/// https://doc.rust-lang.org/book/ch10-02-traits.html#deriving-traits
43///
44/// **Chapter 6.1: Defining an Enum**
45/// https://doc.rust-lang.org/book/ch06-01-defining-an-enum.html
46///
47/// # Educational Notes - Derive Macros on Enums
48///
49/// This enum demonstrates multiple derived traits:
50///
51/// ```rust,ignore
52/// #[derive(Debug, Clone, PartialEq, Eq)]
53/// pub enum NodeType { ... }
54/// ```
55///
56/// **What each trait provides:**
57///
58/// 1. **`Debug`** - Enables `println!("{:?}", node_type)`
59/// - Essential for debugging and error messages
60/// - Automatically formats enum variants
61///
62/// 2. **`Clone`** - Enables `.clone()` method
63/// - Creates a deep copy of the enum value
64/// - For enums without data, this is trivial
65///
66/// 3. **`PartialEq`** - Enables `==` and `!=` operators
67/// - Compares enum variants for equality
68/// - `Root == Root` is true, `Root == Translation` is false
69///
70/// 4. **`Eq`** - Marker trait for full equality
71/// - Requires `PartialEq` first
72/// - Indicates equality is reflexive, symmetric, and transitive
73/// - Needed for using types as HashMap keys
74///
75/// **Why derive instead of manual implementation?**
76/// - Less code to write and maintain
77/// - Compiler-generated code is correct and efficient
78/// - Consistent behavior across the codebase
79#[derive(Debug, Clone, PartialEq, Eq)]
80pub enum NodeType {
81 /// Root node containing the search text
82 Root,
83 /// Translation file entry
84 Translation,
85 /// Full key path (e.g., "invoice.labels.add_new")
86 KeyPath,
87 /// Code reference where the key is used
88 CodeRef,
89}
90
91/// Location information for a node.
92///
93/// # Rust Book Reference
94///
95/// **Chapter 10.2: Traits - Deriving Traits**
96/// https://doc.rust-lang.org/book/ch10-02-traits.html#deriving-traits
97///
98/// # Educational Notes - Derive on Structs
99///
100/// This struct demonstrates deriving traits on a struct with multiple fields:
101///
102/// ```rust,ignore
103/// #[derive(Debug, Clone, PartialEq, Eq)]
104/// pub struct Location {
105/// pub file: PathBuf,
106/// pub line: usize,
107/// }
108/// ```
109///
110/// **How derive works for structs:**
111/// - Compiler checks that ALL fields implement the trait
112/// - `PathBuf` implements `Debug, Clone, PartialEq, Eq` ✓
113/// - `usize` implements `Debug, Clone, PartialEq, Eq` ✓
114/// - Therefore, `Location` can derive these traits
115///
116/// **Generated `PartialEq` implementation:**
117/// ```rust,ignore
118/// impl PartialEq for Location {
119/// fn eq(&self, other: &Self) -> bool {
120/// self.file == other.file && self.line == other.line
121/// }
122/// }
123/// ```
124///
125/// **Why this matters:**
126/// - Locations can be compared: `loc1 == loc2`
127/// - Locations can be cloned: `loc.clone()`
128/// - Locations can be debugged: `println!("{:?}", loc)`
129#[derive(Debug, Clone, PartialEq, Eq)]
130pub struct Location {
131 pub file: PathBuf,
132 pub line: usize,
133}
134
135impl Location {
136 pub fn new(file: PathBuf, line: usize) -> Self {
137 Self { file, line }
138 }
139}
140
141/// A node in the reference tree.
142///
143/// # Rust Book Reference
144///
145/// **Chapter 10.2: Traits**
146/// https://doc.rust-lang.org/book/ch10-02-traits.html
147///
148/// # Educational Notes - Selective Trait Derivation
149///
150/// Notice this struct only derives `Debug` and `Clone`, not `PartialEq`:
151///
152/// ```rust,ignore
153/// #[derive(Debug, Clone)] // No PartialEq!
154/// pub struct TreeNode { ... }
155/// ```
156///
157/// **Why not derive `PartialEq`?**
158/// - `TreeNode` contains `Vec<TreeNode>` (recursive structure)
159/// - Comparing entire trees could be expensive
160/// - We don't need to compare trees for equality in this application
161/// - Omitting `PartialEq` prevents accidental expensive comparisons
162///
163/// **Why derive `Clone`?**
164/// - We need to clone nodes when building trees
165/// - `Clone` is explicit (`.clone()`) so we know when it happens
166/// - All fields implement `Clone`:
167/// - `NodeType: Clone` ✓
168/// - `String: Clone` ✓
169/// - `Option<Location>: Clone` ✓ (Location implements Clone)
170/// - `Vec<TreeNode>: Clone` ✓ (recursive, but works)
171/// - `Option<String>: Clone` ✓
172///
173/// **Design principle:**
174/// Only derive traits you actually need. This prevents:
175/// - Accidental expensive operations
176/// - Unnecessary trait bound requirements
177/// - Compilation errors when adding non-Clone fields later
178#[derive(Debug, Clone)]
179pub struct TreeNode {
180 pub node_type: NodeType,
181 pub content: String,
182 pub location: Option<Location>,
183 pub children: Vec<TreeNode>,
184 pub metadata: Option<String>,
185}
186
187impl TreeNode {
188 /// Create a new TreeNode
189 pub fn new(node_type: NodeType, content: String) -> Self {
190 Self {
191 node_type,
192 content,
193 location: None,
194 children: Vec::new(),
195 metadata: None,
196 }
197 }
198
199 /// Create a TreeNode with a location
200 pub fn with_location(node_type: NodeType, content: String, location: Location) -> Self {
201 Self {
202 node_type,
203 content,
204 location: Some(location),
205 children: Vec::new(),
206 metadata: None,
207 }
208 }
209
210 /// Add a child node
211 pub fn add_child(&mut self, child: TreeNode) {
212 self.children.push(child);
213 }
214
215 /// Check if this node has children
216 pub fn has_children(&self) -> bool {
217 !self.children.is_empty()
218 }
219
220 /// Get the number of children
221 pub fn child_count(&self) -> usize {
222 self.children.len()
223 }
224
225 /// Get the total number of nodes in the tree (including this node)
226 pub fn node_count(&self) -> usize {
227 1 + self.children.iter().map(|c| c.node_count()).sum::<usize>()
228 }
229
230 /// Get the maximum depth of the tree
231 pub fn max_depth(&self) -> usize {
232 if self.children.is_empty() {
233 1
234 } else {
235 1 + self
236 .children
237 .iter()
238 .map(|c| c.max_depth())
239 .max()
240 .unwrap_or(0)
241 }
242 }
243}
244
245/// A reference tree representing the search results
246#[derive(Debug)]
247pub struct ReferenceTree {
248 pub root: TreeNode,
249}
250
251impl ReferenceTree {
252 /// Create a new ReferenceTree with a root node
253 pub fn new(root: TreeNode) -> Self {
254 Self { root }
255 }
256
257 /// Create a ReferenceTree with a root containing the search text
258 pub fn with_search_text(search_text: String) -> Self {
259 Self {
260 root: TreeNode::new(NodeType::Root, search_text),
261 }
262 }
263
264 /// Get the total number of nodes in the tree
265 pub fn node_count(&self) -> usize {
266 self.root.node_count()
267 }
268
269 /// Get the maximum depth of the tree
270 pub fn max_depth(&self) -> usize {
271 self.root.max_depth()
272 }
273
274 /// Check if the tree has any results (children of root)
275 pub fn has_results(&self) -> bool {
276 self.root.has_children()
277 }
278}
279
280#[cfg(test)]
281mod tests {
282 use super::*;
283
284 #[test]
285 fn test_create_tree_node() {
286 let node = TreeNode::new(NodeType::Root, "add new".to_string());
287 assert_eq!(node.content, "add new");
288 assert_eq!(node.node_type, NodeType::Root);
289 assert!(node.location.is_none());
290 assert!(node.children.is_empty());
291 }
292
293 #[test]
294 fn test_create_tree_node_with_location() {
295 let location = Location::new(PathBuf::from("test.yml"), 10);
296 let node = TreeNode::with_location(
297 NodeType::Translation,
298 "add_new: 'add new'".to_string(),
299 location.clone(),
300 );
301
302 assert_eq!(node.content, "add_new: 'add new'");
303 assert_eq!(node.node_type, NodeType::Translation);
304 assert!(node.location.is_some());
305 assert_eq!(node.location.unwrap().line, 10);
306 }
307
308 #[test]
309 fn test_add_child() {
310 let mut parent = TreeNode::new(NodeType::Root, "root".to_string());
311 let child = TreeNode::new(NodeType::Translation, "child".to_string());
312
313 assert_eq!(parent.child_count(), 0);
314 parent.add_child(child);
315 assert_eq!(parent.child_count(), 1);
316 assert!(parent.has_children());
317 }
318
319 #[test]
320 fn test_node_count() {
321 let mut root = TreeNode::new(NodeType::Root, "root".to_string());
322 let mut child1 = TreeNode::new(NodeType::Translation, "child1".to_string());
323 let child2 = TreeNode::new(NodeType::Translation, "child2".to_string());
324 let grandchild = TreeNode::new(NodeType::KeyPath, "grandchild".to_string());
325
326 child1.add_child(grandchild);
327 root.add_child(child1);
328 root.add_child(child2);
329
330 // root + child1 + child2 + grandchild = 4
331 assert_eq!(root.node_count(), 4);
332 }
333
334 #[test]
335 fn test_max_depth() {
336 let mut root = TreeNode::new(NodeType::Root, "root".to_string());
337 let mut child = TreeNode::new(NodeType::Translation, "child".to_string());
338 let grandchild = TreeNode::new(NodeType::KeyPath, "grandchild".to_string());
339
340 // Depth 1: just root
341 assert_eq!(root.max_depth(), 1);
342
343 // Depth 2: root -> child
344 root.add_child(child.clone());
345 assert_eq!(root.max_depth(), 2);
346
347 // Depth 3: root -> child -> grandchild
348 child.add_child(grandchild);
349 root.children[0] = child;
350 assert_eq!(root.max_depth(), 3);
351 }
352
353 #[test]
354 fn test_reference_tree_creation() {
355 let tree = ReferenceTree::with_search_text("add new".to_string());
356 assert_eq!(tree.root.content, "add new");
357 assert_eq!(tree.root.node_type, NodeType::Root);
358 assert!(!tree.has_results());
359 }
360
361 #[test]
362 fn test_reference_tree_with_results() {
363 let mut root = TreeNode::new(NodeType::Root, "add new".to_string());
364 let child = TreeNode::new(NodeType::Translation, "translation".to_string());
365 root.add_child(child);
366
367 let tree = ReferenceTree::new(root);
368 assert!(tree.has_results());
369 assert_eq!(tree.node_count(), 2);
370 assert_eq!(tree.max_depth(), 2);
371 }
372
373 #[test]
374 fn test_location_creation() {
375 let location = Location::new(PathBuf::from("test.yml"), 42);
376 assert_eq!(location.file, PathBuf::from("test.yml"));
377 assert_eq!(location.line, 42);
378 }
379
380 #[test]
381 fn test_node_types() {
382 let root = NodeType::Root;
383 let translation = NodeType::Translation;
384 let key_path = NodeType::KeyPath;
385 let code_ref = NodeType::CodeRef;
386
387 assert_eq!(root, NodeType::Root);
388 assert_eq!(translation, NodeType::Translation);
389 assert_eq!(key_path, NodeType::KeyPath);
390 assert_eq!(code_ref, NodeType::CodeRef);
391 }
392
393 #[test]
394 fn test_complex_tree_structure() {
395 // Build a tree: root -> translation -> key_path -> code_ref
396 let mut root = TreeNode::new(NodeType::Root, "add new".to_string());
397
398 let mut translation = TreeNode::with_location(
399 NodeType::Translation,
400 "add_new: 'add new'".to_string(),
401 Location::new(PathBuf::from("en.yml"), 4),
402 );
403
404 let mut key_path = TreeNode::new(NodeType::KeyPath, "invoice.labels.add_new".to_string());
405
406 let code_ref = TreeNode::with_location(
407 NodeType::CodeRef,
408 "I18n.t('invoice.labels.add_new')".to_string(),
409 Location::new(PathBuf::from("invoices.ts"), 14),
410 );
411
412 key_path.add_child(code_ref);
413 translation.add_child(key_path);
414 root.add_child(translation);
415
416 let tree = ReferenceTree::new(root);
417
418 assert_eq!(tree.node_count(), 4);
419 assert_eq!(tree.max_depth(), 4);
420 assert!(tree.has_results());
421 }
422}