dsa_rust/hierarchies/safe_linked_gentree.rs
1/*! A safe, linked, n-ary tree implementation
2
3# About
4Following classical DSA curricula, this implementation relies on pointers for the structure's composition and navigation. This module explores the use of reference counting and interior mutability through the [Rc] and [RefCell] types (respectively) for a safe, positional implementation that avoids dangling pointers and reference cycles for proper [Drop] semantics.
5
6Reference counting provides a synchronous, deterministic form of memory management that acts like a garbage collector and prevents dangling pointers by automatically managing lifetimes. The structure is able to keep objects alive until their reference count hits zero, potentially even after they've gone out of their original scope. To avoid memory leaks caused by reference cycles, tree nodes use strong `Rc` pointers for children and [Weak] pointers for parent links. This ensures the tree can be correctly dropped recursively from the top down.
7
8Using smart pointers to manage reference counting and interior mutability to skirt multiple mutable references is an elegant solution to the linked desgin, but its still a bit painful, and potentially overkill for many applications. The good news is that there are much easier ways to accomplish similar goals. To wit, this library also includes a `Vec`-backed tree structure with a similar API. For more polished levels of functionality with the same arena-style backing structure concepts see [id_tree](https://docs.rs/id_tree/latest/id_tree/). It is worth noting that `id_tree` uses a hash map to store node IDs, so it may not be as performanat as either a pointer-backed or simple indexed tree structure for smaller, short-lived tree structures.
9
10# Design
11The base [GenTree] structure is sparse and only contains basic operations for constructors and metadata retrieval. Most of the magic happens in the [CursorMut] struct. Both structs rely on a [Position] struct which provides a safe handle to all the reference-counted pointers required to make tree go brrr.
12
13# Example
14This section presents an algorithm that builds a tree from a `Vec` of custom `Heading` objects that contain a level and a heading value. Assume the inputs to the algorithm start at level 1 with the first (and lowest) level in the `Vec<Heading>` list being 2. The result is a single, empty root node represented by `[]`.
15```text
16 []
17 │
18 ├── Landlocked
19 │ ├── Switzerland
20 │ │ └── Geneva
21 │ │ └── Old Town
22 │ │ └── Cathédrale Saint-Pierre
23 │ └── Bolivia
24 │ └── []
25 │ └── []
26 │ ├── Puerta del Sol
27 │ └── Puerta de la Luna
28 └── Islands
29 ├── Marine
30 │ └── Australia
31 └── Fresh Water
32```
33```rust
34 use dsa_rust::hierarchies::safe_linked_gentree::GenTree;
35
36 struct Heading {
37 level: usize,
38 title: String,
39 }
40 impl Heading {
41 fn new(title: String, level: usize) -> Heading {
42 Heading { level, title }
43 }
44 }
45
46 pub fn construct(mut cur_level: usize, data: Vec<Heading>) -> GenTree<Heading> {
47 // Instantiates a Tree with a generic root and traversal positioning
48 let mut tree: GenTree<Heading> = GenTree::<Heading>::new();
49 let mut cursor = tree.cursor_mut(); // Sets cursor to tree.root
50
51 // Constructs tree from Vec<T>
52 for heading in data {
53 let data_level = heading.level;
54
55 // Case 1: Adds a child to the current parent and sets level cursor
56 if data_level == cur_level + 1 {
57 cursor.add_child(heading);
58 cur_level += 1;
59 }
60 // Case 2: Adds a child with multi-generational skips
61 else if data_level > cur_level {
62 let diff = data_level - cur_level;
63 for _ in 1..diff {
64 let empty = Heading::new("[]".to_string(), 0);
65 cursor.add_child(empty);
66 cur_level += 1;
67 }
68 cursor.add_child(heading);
69 cur_level += 1;
70 }
71 // Case 3: Adds sibling to current parent
72 else if data_level == cur_level {
73 cursor.ascend().ok();
74 cursor.add_child(heading);
75 }
76 // Case 4: Adds a child to the appropriate ancestor,
77 // ensuring proper generational skips
78 else {
79 let diff = cur_level - data_level;
80 for _ in 0..=diff {
81 cursor.ascend().ok();
82 cur_level -= 1;
83 }
84 cursor.add_child(heading);
85 cur_level += 1;
86 }
87 }
88 tree
89 }
90
91```
92
93*/
94
95use std::cell::{Ref, RefCell};
96use std::rc::{Rc, Weak};
97//use std::marker::PhantomData;
98
99/// The `Position` struct provides a safe, lightweight handle to `Node` data.
100/// All meaningful accessors and mutators appear on the [CursorMut] struct.
101// Position only contains private members, but must be public due to its
102// presence as a CursorMut return type.
103pub struct Position<T> {
104 ptr: Option<Rc<RefCell<Node<T>>>>,
105}
106impl<T> Position<T> {
107 /// Creates a handle to Node and returns it as a Position.
108 fn new(ptr: Node<T>) -> Self {
109 Position {
110 ptr: Some(Rc::new(RefCell::new(ptr))),
111 }
112 }
113
114 /// Returns an reference to the data at the Position, if Some.
115 fn get_data(&self) -> Option<Ref<'_, T>> {
116 let node_ref: Ref<Node<T>> = self.ptr.as_ref()?.borrow();
117 Ref::filter_map(node_ref, |node| node.data.as_ref()).ok()
118 //if let Some(val) = self.ptr.as_ref() {
119 // Some((*(*val)).borrow())
120 //} else { None }
121 }
122
123 /// Returns the Node from a Position, if Some.
124 //fn get_node(&self) -> Ref<Node<T>> {
125 // self.ptr.as_ref().unwrap().borrow()
126 //}
127 fn get_node(&self) -> Option<Ref<'_, Node<T>>> {
128 self.ptr.as_ref().map(|rc| rc.borrow())
129 }
130
131 /// Returns the Position for the current Position's parent, if Some.
132 //fn get_parent_pos(&self) -> Option<Position<T>> {
133 // if let Some(parent) = self.ptr.as_ref().unwrap().borrow().parent.clone() {
134 // Some(parent)
135 // } else { None }
136 //}
137 fn get_parent_pos(&self) -> Option<Position<T>> {
138 if let Some(weak_parent) = &self.ptr.as_ref()?.borrow().parent {
139 weak_parent.upgrade().map(|rc| Position { ptr: Some(rc) })
140 } else {
141 None
142 }
143 }
144}
145// "Shallow" clone only clones/increases the Rc, not the whole Node
146impl<T> Clone for Position<T> {
147 fn clone(&self) -> Self {
148 Position {
149 ptr: self.ptr.clone(),
150 }
151 }
152}
153impl<T> PartialEq for Position<T> {
154 fn eq(&self, other: &Self) -> bool {
155 match (&self.ptr, &other.ptr) {
156 (Some(a), Some(b)) => Rc::ptr_eq(a, b),
157 (None, None) => true,
158 _ => false,
159 }
160 }
161}
162impl<T> std::fmt::Debug for Position<T> {
163 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
164 match &self.ptr {
165 Some(rc) => write!(f, "Position({:p})", Rc::as_ptr(rc)),
166 None => write!(f, "Position(None)"),
167 }
168 }
169}
170
171/// Internal-only struct that represents the heart of the general tree. The `Node`
172/// struct contains strong pointers to children, but weak pointers to parent nodes
173/// for proper drop semantics to avoid reference cycles.
174struct Node<T> {
175 parent: Option<Weak<RefCell<Node<T>>>>,
176 children: Vec<Position<T>>,
177 data: Option<T>,
178}
179impl<T> Node<T> {
180 /// Builds a new Node and returns its position.
181 fn root(data: Option<T>) -> Node<T> {
182 Node {
183 parent: None,
184 children: Vec::new(),
185 data,
186 }
187 }
188
189 /// Creates a new `Node` with given data for the given `Position`.
190 fn new(parent: &Position<T>, data: T) -> Node<T> {
191 Node {
192 //parent: Some(parent.clone()),
193 parent: Some(Rc::downgrade(parent.ptr.as_ref().unwrap())),
194 children: Vec::new(),
195 data: Some(data),
196 }
197 }
198}
199
200/// The `GenTree` struct represents a positional, linked-based general
201/// tree structure that contains a pointer to the root node and the structure's size.
202/// The genericity of the struct means you'll have to explicitly type the
203/// tree at instantiation.
204///
205/// Most of the major accessors and mutators appear on the [CursorMut] struct.
206///
207/// Example:
208/// ```example
209/// // Creates a tree over Heading objects
210/// let mut tree: GenTree<Heading> = GenTree::<Heading>::new();
211///
212/// // Creates a CursorMut to navigate/mutate the tree,
213/// // starting at the root node
214/// let mut cursor = tree.cursor_mut();
215/// ```
216#[derive(Debug)]
217pub struct GenTree<T> {
218 root: Position<T>,
219 size: usize,
220}
221impl<T> Default for GenTree<T> {
222 fn default() -> Self {
223 Self::new()
224 }
225}
226impl<T> GenTree<T> {
227 /// Instantiates a new `GenTree`.
228 pub fn new() -> GenTree<T> {
229 let root: Position<T> = Position::new(Node::root(None));
230 GenTree { root, size: 0 }
231 }
232
233 /// Returns the `Position` of the tree's root.
234 pub fn root(&self) -> Position<T> {
235 self.root.clone()
236 }
237
238 /// Creates a `CursorMut` starting at the tree's root.
239 pub fn cursor_mut(&mut self) -> CursorMut<'_, T> {
240 CursorMut {
241 node: self.root.clone(),
242 tree: self,
243 }
244 }
245
246 /// Creates a `CursorMut` from a given `Position`.
247 pub fn cursor_from(&mut self, position: Position<T>) -> CursorMut<'_, T> {
248 CursorMut {
249 node: position,
250 tree: self,
251 }
252 }
253}
254
255/** A cursor over mutable `Node` data with safe, reference-counted `Position` handles.
256
257This struct represents the majority of major operations for the [GenTree] structure.
258All operations run in `O(1)` time unless otherwise noted. */
259pub struct CursorMut<'a, T> {
260 node: Position<T>,
261 tree: &'a mut GenTree<T>,
262}
263impl<'a, T> CursorMut<'a, T> {
264 // METADATA
265 ///////////
266
267 /** Returns `true` if the `Node` under the curosr is the tree's root */
268 pub fn is_root(&self) -> bool {
269 self.node.clone() == self.tree.root()
270 }
271
272 /** Returns `true` if the `Node` under the curosr has data */
273 pub fn is_some(&self) -> bool {
274 let val = self.node.get_data();
275 val.is_some()
276 }
277
278 /** Returns `true` if the `Node` under the cursor is empty */
279 pub fn is_none(&self) -> bool {
280 let val = self.node.get_data();
281 val.is_none()
282 }
283
284 /** Returns the number of children for the `Node` under the cursor as usize */
285 pub fn num_children(&self) -> usize {
286 if let Some(val) = self.node.ptr.clone() {
287 (*val).borrow().children.len()
288 } else {
289 0
290 }
291 }
292
293 /** Returns the depth of the cursor from the tree's root */
294 //pub fn depth(&mut self) -> usize {
295 // let mut depth = 0;
296 // let current = self.current().clone();
297 // while !self.is_root() {
298 // self.ascend().ok();
299 // depth += 1;
300 // }
301 // self.jump(¤t);
302 // depth
303 //}
304 pub fn depth(&self) -> usize {
305 let mut depth = 0;
306 let mut current_ptr = self.node.clone().ptr;
307 //while let Some(pos) = current.ptr {
308 while let Some(pos) = current_ptr {
309 let node_ref = pos.borrow();
310 if let Some(parent_weak) = &node_ref.parent {
311 //current = Position { ptr: parent_weak.upgrade() };
312 current_ptr = parent_weak.upgrade();
313 depth += 1;
314 } else {
315 break;
316 }
317 }
318 depth
319 }
320
321 /** Returns the height of the tallest sub-tree for the current position */
322 //pub fn height(&self) -> usize {
323 // let current = self.current();
324 // self.height_rec(current.clone())
325 //}
326 ///** The recursive guts of the height function */
327 //#[allow(clippy::only_used_in_recursion)]
328 //fn height_rec(&self, node: Position<T>) -> usize {
329 // let mut h = 0;
330 // if let Some(n) = node.ptr.clone() {
331 // for e in &(*n).borrow().children {
332 // h = std::cmp::max(h, self.height_rec(e.clone()))
333 // }
334 // }
335 // h + 1
336 //}
337 pub fn height(&self) -> usize {
338 let current = self.current();
339 fn height_rec<T>(node: &Position<T>) -> usize {
340 let mut h = 0;
341 if let Some(n) = node.ptr.clone() {
342 for e in &(*n).borrow().children {
343 h = std::cmp::max(h, height_rec(&e.clone()))
344 }
345 }
346 h + 1
347 }
348 height_rec(current)
349 }
350
351 // ACCESSORS AND MUTATORS
352 /////////////////////////
353
354 /** Returns an _immutable_ reference to the data under the cursor, if `Some` */
355 pub fn get_data(&self) -> Option<Ref<'_, T>> {
356 let node_ref: Ref<Node<T>> = self.node.get_node()?;
357 Ref::filter_map(node_ref, |node| node.data.as_ref()).ok()
358 }
359
360 /** Returns an _immutable_ reference to the data for a supplied `Position` */
361 pub fn get_for_pos(&'a self, pos: &'a Position<T>) -> Option<Ref<'a, T>> {
362 let node_ref: Ref<Node<T>> = pos.get_node()?;
363 Ref::filter_map(node_ref, |node| node.data.as_ref()).ok()
364 }
365
366 // /** Overwrites the data for the current Node without affecting its position,
367 // returns the old data, if Some */
368 //pub fn set(&mut self, data: T) -> Option<T> {
369 // if let Ok(n) = self.node.as_ptr() {
370 // unsafe {
371 // let old = (*n).data.take();
372 // (*n).data = Some(data);
373 // return old;
374 // }
375 // } else {
376 // None
377 // }
378 //}
379
380 /** Adds a new child `Node` under the current cursor and advances the cursor
381 to the new child */
382 pub fn add_child(&mut self, data: T) {
383 let parent = self.node.clone();
384
385 // Create the new child node and give it a Position
386 let new_node = Node::new(&parent, data);
387 let new_pos = Position::new(new_node);
388
389 // Add the new child to the parent's child list
390 let kids = parent.ptr.unwrap();
391 (*kids).borrow_mut().children.push(new_pos.clone());
392
393 // Mutates self to be the Position of the new node
394 self.node = new_pos;
395
396 // Increment the size of the tree
397 self.tree.size += 1;
398 }
399
400 /** Returns a list of owned descendant (child) `Position`s for the `Node`
401 under the cursor in `O(c)` time where `c` is the number of children; The
402 clone used here is a cheap pointer copy, not an underlying data copy */
403 //pub fn children(&self) -> Vec<Position<T>> {
404 // self.node
405 // .get_node()
406 // .unwrap()
407 // .children
408 // .iter()
409 // .cloned()
410 // .collect::<Vec<_>>()
411 //}
412 // Allocates a new Vec and clones Positions in O(n) time
413 pub fn children(&self) -> Vec<Position<T>> {
414 self.node
415 .get_node()
416 //.map(|node| node.children.iter().cloned().collect())
417 .map(|node| node.children.to_vec())
418 .unwrap_or_default()
419 }
420
421 /// Warning: Broken! Does not handle root deletion properly.
422 ///
423 /// Removes the node at the current cursor position and returns its data,
424 /// if Some. Operation executes in `O(c)` time where `c` is the number of
425 /// children for the given node; Adds all children to the parent
426 /// (if `Some`), and returns the deleted `Node`; If the cursor is at the tree's
427 /// root, this just deletes the `Node`'s data, leaving `None`; Moves the cursor
428 /// to the parent, if `Some` */
429 /// TODO: Unsound; need to remove reference from parent too
430 //pub fn delete(&mut self) -> Option<T> {
431 // let self_pos = self.node.clone();
432 // let self_rc = self_pos.ptr.clone()?;
433 //
434 // // Check and get parent
435 // let parent_pos = self_rc.borrow().parent.as_ref()?.upgrade()?;
436 // let parent_pos = Position {
437 // ptr: Some(parent_pos),
438 // };
439 // let parent_rc = parent_pos.ptr.clone().unwrap();
440 //
441 // // 1. Remove self from parent.children
442 // {
443 // let mut parent_node = parent_rc.borrow_mut();
444 // if let Some(index) = parent_node.children.iter().position(|c| *c == self.node) {
445 // parent_node.children.remove(index);
446 // }
447 // }
448 //
449 // // 2. Take self's children (detach them)
450 // let mut self_children = {
451 // let mut self_node = self_rc.borrow_mut();
452 // std::mem::take(&mut self_node.children)
453 // };
454 //
455 // // 3. Reparent each child and move them to parent's children
456 // {
457 // let mut parent_node = parent_rc.borrow_mut();
458 // for child in &mut self_children {
459 // if let Some(child_rc) = child.ptr.clone() {
460 // child_rc.borrow_mut().parent = Some(Rc::downgrade(&parent_rc));
461 // }
462 // parent_node.children.push(child.clone());
463 // }
464 // }
465 //
466 // // 4. Move cursor to parent
467 // self.jump(&parent_pos);
468 //
469 // // 5. Take and return data from the deleted node
470 // let mut self_node = self_rc.borrow_mut();
471 // self_node.data.take()
472 //}
473 pub fn delete(&mut self) -> Option<T> {
474 let self_pos = self.node.clone();
475 let self_rc = self_pos.ptr.clone()?;
476
477 // Check if we have a parent. If not, we are deleting the root!
478 let parent_maybe = self_rc
479 .borrow()
480 .parent
481 .as_ref()
482 .and_then(|weak| weak.upgrade());
483
484 let old_data = {
485 let mut self_node = self_rc.borrow_mut();
486 self_node.data.take()
487 };
488
489 if let Some(parent_rc) = parent_maybe {
490 let parent_pos = Position {
491 ptr: Some(parent_rc.clone()),
492 };
493
494 // 1. Remove self from parent.children by checking pointer address identity
495 {
496 let mut parent_node = parent_rc.borrow_mut();
497
498 // Extract our underlying Rc pointer to compare against
499 if let Some(self_inner_rc) = &self.node.ptr {
500 if let Some(index) = parent_node.children.iter().position(|c| {
501 if let Some(child_inner_rc) = &c.ptr {
502 // Check if they point to the exact same memory allocation
503 Rc::ptr_eq(child_inner_rc, self_inner_rc)
504 } else {
505 false
506 }
507 }) {
508 parent_node.children.remove(index);
509 }
510 }
511 }
512
513 // 2. Take self's children (detach them)
514 let mut self_children = {
515 let mut self_node = self_rc.borrow_mut();
516 std::mem::take(&mut self_node.children)
517 };
518
519 // 3. Reparent each child and move them to parent's children
520 {
521 let mut parent_node = parent_rc.borrow_mut();
522 for child in &mut self_children {
523 if let Some(child_rc) = child.ptr.clone() {
524 child_rc.borrow_mut().parent = Some(Rc::downgrade(&parent_rc));
525 }
526 parent_node.children.push(child.clone());
527 }
528 }
529
530 // 4. Move cursor to parent
531 self.jump(&parent_pos);
532 } else {
533 // Root deletion handling: If you delete the root, you decide where the cursor goes.
534 // For example, making the tree entirely None, or making a child the new root.
535 self.node = Position { ptr: None };
536 }
537
538 // 5. Explicitly decrement the size track!
539 self.tree.size -= 1;
540
541 old_data
542 }
543
544 // NAVIGATION
545 /////////////
546
547 /** Returns a reference to the current `Position` */
548 pub fn current(&self) -> &Position<T> {
549 &self.node
550 }
551
552 /** Jump the cursor to the given `Position` */
553 pub fn jump(&mut self, new: &Position<T>) {
554 self.node = (*new).clone();
555 }
556
557 /** Moves the cursor up a generation, if `Some`; Trying to ascend past the root results in an error */
558 pub fn ascend(&mut self) -> Result<(), String> {
559 if let Some(parent) = self.node.get_parent_pos() {
560 self.node = parent;
561 Ok(())
562 } else {
563 Err("Error: Cannot ascend past root".to_string())
564 }
565 }
566}
567
568#[cfg(test)]
569mod tests {
570
571 // Both basic and dangle tests use the tree builder
572 use super::{GenTree, Position};
573 use crate::hierarchies::safe_linked_gentree_builder::{construct, Heading};
574
575 #[test]
576 /** Creates this tree to test properties
577 []
578 ├── Landlocked
579 │ ├── Switzerland
580 │ │ └── Geneva
581 │ │ └── Old Town
582 │ │ └── Cathédrale Saint-Pierre
583 │ └── Bolivia
584 │ └── []
585 │ └── []
586 │ ├── Puerta del Sol
587 │ └── Puerta de la Luna
588 └── Islands
589 ├── Marine
590 │ └── Australia
591 └── Fresh Water
592 */
593 fn basic() {
594 let tree_vec = vec![
595 Heading {
596 level: 2,
597 title: "Landlocked".to_string(),
598 },
599 Heading {
600 level: 3,
601 title: "Switzerland".to_string(),
602 },
603 Heading {
604 level: 4,
605 title: "Geneva".to_string(),
606 },
607 Heading {
608 level: 5,
609 title: "Old Town".to_string(),
610 },
611 Heading {
612 level: 6,
613 title: "Cathédrale Saint-Pierre".to_string(),
614 },
615 Heading {
616 level: 3,
617 title: "Bolivia".to_string(),
618 },
619 Heading {
620 level: 6,
621 title: "Puerta del Sol".to_string(),
622 },
623 Heading {
624 level: 6,
625 title: "Puerta de la Luna".to_string(),
626 },
627 Heading {
628 level: 2,
629 title: "Islands".to_string(),
630 },
631 Heading {
632 level: 3,
633 title: "Marine".to_string(),
634 },
635 Heading {
636 level: 4,
637 title: "Australia".to_string(),
638 },
639 Heading {
640 level: 3,
641 title: "Fresh Water".to_string(),
642 },
643 ];
644
645 // Constructs tree ignoring the first heading
646 let mut tree: GenTree<Heading> = construct(1, tree_vec);
647
648 assert!(tree.root.get_parent_pos().is_none());
649 assert!(tree.root().ptr.is_some());
650 let p = tree.root();
651 let _ = p.get_data();
652
653 // Tests root() -> Position<T>
654 // By identity (using custom PartialEq ipml)
655 assert!(tree.cursor_mut().node == tree.root);
656 // By assert_eq!'s default Debug route
657 let cursor = tree.cursor_mut();
658 assert_eq!(cursor.node, tree.root());
659
660 let mut cursor = tree.cursor_mut();
661 // Tests that root is empty with is_some() and is_none()
662 assert!(!cursor.is_some());
663 assert!(cursor.is_none());
664 // tests height and depth
665 assert_eq!(cursor.depth(), 0);
666 assert_eq!(cursor.height(), 6);
667
668 // Tests num_children()
669 assert_eq!(cursor.num_children(), 2); // Root has [Landlocked, Islands]
670
671 // Tests children(), jump(), and get_data()
672 let kids = cursor.children();
673 let mut kids_iter = kids.iter();
674
675 // Moves to first child "Landlocked"
676 cursor.jump(kids_iter.next().unwrap());
677 {
678 let data = cursor.get_data().unwrap();
679 assert_eq!(*data.title, "Landlocked".to_string());
680 }
681 assert_eq!(cursor.depth(), 1);
682 assert_eq!(cursor.height(), 5);
683
684 // Moves to second child "Islands"
685 cursor.jump(kids_iter.next().unwrap());
686 let curr: Position<Heading> = cursor.current().clone(); // Passes the torch
687 {
688 let data = cursor.get_data().unwrap();
689 assert_eq!(*data.title, "Islands".to_string());
690 }
691 assert_eq!(cursor.depth(), 1);
692 assert_eq!(cursor.height(), 3);
693
694 // Jumps down a generation to [Marine, Fresh Water]
695 cursor.jump(&curr);
696 {
697 let new_kids = cursor.children();
698 let mut kids_iter = new_kids.iter();
699 cursor.jump(kids_iter.next().unwrap()); // Moves to first child
700 let data = cursor.get_data().unwrap();
701 assert_eq!(*data.title, "Marine".to_string());
702 }
703 // tests height and depth
704 assert_eq!(cursor.depth(), 2);
705 assert_eq!(cursor.height(), 2);
706
707 // Jumps down a generation, for fun
708 let new_kids = cursor.children(); // Gets cursor's chidlren
709 let mut kids_iter = new_kids.iter(); // Creates an iterator
710 cursor.jump(kids_iter.next().unwrap()); // Moves to first child
711 {
712 let data = cursor.get_data().unwrap();
713 assert_eq!(*data.title, "Australia".to_string());
714 }
715 assert_eq!(cursor.depth(), 3);
716 assert_eq!(cursor.height(), 1);
717
718 // Tests ascend()
719 assert!(cursor.ascend().is_ok()); // Marine
720 assert!(cursor.ascend().is_ok()); // Islands
721 {
722 let data = cursor.get_data().unwrap();
723 assert_eq!(*data.title, "Islands".to_string());
724 }
725 assert!(cursor.ascend().is_ok()); // []
726 assert!(cursor.ascend().is_err()); // Cannot ascend() past root
727 assert!(cursor.is_root()); // Double checks, just in case
728 assert_eq!(cursor.depth(), 0);
729 assert_eq!(cursor.height(), 6);
730
731 // Descends to Islands to test delete()
732 let kids = cursor.children(); // Gets cursor's chidlren
733 let mut kids_iter = kids.iter(); // Creates an iterator
734 cursor.jump(kids_iter.next().unwrap()); // Moves to Landlocked
735 cursor.jump(kids_iter.next().unwrap()); // Moves to Islands
736 {
737 let data = cursor.get_data().unwrap();
738 assert_eq!(*data.title, "Islands".to_string());
739 }
740
741 // Tests delete()
742 // Creates placeholder Heading
743 let mut deleted = Heading {
744 title: String::new(),
745 level: 0,
746 };
747 // Iterates through the child position's under the cursor
748 // looking for a matching Heading; Once found, jumps to that position,
749 // and deletes the Heading; The delete() operation automatically jumps
750 // the cursor to the parent of the deleted position
751 for position in cursor.children().iter() {
752 if position.get_data().unwrap().title == "Marine" {
753 cursor.jump(position);
754 deleted = cursor.delete().unwrap();
755 }
756 }
757 // Tests that the correct Heading was deleted
758 assert_eq!(deleted.level, 3);
759 assert_eq!(deleted.title, "Marine".to_string());
760
761 // Tests that the cursor got bumped up to Islands
762 let data = cursor.get_data().unwrap();
763 assert_eq!(data.title, "Islands".to_string());
764
765 // Tests that the Islands node has the correct children
766 let mut kids = Vec::new();
767 assert_eq!(cursor.children().len(), 2);
768 for child in cursor.children().iter() {
769 let title = child.get_data().unwrap().title.clone();
770 kids.push(title)
771 }
772 assert_eq!(kids, ["Fresh Water".to_string(), "Australia".to_string()]);
773 }
774
775 #[test]
776 fn dangle() {
777 use super::{GenTree, Position};
778 let one = vec![
779 Heading {
780 level: 1,
781 title: "Landlocked".to_string(),
782 },
783 Heading {
784 level: 2,
785 title: "Switzerland".to_string(),
786 },
787 ];
788 let two = vec![
789 Heading {
790 level: 1,
791 title: "Bolivia".to_string(),
792 },
793 Heading {
794 level: 2,
795 title: "Zimbabwe".to_string(),
796 },
797 ];
798
799 // Creates a tree, Position, and CursorMut
800 let mut outer_tree: GenTree<Heading> = construct(0, one.clone());
801 let mut _pos: Position<Heading> = outer_tree.root();
802 let mut cursor = outer_tree.cursor_mut();
803
804 {
805 let inner_tree: GenTree<Heading> = construct(0, two.clone());
806 _pos = inner_tree.root();
807 cursor.jump(&_pos);
808 }
809
810 // No more UB!!
811 cursor.get_data();
812 _pos.get_data();
813
814 // Creates a tree, Position, and CursorMut
815 let mut outer_tree: GenTree<Heading> = construct(0, one);
816 let mut pos: Position<Heading> = outer_tree.root();
817 let mut cursor = outer_tree.cursor_from(pos);
818
819 {
820 let inner_tree: GenTree<Heading> = construct(0, two);
821 pos = inner_tree.root();
822 cursor.jump(&pos);
823 }
824
825 // No more UB!!
826 cursor.get_data();
827 _pos.get_data();
828 }
829
830 use super::*;
831 use std::cell::RefCell;
832 use std::rc::Rc;
833
834 fn create_initialized_tree<T>() -> GenTree<T> {
835 // Instantiate the inner Node layout directly
836 let root_node = Rc::new(RefCell::new(Node {
837 parent: None,
838 children: Vec::new(),
839 data: None, // Initialized with None, ready to populate
840 }));
841
842 // Build the actual GenTree struct using its true fields
843 GenTree {
844 root: Position {
845 ptr: Some(root_node),
846 },
847 size: 1,
848 }
849 }
850
851 #[test]
852 fn test_rc_isolation_and_no_overlapping_data() {
853 let mut tree = GenTree::new();
854 let mut cursor = tree.cursor_mut();
855
856 // 1. Add a child node (auto-descends into Child A)
857 cursor.add_child("Child A".to_string());
858
859 // Capture the handle to Child A
860 let child_pos = cursor.current().clone();
861
862 // 2. Delete Child A.
863 // This moves the cursor to Root and returns Child A's data as an Option!
864 let deleted_data = cursor.delete();
865 assert_eq!(deleted_data, Some("Child A".to_string()));
866
867 // VERIFICATION: Verify cursor successfully jumped back to the root node
868 assert!(cursor.is_root());
869
870 // 3. THE LIFECYCLE CHECK:
871 // Jump back to the isolated handle. Its internal data should now be None.
872 cursor.jump(&child_pos);
873 assert!(cursor.get_data().is_none());
874 }
875
876 #[test]
877 fn test_parent_child_severance_on_delete() {
878 let mut tree = create_initialized_tree();
879 let mut cursor = tree.cursor_mut();
880
881 // Create: Root -> Parent Node (cursor enters Parent Node)
882 cursor.add_child("Parent Node".to_string());
883 let parent_pos = cursor.current().clone();
884
885 // Create child: Parent Node -> Leaf Node (cursor enters Leaf Node)
886 cursor.add_child("Leaf Node".to_string());
887
888 // Jump back to Parent Node and delete it
889 cursor.jump(&parent_pos);
890 cursor.delete();
891
892 // VERIFICATION: Because the delete method hoists children up,
893 // the Root node should now have exactly 1 child (the promoted Leaf Node!)
894 assert!(cursor.is_root());
895 assert_eq!(cursor.num_children(), 1);
896 }
897
898 #[test]
899 fn test_rc_tree_churn_and_metrics() {
900 let mut tree = create_initialized_tree();
901 let mut cursor = tree.cursor_mut();
902
903 // 1. Build a deep linear spine: Root -> N1 -> N2 -> N3
904 cursor.add_child("N1".to_string());
905 let n1_pos = cursor.current().clone();
906
907 cursor.add_child("N2".to_string());
908 let n2_pos = cursor.current().clone();
909
910 cursor.add_child("N3".to_string());
911
912 // VERIFICATION: Confirming the depth logic measures a full 3 edges from the root!
913 assert_eq!(cursor.depth(), 3);
914
915 // 2. Erase the middle node (N2)
916 cursor.jump(&n2_pos);
917 cursor.delete();
918
919 // 3. Verify metrics adjust automatically based on hoisting logic
920 // N3 should now be a direct child of N1
921 cursor.jump(&n1_pos);
922 assert_eq!(cursor.num_children(), 1);
923 }
924}