Skip to main content

xml_3dm/node/
base.rs

1//! Base node helper functions.
2//!
3//! This module provides helper functions for working with base nodes.
4//! Base nodes are nodes in the base tree that are matched to two branch trees
5//! (left and right).
6
7use std::cell::RefCell;
8use std::rc::Rc;
9
10use super::{MatchedNodes, NodeKind, NodeRef};
11
12/// Helper functions for base nodes.
13pub struct BaseNode;
14
15impl BaseNode {
16    /// Returns the left MatchedNodes for a base node.
17    ///
18    /// # Panics
19    /// Panics if the node is not a base node.
20    pub fn left(node: &NodeRef) -> Rc<RefCell<MatchedNodes>> {
21        let borrowed = node.borrow();
22        match &borrowed.kind {
23            NodeKind::Base { left, .. } => Rc::clone(left),
24            NodeKind::Branch { .. } => panic!("left() called on branch node"),
25        }
26    }
27
28    /// Returns the right MatchedNodes for a base node.
29    ///
30    /// # Panics
31    /// Panics if the node is not a base node.
32    pub fn right(node: &NodeRef) -> Rc<RefCell<MatchedNodes>> {
33        let borrowed = node.borrow();
34        match &borrowed.kind {
35            NodeKind::Base { right, .. } => Rc::clone(right),
36            NodeKind::Branch { .. } => panic!("right() called on branch node"),
37        }
38    }
39
40    /// Swaps the left and right matchings.
41    ///
42    /// # Panics
43    /// Panics if the node is not a base node.
44    pub fn swap_left_right_matchings(node: &NodeRef) {
45        let mut borrowed = node.borrow_mut();
46        match &mut borrowed.kind {
47            NodeKind::Base { left, right } => {
48                std::mem::swap(left, right);
49            }
50            NodeKind::Branch { .. } => panic!("swap_left_right_matchings() called on branch node"),
51        }
52    }
53
54    /// Gets the child at the given index, asserting it's a base node.
55    ///
56    /// # Panics
57    /// Panics if the index is out of bounds or if the child is not a base node.
58    pub fn child(node: &NodeRef, index: usize) -> NodeRef {
59        let borrowed = node.borrow();
60        let child = borrowed
61            .children()
62            .get(index)
63            .expect("child index out of bounds")
64            .clone();
65        assert!(child.borrow().is_base(), "child is not a base node");
66        child
67    }
68
69    /// Gets the parent, asserting it's a base node.
70    ///
71    /// Returns None if there is no parent.
72    ///
73    /// # Panics
74    /// Panics if the parent exists but is not a base node.
75    pub fn parent(node: &NodeRef) -> Option<NodeRef> {
76        let borrowed = node.borrow();
77        borrowed.parent().upgrade().inspect(|p| {
78            assert!(p.borrow().is_base(), "parent is not a base node");
79        })
80    }
81
82    /// Checks if the given node is a base node.
83    pub fn is_base(node: &NodeRef) -> bool {
84        node.borrow().is_base()
85    }
86}
87
88#[cfg(test)]
89mod tests {
90    use super::*;
91    use crate::node::{new_base_node, new_branch_node, NodeInner};
92
93    #[test]
94    fn test_base_left_right() {
95        let base = new_base_node(None);
96
97        // Should not panic
98        let _left = BaseNode::left(&base);
99        let _right = BaseNode::right(&base);
100    }
101
102    #[test]
103    #[should_panic(expected = "left() called on branch node")]
104    fn test_left_on_branch_panics() {
105        let branch = new_branch_node(None);
106        let _left = BaseNode::left(&branch);
107    }
108
109    #[test]
110    fn test_swap_matchings() {
111        let base = new_base_node(None);
112        let branch = new_branch_node(None);
113
114        // Add branch to left matches
115        BaseNode::left(&base).borrow_mut().add_match(branch.clone());
116
117        assert_eq!(BaseNode::left(&base).borrow().match_count(), 1);
118        assert_eq!(BaseNode::right(&base).borrow().match_count(), 0);
119
120        // Swap
121        BaseNode::swap_left_right_matchings(&base);
122
123        assert_eq!(BaseNode::left(&base).borrow().match_count(), 0);
124        assert_eq!(BaseNode::right(&base).borrow().match_count(), 1);
125    }
126
127    #[test]
128    fn test_base_child_parent() {
129        let parent = new_base_node(None);
130        let child = new_base_node(None);
131
132        NodeInner::add_child_to_ref(&parent, child.clone());
133
134        let retrieved_child = BaseNode::child(&parent, 0);
135        assert_eq!(retrieved_child.borrow().id(), child.borrow().id());
136
137        let retrieved_parent = BaseNode::parent(&child).expect("should have parent");
138        assert_eq!(retrieved_parent.borrow().id(), parent.borrow().id());
139    }
140}