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}