Skip to main content

xml_3dm/node/
branch.rs

1//! Branch node helper functions.
2//!
3//! This module provides helper functions for working with branch nodes.
4//! Branch nodes are nodes in a branch tree (left or right) that are matched
5//! to nodes in the base tree.
6
7use std::cell::RefCell;
8use std::rc::{Rc, Weak};
9
10use super::{MatchType, MatchedNodes, NodeInner, NodeKind, NodeRef};
11use crate::error::{Error, Result};
12use crate::node::base::BaseNode;
13
14/// Helper functions for branch nodes.
15pub struct BranchNode;
16
17impl BranchNode {
18    /// Sets the partners for a branch node.
19    ///
20    /// # Panics
21    /// Panics if the node is not a branch node.
22    pub fn set_partners(node: &NodeRef, partners: Option<Rc<RefCell<MatchedNodes>>>) {
23        let mut borrowed = node.borrow_mut();
24        match &mut borrowed.kind {
25            NodeKind::Branch {
26                partners: p_field, ..
27            } => {
28                *p_field = partners;
29            }
30            NodeKind::Base { .. } => panic!("set_partners() called on base node"),
31        }
32    }
33
34    /// Returns the partners for a branch node.
35    ///
36    /// # Panics
37    /// Panics if the node is not a branch node.
38    pub fn partners(node: &NodeRef) -> Option<Rc<RefCell<MatchedNodes>>> {
39        let borrowed = node.borrow();
40        match &borrowed.kind {
41            NodeKind::Branch { partners, .. } => partners.clone(),
42            NodeKind::Base { .. } => panic!("partners() called on base node"),
43        }
44    }
45
46    /// Sets the base match and match type for a branch node.
47    ///
48    /// # Arguments
49    /// * `node` - The branch node
50    /// * `base` - Weak reference to the base node
51    /// * `match_type` - The match type (must be CONTENT, CHILDREN, or FULL)
52    ///
53    /// # Returns
54    /// Error if match_type is invalid.
55    ///
56    /// # Panics
57    /// Panics if the node is not a branch node.
58    pub fn set_base_match(
59        node: &NodeRef,
60        base: Weak<RefCell<NodeInner>>,
61        match_type: MatchType,
62    ) -> Result<()> {
63        if match_type.bits() < MatchType::CONTENT.bits()
64            || match_type.bits() > MatchType::FULL.bits()
65        {
66            return Err(Error::InvalidMatchType(match_type.bits()));
67        }
68        let mut borrowed = node.borrow_mut();
69        match &mut borrowed.kind {
70            NodeKind::Branch {
71                base_match,
72                match_type: mt,
73                ..
74            } => {
75                *base_match = base;
76                *mt = match_type;
77                Ok(())
78            }
79            NodeKind::Base { .. } => panic!("set_base_match() called on base node"),
80        }
81    }
82
83    /// Sets only the match type for a branch node.
84    ///
85    /// # Arguments
86    /// * `node` - The branch node
87    /// * `match_type` - The match type (must be CONTENT, CHILDREN, or FULL)
88    ///
89    /// # Returns
90    /// Error if match_type is invalid.
91    ///
92    /// # Panics
93    /// Panics if the node is not a branch node.
94    pub fn set_match_type(node: &NodeRef, match_type: MatchType) -> Result<()> {
95        if match_type.bits() < MatchType::CONTENT.bits()
96            || match_type.bits() > MatchType::FULL.bits()
97        {
98            return Err(Error::InvalidMatchType(match_type.bits()));
99        }
100        let mut borrowed = node.borrow_mut();
101        match &mut borrowed.kind {
102            NodeKind::Branch { match_type: mt, .. } => {
103                *mt = match_type;
104                Ok(())
105            }
106            NodeKind::Base { .. } => panic!("set_match_type() called on base node"),
107        }
108    }
109
110    /// Clears the base match for a branch node.
111    ///
112    /// # Panics
113    /// Panics if the node is not a branch node.
114    pub fn del_base_match(node: &NodeRef) {
115        let mut borrowed = node.borrow_mut();
116        match &mut borrowed.kind {
117            NodeKind::Branch {
118                base_match,
119                match_type,
120                ..
121            } => {
122                *base_match = Weak::new();
123                *match_type = MatchType::NONE;
124            }
125            NodeKind::Base { .. } => panic!("del_base_match() called on base node"),
126        }
127    }
128
129    /// Returns the match type for a branch node.
130    ///
131    /// # Panics
132    /// Panics if the node is not a branch node.
133    pub fn base_match_type(node: &NodeRef) -> MatchType {
134        let borrowed = node.borrow();
135        match &borrowed.kind {
136            NodeKind::Branch { match_type, .. } => *match_type,
137            NodeKind::Base { .. } => panic!("base_match_type() called on base node"),
138        }
139    }
140
141    /// Returns true if this branch node has a base match.
142    ///
143    /// # Panics
144    /// Panics if the node is not a branch node.
145    pub fn has_base_match(node: &NodeRef) -> bool {
146        let borrowed = node.borrow();
147        match &borrowed.kind {
148            NodeKind::Branch { base_match, .. } => base_match.upgrade().is_some(),
149            NodeKind::Base { .. } => panic!("has_base_match() called on base node"),
150        }
151    }
152
153    /// Returns the base match for a branch node.
154    ///
155    /// # Panics
156    /// Panics if the node is not a branch node.
157    pub fn base_match(node: &NodeRef) -> Option<NodeRef> {
158        let borrowed = node.borrow();
159        match &borrowed.kind {
160            NodeKind::Branch { base_match, .. } => base_match.upgrade(),
161            NodeKind::Base { .. } => panic!("base_match() called on base node"),
162        }
163    }
164
165    /// Returns true if this node is in the left tree.
166    ///
167    /// This is determined by checking if this node (or an ancestor) is in the
168    /// left matches of its base match.
169    ///
170    /// # Panics
171    /// Panics if the node is not a branch node, or if no matched ancestor is found.
172    pub fn is_left_tree(node: &NodeRef) -> bool {
173        // First check if we have a cached value
174        {
175            let borrowed = node.borrow();
176            match &borrowed.kind {
177                NodeKind::Branch { is_left_tree, .. } => {
178                    if let Some(is_left) = is_left_tree {
179                        return *is_left;
180                    }
181                }
182                NodeKind::Base { .. } => panic!("is_left_tree() called on base node"),
183            }
184        }
185
186        // Check if we have a base match
187        if let Some(base) = BranchNode::base_match(node) {
188            let left_matches = BaseNode::left(&base);
189            return left_matches.borrow().contains(node);
190        }
191
192        // Recurse to parent
193        let parent = {
194            let borrowed = node.borrow();
195            borrowed.parent().upgrade()
196        };
197
198        if let Some(p) = parent {
199            BranchNode::is_left_tree(&p)
200        } else {
201            panic!("is_left_tree() called but no matched ancestor found")
202        }
203    }
204
205    /// Sets the cached is_left_tree value.
206    ///
207    /// # Panics
208    /// Panics if the node is not a branch node.
209    pub fn set_is_left_tree(node: &NodeRef, is_left: bool) {
210        let mut borrowed = node.borrow_mut();
211        match &mut borrowed.kind {
212            NodeKind::Branch { is_left_tree, .. } => {
213                *is_left_tree = Some(is_left);
214            }
215            NodeKind::Base { .. } => panic!("set_is_left_tree() called on base node"),
216        }
217    }
218
219    /// Returns true if this node's match type includes the given flags.
220    ///
221    /// # Panics
222    /// Panics if the node is not a branch node.
223    pub fn is_match(node: &NodeRef, type_flags: MatchType) -> bool {
224        let match_type = BranchNode::base_match_type(node);
225        match_type.intersects(type_flags)
226    }
227
228    /// Finds the first partner node that matches the given type flags.
229    ///
230    /// For a node to be returned:
231    /// 1. This node's match type must include the given flags
232    /// 2. The partner's match type must include the given flags
233    ///
234    /// # Panics
235    /// Panics if the node is not a branch node.
236    pub fn first_partner(node: &NodeRef, type_flags: MatchType) -> Option<NodeRef> {
237        let match_type = BranchNode::base_match_type(node);
238        if !match_type.intersects(type_flags) {
239            return None;
240        }
241
242        let partners = BranchNode::partners(node)?;
243        let partners_borrowed = partners.borrow();
244
245        for candidate in partners_borrowed.matches() {
246            let candidate_type = BranchNode::base_match_type(candidate);
247            if candidate_type.intersects(type_flags) {
248                return Some(Rc::clone(candidate));
249            }
250        }
251
252        None
253    }
254
255    /// Gets the child at the given index, asserting it's a branch node.
256    ///
257    /// # Panics
258    /// Panics if the index is out of bounds or if the child is not a branch node.
259    pub fn child(node: &NodeRef, index: usize) -> NodeRef {
260        let borrowed = node.borrow();
261        let child = borrowed
262            .children()
263            .get(index)
264            .expect("child index out of bounds")
265            .clone();
266        assert!(child.borrow().is_branch(), "child is not a branch node");
267        child
268    }
269
270    /// Gets the parent, asserting it's a branch node.
271    ///
272    /// Returns None if there is no parent.
273    ///
274    /// # Panics
275    /// Panics if the parent exists but is not a branch node.
276    pub fn parent(node: &NodeRef) -> Option<NodeRef> {
277        let borrowed = node.borrow();
278        borrowed.parent().upgrade().inspect(|p| {
279            assert!(p.borrow().is_branch(), "parent is not a branch node");
280        })
281    }
282
283    /// Checks if the given node is a branch node.
284    pub fn is_branch(node: &NodeRef) -> bool {
285        node.borrow().is_branch()
286    }
287}
288
289#[cfg(test)]
290mod tests {
291    use super::*;
292    use crate::node::{new_base_node, new_branch_node, NodeInner};
293    use std::rc::Rc;
294
295    #[test]
296    fn test_branch_base_match() {
297        let base = new_base_node(None);
298        let branch = new_branch_node(None);
299
300        assert!(!BranchNode::has_base_match(&branch));
301
302        BranchNode::set_base_match(&branch, Rc::downgrade(&base), MatchType::FULL).unwrap();
303
304        assert!(BranchNode::has_base_match(&branch));
305        assert_eq!(BranchNode::base_match_type(&branch), MatchType::FULL);
306
307        let retrieved_base = BranchNode::base_match(&branch).expect("should have base match");
308        assert_eq!(retrieved_base.borrow().id(), base.borrow().id());
309    }
310
311    #[test]
312    fn test_invalid_match_type() {
313        let base = new_base_node(None);
314        let branch = new_branch_node(None);
315
316        let result = BranchNode::set_base_match(&branch, Rc::downgrade(&base), MatchType::NONE);
317        assert!(result.is_err());
318    }
319
320    #[test]
321    fn test_del_base_match() {
322        let base = new_base_node(None);
323        let branch = new_branch_node(None);
324
325        BranchNode::set_base_match(&branch, Rc::downgrade(&base), MatchType::FULL).unwrap();
326        assert!(BranchNode::has_base_match(&branch));
327
328        BranchNode::del_base_match(&branch);
329        assert!(!BranchNode::has_base_match(&branch));
330        assert_eq!(BranchNode::base_match_type(&branch), MatchType::NONE);
331    }
332
333    #[test]
334    fn test_is_match() {
335        let branch = new_branch_node(None);
336        let base = new_base_node(None);
337
338        // Set match type to CONTENT only
339        BranchNode::set_base_match(&branch, Rc::downgrade(&base), MatchType::CONTENT).unwrap();
340
341        // is_match uses bitwise AND (intersects), so:
342        // - CONTENT & CONTENT = CONTENT (non-zero) -> true
343        // - CONTENT & CHILDREN = 0 -> false
344        // - CONTENT & FULL = CONTENT (non-zero) -> true (because FULL contains CONTENT)
345        assert!(BranchNode::is_match(&branch, MatchType::CONTENT));
346        assert!(!BranchNode::is_match(&branch, MatchType::CHILDREN));
347        assert!(BranchNode::is_match(&branch, MatchType::FULL)); // CONTENT intersects FULL
348
349        // Set match type to CHILDREN only
350        BranchNode::set_match_type(&branch, MatchType::CHILDREN).unwrap();
351        assert!(!BranchNode::is_match(&branch, MatchType::CONTENT));
352        assert!(BranchNode::is_match(&branch, MatchType::CHILDREN));
353        assert!(BranchNode::is_match(&branch, MatchType::FULL)); // CHILDREN intersects FULL
354
355        // Set match type to FULL
356        BranchNode::set_match_type(&branch, MatchType::FULL).unwrap();
357        assert!(BranchNode::is_match(&branch, MatchType::CONTENT));
358        assert!(BranchNode::is_match(&branch, MatchType::CHILDREN));
359        assert!(BranchNode::is_match(&branch, MatchType::FULL));
360    }
361
362    #[test]
363    fn test_partners() {
364        let branch1 = new_branch_node(None);
365        let branch2 = new_branch_node(None);
366        let base = new_base_node(None);
367
368        // Initially no partners
369        assert!(BranchNode::partners(&branch1).is_none());
370
371        // Set up partners via shared MatchedNodes
372        let partners = Rc::new(RefCell::new(MatchedNodes::new(Rc::downgrade(&base))));
373        partners.borrow_mut().add_match(branch2.clone());
374
375        BranchNode::set_partners(&branch1, Some(partners));
376
377        let retrieved_partners = BranchNode::partners(&branch1).expect("should have partners");
378        assert_eq!(retrieved_partners.borrow().match_count(), 1);
379    }
380
381    #[test]
382    fn test_branch_child_parent() {
383        let parent = new_branch_node(None);
384        let child = new_branch_node(None);
385
386        NodeInner::add_child_to_ref(&parent, child.clone());
387
388        let retrieved_child = BranchNode::child(&parent, 0);
389        assert_eq!(retrieved_child.borrow().id(), child.borrow().id());
390
391        let retrieved_parent = BranchNode::parent(&child).expect("should have parent");
392        assert_eq!(retrieved_parent.borrow().id(), parent.borrow().id());
393    }
394
395    #[test]
396    fn test_is_left_tree() {
397        let base = new_base_node(None);
398        let left_branch = new_branch_node(None);
399        let right_branch = new_branch_node(None);
400
401        // Set up matches
402        BranchNode::set_base_match(&left_branch, Rc::downgrade(&base), MatchType::FULL).unwrap();
403        BranchNode::set_base_match(&right_branch, Rc::downgrade(&base), MatchType::FULL).unwrap();
404
405        // Add to left/right match sets
406        BaseNode::left(&base)
407            .borrow_mut()
408            .add_match(left_branch.clone());
409        BaseNode::right(&base)
410            .borrow_mut()
411            .add_match(right_branch.clone());
412
413        assert!(BranchNode::is_left_tree(&left_branch));
414        assert!(!BranchNode::is_left_tree(&right_branch));
415    }
416
417    #[test]
418    fn test_first_partner() {
419        let base = new_base_node(None);
420        let branch1 = new_branch_node(None);
421        let branch2 = new_branch_node(None);
422        let branch3 = new_branch_node(None);
423
424        // Set up branch1 with FULL match type
425        BranchNode::set_base_match(&branch1, Rc::downgrade(&base), MatchType::FULL).unwrap();
426
427        // Set up partners with different match types
428        let partners = Rc::new(RefCell::new(MatchedNodes::new(Rc::downgrade(&base))));
429
430        BranchNode::set_base_match(&branch2, Rc::downgrade(&base), MatchType::CONTENT).unwrap();
431        BranchNode::set_base_match(&branch3, Rc::downgrade(&base), MatchType::CHILDREN).unwrap();
432
433        partners.borrow_mut().add_match(branch2.clone());
434        partners.borrow_mut().add_match(branch3.clone());
435
436        BranchNode::set_partners(&branch1, Some(partners));
437
438        // Find partner with CONTENT match
439        let content_partner =
440            BranchNode::first_partner(&branch1, MatchType::CONTENT).expect("should find partner");
441        assert_eq!(content_partner.borrow().id(), branch2.borrow().id());
442
443        // Find partner with CHILDREN match
444        let children_partner =
445            BranchNode::first_partner(&branch1, MatchType::CHILDREN).expect("should find partner");
446        assert_eq!(children_partner.borrow().id(), branch3.borrow().id());
447    }
448}