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.borrow().parent.as_ref()
479 .and_then(|weak| weak.upgrade());
480
481 let old_data = {
482 let mut self_node = self_rc.borrow_mut();
483 self_node.data.take()
484 };
485
486 if let Some(parent_rc) = parent_maybe {
487 let parent_pos = Position { ptr: Some(parent_rc.clone()) };
488
489 // 1. Remove self from parent.children by checking pointer address identity
490{
491 let mut parent_node = parent_rc.borrow_mut();
492
493 // Extract our underlying Rc pointer to compare against
494 if let Some(self_inner_rc) = &self.node.ptr {
495 if let Some(index) = parent_node.children.iter().position(|c| {
496 if let Some(child_inner_rc) = &c.ptr {
497 // Check if they point to the exact same memory allocation
498 Rc::ptr_eq(child_inner_rc, self_inner_rc)
499 } else {
500 false
501 }
502 }) {
503 parent_node.children.remove(index);
504 }
505 }
506}
507
508 // 2. Take self's children (detach them)
509 let mut self_children = {
510 let mut self_node = self_rc.borrow_mut();
511 std::mem::take(&mut self_node.children)
512 };
513
514 // 3. Reparent each child and move them to parent's children
515 {
516 let mut parent_node = parent_rc.borrow_mut();
517 for child in &mut self_children {
518 if let Some(child_rc) = child.ptr.clone() {
519 child_rc.borrow_mut().parent = Some(Rc::downgrade(&parent_rc));
520 }
521 parent_node.children.push(child.clone());
522 }
523 }
524
525 // 4. Move cursor to parent
526 self.jump(&parent_pos);
527 } else {
528 // Root deletion handling: If you delete the root, you decide where the cursor goes.
529 // For example, making the tree entirely None, or making a child the new root.
530 self.node = Position { ptr: None };
531 }
532
533 // 5. Explicitly decrement the size track!
534 self.tree.size -= 1;
535
536 old_data
537}
538
539 // NAVIGATION
540 /////////////
541
542 /** Returns a reference to the current `Position` */
543 pub fn current(&self) -> &Position<T> {
544 &self.node
545 }
546
547 /** Jump the cursor to the given `Position` */
548 pub fn jump(&mut self, new: &Position<T>) {
549 self.node = (*new).clone();
550 }
551
552 /** Moves the cursor up a generation, if `Some`; Trying to ascend past the root results in an error */
553 pub fn ascend(&mut self) -> Result<(), String> {
554 if let Some(parent) = self.node.get_parent_pos() {
555 self.node = parent;
556 Ok(())
557 } else {
558 Err("Error: Cannot ascend past root".to_string())
559 }
560 }
561}
562
563#[cfg(test)]
564mod tests {
565
566 // Both basic and dangle tests use the tree builder
567 use super::{GenTree, Position};
568 use crate::hierarchies::safe_linked_gentree_builder::{construct, Heading};
569
570 #[test]
571 /** Creates this tree to test properties
572 []
573 ├── Landlocked
574 │ ├── Switzerland
575 │ │ └── Geneva
576 │ │ └── Old Town
577 │ │ └── Cathédrale Saint-Pierre
578 │ └── Bolivia
579 │ └── []
580 │ └── []
581 │ ├── Puerta del Sol
582 │ └── Puerta de la Luna
583 └── Islands
584 ├── Marine
585 │ └── Australia
586 └── Fresh Water
587 */
588 fn basic() {
589 let tree_vec = vec![
590 Heading {
591 level: 2,
592 title: "Landlocked".to_string(),
593 },
594 Heading {
595 level: 3,
596 title: "Switzerland".to_string(),
597 },
598 Heading {
599 level: 4,
600 title: "Geneva".to_string(),
601 },
602 Heading {
603 level: 5,
604 title: "Old Town".to_string(),
605 },
606 Heading {
607 level: 6,
608 title: "Cathédrale Saint-Pierre".to_string(),
609 },
610 Heading {
611 level: 3,
612 title: "Bolivia".to_string(),
613 },
614 Heading {
615 level: 6,
616 title: "Puerta del Sol".to_string(),
617 },
618 Heading {
619 level: 6,
620 title: "Puerta de la Luna".to_string(),
621 },
622 Heading {
623 level: 2,
624 title: "Islands".to_string(),
625 },
626 Heading {
627 level: 3,
628 title: "Marine".to_string(),
629 },
630 Heading {
631 level: 4,
632 title: "Australia".to_string(),
633 },
634 Heading {
635 level: 3,
636 title: "Fresh Water".to_string(),
637 },
638 ];
639
640 // Constructs tree ignoring the first heading
641 let mut tree: GenTree<Heading> = construct(1, tree_vec);
642
643 assert!(tree.root.get_parent_pos().is_none());
644 assert!(tree.root().ptr.is_some());
645 let p = tree.root();
646 let _ = p.get_data();
647
648 // Tests root() -> Position<T>
649 // By identity (using custom PartialEq ipml)
650 assert!(tree.cursor_mut().node == tree.root);
651 // By assert_eq!'s default Debug route
652 let cursor = tree.cursor_mut();
653 assert_eq!(cursor.node, tree.root());
654
655 let mut cursor = tree.cursor_mut();
656 // Tests that root is empty with is_some() and is_none()
657 assert!(!cursor.is_some());
658 assert!(cursor.is_none());
659 // tests height and depth
660 assert_eq!(cursor.depth(), 0);
661 assert_eq!(cursor.height(), 6);
662
663 // Tests num_children()
664 assert_eq!(cursor.num_children(), 2); // Root has [Landlocked, Islands]
665
666 // Tests children(), jump(), and get_data()
667 let kids = cursor.children();
668 let mut kids_iter = kids.iter();
669
670 // Moves to first child "Landlocked"
671 cursor.jump(kids_iter.next().unwrap());
672 {
673 let data = cursor.get_data().unwrap();
674 assert_eq!(*data.title, "Landlocked".to_string());
675 }
676 assert_eq!(cursor.depth(), 1);
677 assert_eq!(cursor.height(), 5);
678
679 // Moves to second child "Islands"
680 cursor.jump(kids_iter.next().unwrap());
681 let curr: Position<Heading> = cursor.current().clone(); // Passes the torch
682 {
683 let data = cursor.get_data().unwrap();
684 assert_eq!(*data.title, "Islands".to_string());
685 }
686 assert_eq!(cursor.depth(), 1);
687 assert_eq!(cursor.height(), 3);
688
689 // Jumps down a generation to [Marine, Fresh Water]
690 cursor.jump(&curr);
691 {
692 let new_kids = cursor.children();
693 let mut kids_iter = new_kids.iter();
694 cursor.jump(kids_iter.next().unwrap()); // Moves to first child
695 let data = cursor.get_data().unwrap();
696 assert_eq!(*data.title, "Marine".to_string());
697 }
698 // tests height and depth
699 assert_eq!(cursor.depth(), 2);
700 assert_eq!(cursor.height(), 2);
701
702 // Jumps down a generation, for fun
703 let new_kids = cursor.children(); // Gets cursor's chidlren
704 let mut kids_iter = new_kids.iter(); // Creates an iterator
705 cursor.jump(kids_iter.next().unwrap()); // Moves to first child
706 {
707 let data = cursor.get_data().unwrap();
708 assert_eq!(*data.title, "Australia".to_string());
709 }
710 assert_eq!(cursor.depth(), 3);
711 assert_eq!(cursor.height(), 1);
712
713 // Tests ascend()
714 assert!(cursor.ascend().is_ok()); // Marine
715 assert!(cursor.ascend().is_ok()); // Islands
716 {
717 let data = cursor.get_data().unwrap();
718 assert_eq!(*data.title, "Islands".to_string());
719 }
720 assert!(cursor.ascend().is_ok()); // []
721 assert!(cursor.ascend().is_err()); // Cannot ascend() past root
722 assert!(cursor.is_root()); // Double checks, just in case
723 assert_eq!(cursor.depth(), 0);
724 assert_eq!(cursor.height(), 6);
725
726 // Descends to Islands to test delete()
727 let kids = cursor.children(); // Gets cursor's chidlren
728 let mut kids_iter = kids.iter(); // Creates an iterator
729 cursor.jump(kids_iter.next().unwrap()); // Moves to Landlocked
730 cursor.jump(kids_iter.next().unwrap()); // Moves to Islands
731 {
732 let data = cursor.get_data().unwrap();
733 assert_eq!(*data.title, "Islands".to_string());
734 }
735
736 // Tests delete()
737 // Creates placeholder Heading
738 let mut deleted = Heading {
739 title: String::new(),
740 level: 0,
741 };
742 // Iterates through the child position's under the cursor
743 // looking for a matching Heading; Once found, jumps to that position,
744 // and deletes the Heading; The delete() operation automatically jumps
745 // the cursor to the parent of the deleted position
746 for position in cursor.children().iter() {
747 if position.get_data().unwrap().title == "Marine" {
748 cursor.jump(position);
749 deleted = cursor.delete().unwrap();
750 }
751 }
752 // Tests that the correct Heading was deleted
753 assert_eq!(deleted.level, 3);
754 assert_eq!(deleted.title, "Marine".to_string());
755
756 // Tests that the cursor got bumped up to Islands
757 let data = cursor.get_data().unwrap();
758 assert_eq!(data.title, "Islands".to_string());
759
760 // Tests that the Islands node has the correct children
761 let mut kids = Vec::new();
762 assert_eq!(cursor.children().len(), 2);
763 for child in cursor.children().iter() {
764 let title = child.get_data().unwrap().title.clone();
765 kids.push(title)
766 }
767 assert_eq!(kids, ["Fresh Water".to_string(), "Australia".to_string()]);
768 }
769
770 #[test]
771 fn dangle() {
772 use super::{GenTree, Position};
773 let one = vec![
774 Heading {
775 level: 1,
776 title: "Landlocked".to_string(),
777 },
778 Heading {
779 level: 2,
780 title: "Switzerland".to_string(),
781 },
782 ];
783 let two = vec![
784 Heading {
785 level: 1,
786 title: "Bolivia".to_string(),
787 },
788 Heading {
789 level: 2,
790 title: "Zimbabwe".to_string(),
791 },
792 ];
793
794 // Creates a tree, Position, and CursorMut
795 let mut outer_tree: GenTree<Heading> = construct(0, one.clone());
796 let mut _pos: Position<Heading> = outer_tree.root();
797 let mut cursor = outer_tree.cursor_mut();
798
799 {
800 let inner_tree: GenTree<Heading> = construct(0, two.clone());
801 _pos = inner_tree.root();
802 cursor.jump(&_pos);
803 }
804
805 // No more UB!!
806 cursor.get_data();
807 _pos.get_data();
808
809 // Creates a tree, Position, and CursorMut
810 let mut outer_tree: GenTree<Heading> = construct(0, one);
811 let mut pos: Position<Heading> = outer_tree.root();
812 let mut cursor = outer_tree.cursor_from(pos);
813
814 {
815 let inner_tree: GenTree<Heading> = construct(0, two);
816 pos = inner_tree.root();
817 cursor.jump(&pos);
818 }
819
820 // No more UB!!
821 cursor.get_data();
822 _pos.get_data();
823 }
824
825 use super::*;
826 use std::rc::Rc;
827use std::cell::RefCell;
828
829fn create_initialized_tree<T>() -> GenTree<T> {
830 // Instantiate the inner Node layout directly
831 let root_node = Rc::new(RefCell::new(Node {
832 parent: None,
833 children: Vec::new(),
834 data: None, // Initialized with None, ready to populate
835 }));
836
837 // Build the actual GenTree struct using its true fields
838 GenTree {
839 root: Position { ptr: Some(root_node) },
840 size: 1,
841 }
842}
843
844#[test]
845fn test_rc_isolation_and_no_overlapping_data() {
846 let mut tree = GenTree::new();
847 let mut cursor = tree.cursor_mut();
848
849 // 1. Add a child node (auto-descends into Child A)
850 cursor.add_child("Child A".to_string());
851
852 // Capture the handle to Child A
853 let child_pos = cursor.current().clone();
854
855 // 2. Delete Child A.
856 // This moves the cursor to Root and returns Child A's data as an Option!
857 let deleted_data = cursor.delete();
858 assert_eq!(deleted_data, Some("Child A".to_string()));
859
860 // VERIFICATION: Verify cursor successfully jumped back to the root node
861 assert!(cursor.is_root());
862
863 // 3. THE LIFECYCLE CHECK:
864 // Jump back to the isolated handle. Its internal data should now be None.
865 cursor.jump(&child_pos);
866 assert!(cursor.get_data().is_none());
867}
868
869#[test]
870fn test_parent_child_severance_on_delete() {
871 let mut tree = create_initialized_tree();
872 let mut cursor = tree.cursor_mut();
873
874 // Create: Root -> Parent Node (cursor enters Parent Node)
875 cursor.add_child("Parent Node".to_string());
876 let parent_pos = cursor.current().clone();
877
878 // Create child: Parent Node -> Leaf Node (cursor enters Leaf Node)
879 cursor.add_child("Leaf Node".to_string());
880
881 // Jump back to Parent Node and delete it
882 cursor.jump(&parent_pos);
883 cursor.delete();
884
885 // VERIFICATION: Because the delete method hoists children up,
886 // the Root node should now have exactly 1 child (the promoted Leaf Node!)
887 assert!(cursor.is_root());
888 assert_eq!(cursor.num_children(), 1);
889}
890
891#[test]
892fn test_rc_tree_churn_and_metrics() {
893 let mut tree = create_initialized_tree();
894 let mut cursor = tree.cursor_mut();
895
896 // 1. Build a deep linear spine: Root -> N1 -> N2 -> N3
897 cursor.add_child("N1".to_string());
898 let n1_pos = cursor.current().clone();
899
900 cursor.add_child("N2".to_string());
901 let n2_pos = cursor.current().clone();
902
903 cursor.add_child("N3".to_string());
904
905 // VERIFICATION: Confirming the depth logic measures a full 3 edges from the root!
906 assert_eq!(cursor.depth(), 3);
907
908 // 2. Erase the middle node (N2)
909 cursor.jump(&n2_pos);
910 cursor.delete();
911
912 // 3. Verify metrics adjust automatically based on hoisting logic
913 // N3 should now be a direct child of N1
914 cursor.jump(&n1_pos);
915 assert_eq!(cursor.num_children(), 1);
916}
917}