1mod conflict_log;
16mod edit_log;
17mod merge_list;
18mod merge_pair;
19
20pub use conflict_log::{ConflictLog, ConflictType};
21pub use edit_log::{EditLog, EditType};
22pub use merge_list::{MergeEntry, MergeList};
23pub use merge_pair::{MergePair, MergePairList};
24
25use std::io::Write;
26
27use crate::matching::TriMatching;
28use crate::node::base::BaseNode;
29use crate::node::branch::BranchNode;
30use crate::node::{new_node_ref, MatchType, NodeInner, NodeRef, XmlContent, XmlElement};
31use crate::xml::XmlPrinter;
32
33#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
35pub enum Operation {
36 Nop,
38 MoveI,
40 MoveF,
42 Delete,
44}
45
46pub struct Merge {
48 matching: TriMatching,
50 pub conflict_log: ConflictLog,
52 pub edit_log: EditLog,
54}
55
56impl Merge {
57 pub fn new(matching: TriMatching) -> Self {
59 Merge {
60 matching,
61 conflict_log: ConflictLog::new(),
62 edit_log: EditLog::new(),
63 }
64 }
65
66 pub fn matching(&self) -> &TriMatching {
68 &self.matching
69 }
70
71 pub fn merge<W: Write>(&mut self, writer: &mut W) -> std::io::Result<()> {
75 let tree = self.merge_to_tree();
77
78 let mut printer = XmlPrinter::new(writer);
80 printer.print(&tree)
81 }
82
83 pub fn merge_to_tree(&mut self) -> NodeRef {
88 let root_content = XmlContent::Element(XmlElement::new(
90 "$ROOT$".to_string(),
91 std::collections::HashMap::new(),
92 ));
93 let root = new_node_ref(NodeInner::new_base(Some(root_content)));
94
95 self.tree_merge_to_node(
97 Some(self.matching.left_root().clone()),
98 Some(self.matching.right_root().clone()),
99 &root,
100 );
101
102 root
103 }
104
105 fn tree_merge_to_node(&mut self, a: Option<NodeRef>, b: Option<NodeRef>, parent: &NodeRef) {
107 let mlist_a = a.as_ref().map(|node| self.make_merge_list(node));
109 let mlist_b = b.as_ref().map(|node| self.make_merge_list(node));
110
111 let merged = match (&mlist_a, &mlist_b) {
113 (Some(ma), Some(mb)) => self.make_merge_pair_list(ma.clone(), mb.clone()),
114 (Some(ma), None) => self.merge_list_to_pair_list(ma, None),
115 (None, Some(mb)) => self.merge_list_to_pair_list(mb, None),
116 (None, None) => MergePairList::new(),
117 };
118
119 for pair in merged.pairs() {
121 let merged_content = self.merge_node_content(pair);
122
123 if let Some(content) = merged_content {
124 match &content {
125 XmlContent::Element(elem) if elem.qname() == "$ROOT$" => {
126 let partners = self.get_recursion_partners(pair);
128 self.tree_merge_to_node(partners.first, partners.second, parent);
129 }
130 _ => {
131 let child = new_node_ref(NodeInner::new_base(Some(content.clone())));
133
134 NodeInner::add_child_to_ref(parent, child.clone());
136
137 if let XmlContent::Element(_) = content {
139 let partners = self.get_recursion_partners(pair);
140 self.tree_merge_to_node(partners.first, partners.second, &child);
141 }
142 }
143 }
144 }
145 }
146 }
147
148 fn make_merge_list(&mut self, parent: &NodeRef) -> MergeList {
150 MergeList::from_branch(parent, &mut self.edit_log)
151 }
152
153 fn make_merge_pair_list(&mut self, mlist_a: MergeList, mlist_b: MergeList) -> MergePairList {
155 let mut mlist_a = mlist_a;
156 let mut mlist_b = mlist_b;
157
158 self.remove_deleted_or_moved(&mut mlist_a, &mut mlist_b);
160
161 self.edit_log.checkpoint();
163
164 if mlist_a.entry_count() != mlist_b.entry_count() {
166 self.edit_log.rewind();
168 let is_left = mlist_a
169 .entry_parent()
170 .map(|p| BranchNode::is_left_tree(&p))
171 .unwrap_or(true);
172 return if is_left {
173 self.merge_list_to_pair_list(&mlist_a, Some(&mlist_b))
174 } else {
175 self.merge_list_to_pair_list(&mlist_b, Some(&mlist_a))
176 };
177 }
178
179 let mut merged = MergePairList::new();
180 let mut pos_a = 0usize;
181 let mut pos_b = 0usize;
182
183 loop {
184 let ea = mlist_a.entry(pos_a);
185 let eb = mlist_b.entry(pos_b);
186
187 if ea.is_none() || eb.is_none() {
188 break;
189 }
190
191 let ea = ea.unwrap();
192 let eb = eb.unwrap();
193
194 for hangon in ea.hangons() {
196 let partner = hangon.borrow().first_partner(MatchType::FULL);
197 merged.append(Some(hangon.clone()), partner);
198 self.log_hangon_struct_ops(hangon);
199 }
200
201 if !self.check_hangon_combine(ea, eb) {
203 for hangon in eb.hangons() {
204 let partner = hangon.borrow().first_partner(MatchType::FULL);
205 merged.append(Some(hangon.clone()), partner);
206 self.log_hangon_struct_ops(hangon);
207 }
208 }
209
210 let next_a: Option<usize>;
212 let next_b: Option<usize>;
213
214 let ea_locked = ea.locked && mlist_a.entry(pos_a + 1).is_some_and(|e| e.locked);
215 let eb_locked = eb.locked && mlist_b.entry(pos_b + 1).is_some_and(|e| e.locked);
216
217 if !ea_locked && !eb_locked {
218 next_a = Some(pos_a + 1);
220 next_b = Some(pos_b + 1);
221 } else if ea_locked && !eb_locked {
222 next_a = Some(pos_a + 1);
223 next_b = mlist_b.find_partner_index(mlist_a.entry(pos_a + 1));
224 } else if !ea_locked && eb_locked {
225 next_b = Some(pos_b + 1);
226 next_a = mlist_a.find_partner_index(mlist_b.entry(pos_b + 1));
227 } else {
228 let partner_b = mlist_b.find_partner_index(mlist_a.entry(pos_a + 1));
230 if partner_b != Some(pos_b + 1) {
231 self.conflict_log.add_list_conflict(
233 ConflictType::Move,
234 "Conflicting moves inside child list",
235 ea.node().and_then(|n| BranchNode::base_match(&n)),
236 ea.node(),
237 eb.node(),
238 );
239 self.edit_log.rewind();
240 let is_left = mlist_a
241 .entry_parent()
242 .map(|p| BranchNode::is_left_tree(&p))
243 .unwrap_or(true);
244 return if is_left {
245 self.merge_list_to_pair_list(&mlist_a, Some(&mlist_b))
246 } else {
247 self.merge_list_to_pair_list(&mlist_b, Some(&mlist_a))
248 };
249 }
250 next_a = Some(pos_a + 1);
251 next_b = partner_b;
252 }
253
254 pos_a = next_a.unwrap_or(pos_a + 1);
255 pos_b = next_b.unwrap_or(pos_b + 1);
256
257 if mlist_a.is_end(pos_a) || mlist_b.is_end(pos_b) {
259 break;
260 }
261
262 let ea = mlist_a.entry(pos_a);
264 let eb = mlist_b.entry(pos_b);
265 if let (Some(ea), Some(eb)) = (ea, eb) {
266 merged.append(ea.node(), eb.node());
267 self.log_entry_struct_ops(ea, eb);
268 }
269 }
270
271 self.edit_log.commit();
272 merged
273 }
274
275 fn merge_list_to_pair_list(
277 &mut self,
278 mlist_a: &MergeList,
279 mlist_b: Option<&MergeList>,
280 ) -> MergePairList {
281 let mut merged = MergePairList::new();
282
283 for (i, entry) in mlist_a.entries().iter().enumerate() {
284 if i > 0 && !mlist_a.is_end(i) {
286 let node = entry.node();
287 let partner = node
288 .as_ref()
289 .and_then(|n| n.borrow().first_partner(MatchType::FULL));
290 merged.append(node, partner);
291 if let Some(n) = entry.node() {
292 self.log_hangon_struct_ops(&n);
293 }
294 }
295
296 for hangon in entry.hangons() {
298 let partner = hangon.borrow().first_partner(MatchType::FULL);
299 merged.append(Some(hangon.clone()), partner);
300 self.log_hangon_struct_ops(hangon);
301 }
302
303 if let Some(mb) = mlist_b {
305 if let Some(partner_entry) = mb.find_partner(entry) {
306 if !self.check_hangon_combine(entry, partner_entry) {
307 for hangon in partner_entry.hangons() {
308 let partner = hangon.borrow().first_partner(MatchType::FULL);
309 merged.append(Some(hangon.clone()), partner);
310 self.log_hangon_struct_ops(hangon);
311 }
312 }
313 }
314 }
315 }
316
317 merged
318 }
319
320 fn remove_deleted_or_moved(&mut self, mlist_a: &mut MergeList, mlist_b: &mut MergeList) {
322 let base_parent = mlist_a
323 .entry_parent()
324 .and_then(|p| BranchNode::base_match(&p));
325
326 if base_parent.is_none() {
327 return;
328 }
329
330 let base_parent = base_parent.unwrap();
331 let child_count = base_parent.borrow().child_count();
332
333 for i in 0..child_count {
334 let bn = base_parent.borrow().child(i).cloned();
335 if bn.is_none() {
336 continue;
337 }
338 let bn = bn.unwrap();
339
340 let op1 = self.get_operation(&bn, mlist_a);
341 let op2 = self.get_operation(&bn, mlist_b);
342
343 if op1 <= op2 {
346 self.handle_operation_pair(op1, op2, &bn, mlist_a, mlist_b);
347 } else {
348 self.handle_operation_pair(op2, op1, &bn, mlist_b, mlist_a);
349 }
350 }
351 }
352
353 fn handle_operation_pair(
355 &mut self,
356 op1: Operation,
357 op2: Operation,
358 bn: &NodeRef,
359 ml1: &mut MergeList,
360 _ml2: &mut MergeList,
361 ) {
362 use Operation::*;
363
364 match (op1, op2) {
365 (Nop, Nop) | (Nop, MoveI) | (MoveI, MoveI) | (Delete, Delete) => {
366 }
368 (Nop, MoveF) | (Nop, Delete) => {
369 if let Some(ix) = ml1.match_in_list(bn) {
371 if op2 == Delete {
372 if let Some(entry) = ml1.entry(ix) {
374 if let Some(node) = entry.node() {
375 if self.is_deletia_modified(&node, ml1) {
376 self.conflict_log.add_list_warning(
377 ConflictType::Delete,
378 "Modifications in deleted subtree",
379 Some(bn.clone()),
380 entry.node(),
381 None,
382 );
383 }
384 }
385 }
386 self.edit_log.delete(Some(bn.clone()));
387 }
388
389 ml1.move_hangons_to_predecessor(ix);
391 ml1.remove_entry_at(ix);
392 }
393 }
394 (MoveI, MoveF) => {
395 if let Some(ix) = ml1.match_in_list(bn) {
397 let node = ml1.entry(ix).and_then(|e| e.node());
398 let partner = node
399 .as_ref()
400 .and_then(|n| n.borrow().first_partner(MatchType::FULL));
401 self.conflict_log.add_list_conflict(
402 ConflictType::Move,
403 "Node moved to different locations",
404 Some(bn.clone()),
405 node,
406 partner,
407 );
408 ml1.remove_entry_at(ix);
409 }
410 }
411 (MoveI, Delete) => {
412 if let Some(ix) = ml1.match_in_list(bn) {
414 let node = ml1.entry(ix).and_then(|e| e.node());
415 self.conflict_log.add_list_conflict(
416 ConflictType::Move,
417 "Node moved and deleted",
418 Some(bn.clone()),
419 node,
420 None,
421 );
422 self.edit_log.delete(Some(bn.clone()));
423 ml1.remove_entry_at(ix);
424 }
425 }
426 (MoveF, MoveF) => {
427 if self.is_movef_movef_conflict(bn) {
429 self.conflict_log.add_list_conflict(
430 ConflictType::Move,
431 "Node moved to different locations",
432 Some(bn.clone()),
433 BaseNode::left(bn).borrow().full_match(),
434 BaseNode::right(bn).borrow().full_match(),
435 );
436 }
437 }
438 (MoveF, Delete) => {
439 self.conflict_log.add_list_conflict(
441 ConflictType::Move,
442 "Node moved and deleted",
443 Some(bn.clone()),
444 BaseNode::left(bn).borrow().full_match(),
445 BaseNode::right(bn).borrow().full_match(),
446 );
447 }
448 _ => {}
449 }
450 }
451
452 fn get_operation(&self, bn: &NodeRef, ml: &MergeList) -> Operation {
454 if let Some(pos) = ml.match_in_list(bn) {
455 if ml.entry(pos).is_some_and(|e| e.moved) {
456 Operation::MoveI
457 } else {
458 Operation::Nop
459 }
460 } else {
461 let is_left = ml
463 .entry_parent()
464 .map(|p| BranchNode::is_left_tree(&p))
465 .unwrap_or(true);
466
467 let copies = if is_left {
468 BaseNode::left(bn)
469 } else {
470 BaseNode::right(bn)
471 };
472
473 if copies.borrow().match_count() == 0 {
474 Operation::Delete
475 } else {
476 Operation::MoveF
477 }
478 }
479 }
480
481 fn is_deletia_modified(&self, n: &NodeRef, ml: &MergeList) -> bool {
483 let base_match = BranchNode::base_match(n);
484 if base_match.is_none() {
485 return true; }
487
488 let base_match = base_match.unwrap();
489 if self.get_operation(&base_match, ml) != Operation::Nop {
490 return true; }
492
493 if BranchNode::base_match_type(n) != MatchType::FULL {
494 return true; }
496
497 if let Some(partners) = BranchNode::partners(n) {
499 if partners.borrow().match_count() == 0 {
500 if !self.matches(n, &base_match) {
501 return true; }
503 for i in 0..n.borrow().child_count() {
505 if let Some(child) = n.borrow().child(i).cloned() {
506 if BranchNode::base_match_type(&child) != MatchType::FULL {
508 return true;
509 }
510 }
511 }
512 }
513 }
514
515 false
516 }
517
518 fn is_movef_movef_conflict(&self, bn: &NodeRef) -> bool {
523 let left_node = BaseNode::left(bn).borrow().full_match();
525 let right_node = BaseNode::right(bn).borrow().full_match();
526
527 let (left_node, right_node) = match (left_node, right_node) {
528 (Some(l), Some(r)) => (l, r),
529 _ => return false,
530 };
531
532 let left_parent = BranchNode::parent(&left_node);
534 let right_parent = BranchNode::parent(&right_node);
535
536 match (left_parent, right_parent) {
537 (Some(lp), Some(rp)) => {
538 let lp_base = BranchNode::base_match(&lp);
539 let rp_base = BranchNode::base_match(&rp);
540 match (lp_base, rp_base) {
541 (Some(lb), Some(rb)) => lb.borrow().id() != rb.borrow().id(),
542 _ => true, }
544 }
545 (None, None) => false, _ => true, }
548 }
549
550 fn merge_node_content(&mut self, pair: &MergePair) -> Option<XmlContent> {
552 let n1 = pair.first.as_ref();
553 let n2 = pair.second.as_ref();
554
555 match (n1, n2) {
556 (None, None) => None,
557 (Some(n), None) | (None, Some(n)) => n.borrow().content().cloned(),
558 (Some(n1), Some(n2)) => {
559 let n1_content = n1.borrow().is_match(MatchType::CONTENT);
560 let n2_content = n2.borrow().is_match(MatchType::CONTENT);
561
562 if n1_content {
563 if !n2_content {
564 self.edit_log.update(Some(n2.clone()));
565 n2.borrow().content().cloned()
566 } else {
567 self.content_merge(n1, n2)
568 }
569 } else if n2_content {
570 self.edit_log.update(Some(n1.clone()));
571 n1.borrow().content().cloned()
572 } else {
573 self.content_merge(n1, n2)
575 }
576 }
577 }
578 }
579
580 fn content_merge(&mut self, a: &NodeRef, b: &NodeRef) -> Option<XmlContent> {
582 let base_a = BranchNode::base_match(a);
583 let base_b = BranchNode::base_match(b);
584
585 let a_updated = base_a.as_ref().is_none_or(|ba| !self.matches(a, ba));
586 let b_updated = base_b.as_ref().is_none_or(|bb| !self.matches(b, bb));
587
588 if a_updated && b_updated {
589 if self.matches(a, b) {
591 self.conflict_log.add_node_warning(
592 ConflictType::Update,
593 "Node updated in both branches, but updates are equal",
594 base_a.clone(),
595 Some(a.clone()),
596 Some(b.clone()),
597 );
598 self.edit_log.update(Some(a.clone()));
599 return a.borrow().content().cloned();
600 }
601
602 if let Some(merged) = self.merge_elements(base_a.as_ref(), a, b) {
604 return Some(merged);
605 }
606
607 self.conflict_log.add_node_conflict(
609 ConflictType::Update,
610 "Node updated in both branches, using branch 1",
611 base_a,
612 Some(a.clone()),
613 Some(b.clone()),
614 );
615 self.edit_log.update(Some(a.clone()));
616 a.borrow().content().cloned()
617 } else if b_updated {
618 self.edit_log.update(Some(b.clone()));
619 b.borrow().content().cloned()
620 } else if a_updated {
621 self.edit_log.update(Some(a.clone()));
622 a.borrow().content().cloned()
623 } else {
624 a.borrow().content().cloned()
625 }
626 }
627
628 fn merge_elements(
630 &self,
631 base: Option<&NodeRef>,
632 a: &NodeRef,
633 b: &NodeRef,
634 ) -> Option<XmlContent> {
635 let base_content = base.and_then(|n| n.borrow().content().cloned());
636 let a_content = a.borrow().content().cloned();
637 let b_content = b.borrow().content().cloned();
638
639 match (&base_content, &a_content, &b_content) {
640 (
641 Some(XmlContent::Element(base_elem)),
642 Some(XmlContent::Element(a_elem)),
643 Some(XmlContent::Element(b_elem)),
644 ) => {
645 let tag_name = if base_elem.qname() == b_elem.qname() {
647 a_elem.qname().to_string()
648 } else if base_elem.qname() == a_elem.qname() {
649 b_elem.qname().to_string()
650 } else {
651 return None; };
653
654 self.merge_attributes(
656 base_elem.attributes(),
657 a_elem.attributes(),
658 b_elem.attributes(),
659 )
660 .map(|merged_attrs| XmlContent::Element(XmlElement::new(tag_name, merged_attrs)))
661 }
662 _ => None,
663 }
664 }
665
666 fn merge_attributes(
668 &self,
669 base: &std::collections::HashMap<String, String>,
670 a: &std::collections::HashMap<String, String>,
671 b: &std::collections::HashMap<String, String>,
672 ) -> Option<std::collections::HashMap<String, String>> {
673 let mut merged = std::collections::HashMap::new();
674 let mut deletia = std::collections::HashSet::new();
675
676 for (key, base_val) in base {
678 let in_a = a.get(key);
679 let in_b = b.get(key);
680
681 match (in_a, in_b) {
682 (None, Some(b_val)) if base_val != b_val => return None, (Some(a_val), None) if base_val != a_val => return None, (None, _) | (_, None) => {
685 deletia.insert(key.clone());
686 }
687 _ => {}
688 }
689 }
690
691 for (key, a_val) in a {
693 if deletia.contains(key) {
694 continue;
695 }
696
697 if let Some(b_val) = b.get(key) {
698 let base_val = base.get(key);
699 if base_val == Some(b_val) {
700 merged.insert(key.clone(), a_val.clone());
702 } else if base_val == Some(a_val) {
703 merged.insert(key.clone(), b_val.clone());
705 } else if a_val != b_val {
706 return None;
708 } else {
709 merged.insert(key.clone(), a_val.clone());
710 }
711 } else {
712 merged.insert(key.clone(), a_val.clone());
714 }
715 }
716
717 for (key, b_val) in b {
719 if !deletia.contains(key) && !a.contains_key(key) {
720 merged.insert(key.clone(), b_val.clone());
721 }
722 }
723
724 Some(merged)
725 }
726
727 fn get_recursion_partners(&self, pair: &MergePair) -> RecursionPartners {
729 let n1 = pair.first.as_ref();
730 let n2 = pair.second.as_ref();
731
732 match (n1, n2) {
733 (None, _) | (_, None) => RecursionPartners {
734 first: pair.first.clone(),
735 second: pair.second.clone(),
736 },
737 (Some(n1), Some(n2)) => {
738 let n1_children = n1.borrow().is_match(MatchType::CHILDREN);
739 let n2_children = n2.borrow().is_match(MatchType::CHILDREN);
740
741 if n1_children && n2_children {
742 RecursionPartners {
743 first: Some(n1.clone()),
744 second: Some(n2.clone()),
745 }
746 } else if n1_children && n2.borrow().is_match(MatchType::CONTENT) {
747 RecursionPartners {
748 first: None,
749 second: Some(n2.clone()),
750 }
751 } else if n1.borrow().is_match(MatchType::CONTENT) && n2_children {
752 RecursionPartners {
753 first: Some(n1.clone()),
754 second: None,
755 }
756 } else {
757 RecursionPartners {
758 first: Some(n1.clone()),
759 second: Some(n2.clone()),
760 }
761 }
762 }
763 }
764 }
765
766 fn matches(&self, a: &NodeRef, b: &NodeRef) -> bool {
768 let a_borrowed = a.borrow();
769 let b_borrowed = b.borrow();
770 let a_content = a_borrowed.content();
771 let b_content = b_borrowed.content();
772
773 match (a_content, b_content) {
774 (Some(ac), Some(bc)) => ac.content_equals(bc),
775 _ => false,
776 }
777 }
778
779 fn log_hangon_struct_ops(&mut self, n: &NodeRef) {
781 if !BranchNode::has_base_match(n) {
782 self.edit_log.insert(Some(n.clone()));
783 } else {
784 let base = BranchNode::base_match(n).unwrap();
785 let is_left = BranchNode::is_left_tree(n);
786
787 let copies = if is_left {
788 BaseNode::left(&base)
789 } else {
790 BaseNode::right(&base)
791 };
792
793 if copies.borrow().match_count() > 1 {
794 self.edit_log.copy(Some(n.clone()));
795 } else {
796 self.edit_log.r#move(Some(n.clone()));
797 }
798 }
799 }
800
801 fn log_entry_struct_ops(&mut self, e1: &MergeEntry, e2: &MergeEntry) {
803 if e1.moved {
804 if let Some(n) = e1.node() {
805 self.edit_log.r#move(Some(n));
806 }
807 } else if e2.moved {
808 if let Some(n) = e2.node() {
809 self.edit_log.r#move(Some(n));
810 }
811 }
812 }
813
814 fn check_hangon_combine(&mut self, ea: &MergeEntry, eb: &MergeEntry) -> bool {
818 if ea.hangons().is_empty() {
820 return false;
821 }
822
823 if ea.hangons().len() != eb.hangons().len() {
824 if !ea.hangons().is_empty() && !eb.hangons().is_empty() {
825 self.conflict_log.add_list_warning(
826 ConflictType::Insert,
827 "Insertions/copies in both branches",
828 ea.node().and_then(|n| BranchNode::base_match(&n)),
829 ea.node(),
830 eb.node(),
831 );
832 }
833 return false;
834 }
835
836 let mut equal = true;
838 for (ha, hb) in ea.hangons().iter().zip(eb.hangons().iter()) {
839 if !self.tree_matches(ha, hb) {
840 equal = false;
841 break;
842 }
843 }
844
845 if equal {
846 self.conflict_log.add_list_warning(
847 ConflictType::Insert,
848 "Equal insertions/copies in both branches",
849 ea.node().and_then(|n| BranchNode::base_match(&n)),
850 ea.node(),
851 eb.node(),
852 );
853 } else if !ea.hangons().is_empty() {
854 self.conflict_log.add_list_warning(
855 ConflictType::Insert,
856 "Insertions/copies in both branches, sequencing",
857 ea.node().and_then(|n| BranchNode::base_match(&n)),
858 ea.node(),
859 eb.node(),
860 );
861 }
862
863 equal
864 }
865
866 fn tree_matches(&self, a: &NodeRef, b: &NodeRef) -> bool {
868 if !self.matches(a, b) {
869 return false;
870 }
871
872 let a_count = a.borrow().child_count();
873 let b_count = b.borrow().child_count();
874
875 if a_count != b_count {
876 return false;
877 }
878
879 for i in 0..a_count {
880 let a_child = a.borrow().child(i).cloned();
881 let b_child = b.borrow().child(i).cloned();
882
883 match (a_child, b_child) {
884 (Some(ac), Some(bc)) => {
885 if !self.tree_matches(&ac, &bc) {
886 return false;
887 }
888 }
889 _ => return false,
890 }
891 }
892
893 true
894 }
895}
896
897struct RecursionPartners {
899 first: Option<NodeRef>,
900 second: Option<NodeRef>,
901}
902
903impl crate::node::NodeInner {
904 pub fn first_partner(&self, type_flags: MatchType) -> Option<NodeRef> {
906 if let crate::node::NodeKind::Branch {
907 match_type,
908 partners,
909 ..
910 } = self.kind()
911 {
912 if !match_type.intersects(type_flags) {
913 return None;
914 }
915
916 if let Some(partners) = partners {
917 let borrowed = partners.borrow();
918 for candidate in borrowed.matches() {
919 if BranchNode::base_match_type(candidate).intersects(type_flags) {
920 return Some(candidate.clone());
921 }
922 }
923 }
924 }
925 None
926 }
927
928 pub fn is_match(&self, type_flags: MatchType) -> bool {
930 if let crate::node::NodeKind::Branch { match_type, .. } = self.kind() {
931 match_type.intersects(type_flags)
932 } else {
933 false
934 }
935 }
936}