1use crate::document::Revision;
9use crate::rev_tree::{collect_leaves, RevNode, RevPath, RevStatus, RevTree};
10
11#[derive(Debug, Clone, PartialEq, Eq)]
13pub enum MergeResult {
14 NewLeaf,
16 NewBranch,
18 InternalNode,
20}
21
22pub fn merge_tree(tree: &RevTree, new_path: &RevPath, rev_limit: u64) -> (RevTree, MergeResult) {
26 let mut result_tree = tree.clone();
27 let merge_result = do_merge(&mut result_tree, new_path);
28
29 if rev_limit > 0 {
31 let _stemmed = stem(&mut result_tree, rev_limit);
32 }
33
34 (result_tree, merge_result)
35}
36
37fn do_merge(tree: &mut RevTree, new_path: &RevPath) -> MergeResult {
40 for existing in tree.iter_mut() {
42 let result = try_merge_path(existing, new_path);
43 if let Some(merge_result) = result {
44 return merge_result;
45 }
46 }
47
48 tree.push(new_path.clone());
50 MergeResult::NewBranch
51}
52
53fn try_merge_path(existing: &mut RevPath, new_path: &RevPath) -> Option<MergeResult> {
59 let overlap = find_overlap(existing, new_path);
61
62 match overlap {
63 None => None, Some(OverlapInfo {
65 existing_node_path,
66 new_remainder,
67 is_exact_match,
68 }) => {
69 if is_exact_match && new_remainder.is_empty() {
70 return Some(MergeResult::InternalNode);
72 }
73
74 let target = navigate_to_mut(&mut existing.tree, &existing_node_path);
76
77 if new_remainder.is_empty() {
78 Some(MergeResult::InternalNode)
80 } else {
81 let result = graft_nodes(target, &new_remainder);
83 Some(result)
84 }
85 }
86 }
87}
88
89struct OverlapInfo {
90 existing_node_path: Vec<usize>,
92 new_remainder: Vec<RevNode>,
94 is_exact_match: bool,
96}
97
98fn find_overlap(existing: &RevPath, new_path: &RevPath) -> Option<OverlapInfo> {
100 let new_chain = flatten_chain(&new_path.tree, new_path.pos);
102
103 for (i, (new_pos, new_hash)) in new_chain.iter().enumerate() {
105 if let Some(path_indices) = find_node_path(&existing.tree, existing.pos, *new_pos, new_hash)
106 {
107 let remainder = build_remainder_from_chain(&new_chain, i, &new_path.tree, new_path.pos);
109
110 return Some(OverlapInfo {
111 existing_node_path: path_indices,
112 new_remainder: remainder,
113 is_exact_match: true,
114 });
115 }
116 }
117
118 let existing_leaves = collect_leaf_positions(existing);
122 let new_root_pos = new_path.pos;
123 let new_root_hash = &new_path.tree.hash;
124
125 for (leaf_pos, leaf_hash, leaf_path) in &existing_leaves {
127 if *leaf_pos == new_root_pos && leaf_hash == new_root_hash {
129 let remainder = if new_path.tree.children.is_empty() {
130 vec![]
131 } else {
132 new_path.tree.children.clone()
133 };
134 return Some(OverlapInfo {
135 existing_node_path: leaf_path.clone(),
136 new_remainder: remainder,
137 is_exact_match: true,
138 });
139 }
140 }
141
142 None
143}
144
145fn flatten_chain(node: &RevNode, start_pos: u64) -> Vec<(u64, String)> {
147 let mut chain = Vec::new();
148 fn walk(node: &RevNode, pos: u64, chain: &mut Vec<(u64, String)>) {
149 chain.push((pos, node.hash.clone()));
150 if let Some(child) = node.children.first() {
152 walk(child, pos + 1, chain);
153 }
154 }
155 walk(node, start_pos, &mut chain);
156 chain
157}
158
159fn find_node_path(
161 node: &RevNode,
162 current_pos: u64,
163 target_pos: u64,
164 target_hash: &str,
165) -> Option<Vec<usize>> {
166 if current_pos == target_pos && node.hash == target_hash {
167 return Some(vec![]);
168 }
169
170 for (i, child) in node.children.iter().enumerate() {
171 if let Some(mut path) = find_node_path(child, current_pos + 1, target_pos, target_hash) {
172 path.insert(0, i);
173 return Some(path);
174 }
175 }
176
177 None
178}
179
180fn collect_leaf_positions(path: &RevPath) -> Vec<(u64, String, Vec<usize>)> {
182 let mut leaves = Vec::new();
183 fn walk(
184 node: &RevNode,
185 pos: u64,
186 current_path: &mut Vec<usize>,
187 leaves: &mut Vec<(u64, String, Vec<usize>)>,
188 ) {
189 if node.children.is_empty() {
190 leaves.push((pos, node.hash.clone(), current_path.clone()));
191 }
192 for (i, child) in node.children.iter().enumerate() {
193 current_path.push(i);
194 walk(child, pos + 1, current_path, leaves);
195 current_path.pop();
196 }
197 }
198 let mut current = Vec::new();
199 walk(&path.tree, path.pos, &mut current, &mut leaves);
200 leaves
201}
202
203fn build_remainder_from_chain(
205 _chain: &[(u64, String)],
206 overlap_index: usize,
207 original_tree: &RevNode,
208 _original_pos: u64,
209) -> Vec<RevNode> {
210 let depth_to_overlap = overlap_index;
213
214 fn get_subtree_at_depth(node: &RevNode, depth: usize) -> Option<&RevNode> {
215 if depth == 0 {
216 return Some(node);
217 }
218 if let Some(child) = node.children.first() {
219 get_subtree_at_depth(child, depth - 1)
220 } else {
221 None
222 }
223 }
224
225 if let Some(overlap_node) = get_subtree_at_depth(original_tree, depth_to_overlap) {
226 overlap_node.children.clone()
227 } else {
228 vec![]
229 }
230}
231
232fn navigate_to_mut<'a>(node: &'a mut RevNode, path: &[usize]) -> &'a mut RevNode {
234 let mut current = node;
235 for &idx in path {
236 current = &mut current.children[idx];
237 }
238 current
239}
240
241fn graft_nodes(target: &mut RevNode, new_nodes: &[RevNode]) -> MergeResult {
244 let mut is_new_branch = false;
245 let mut added_anything = false;
246
247 for new_node in new_nodes {
248 let existing_child = target
250 .children
251 .iter_mut()
252 .find(|c| c.hash == new_node.hash);
253
254 match existing_child {
255 Some(existing) => {
256 for grandchild in &new_node.children {
258 let sub_nodes = vec![grandchild.clone()];
259 let result = graft_nodes(existing, &sub_nodes);
260 match result {
261 MergeResult::NewBranch => {
262 is_new_branch = true;
263 added_anything = true;
264 }
265 MergeResult::NewLeaf => {
266 added_anything = true;
267 }
268 MergeResult::InternalNode => {}
269 }
270 }
271 }
272 None => {
273 if !target.children.is_empty() {
275 is_new_branch = true;
276 }
277 target.children.push(new_node.clone());
278 added_anything = true;
279 }
280 }
281 }
282
283 if !added_anything {
284 MergeResult::InternalNode
285 } else if is_new_branch {
286 MergeResult::NewBranch
287 } else {
288 MergeResult::NewLeaf
289 }
290}
291
292pub fn winning_rev(tree: &RevTree) -> Option<Revision> {
305 let leaves = collect_leaves(tree);
306 leaves.first().map(|l| Revision::new(l.pos, l.hash.clone()))
307}
308
309pub fn is_deleted(tree: &RevTree) -> bool {
311 collect_leaves(tree)
312 .first()
313 .map(|l| l.deleted)
314 .unwrap_or(false)
315}
316
317pub fn collect_conflicts(tree: &RevTree) -> Vec<Revision> {
319 let leaves = collect_leaves(tree);
320 leaves
321 .iter()
322 .skip(1) .filter(|l| !l.deleted)
324 .map(|l| Revision::new(l.pos, l.hash.clone()))
325 .collect()
326}
327
328pub fn stem(tree: &mut RevTree, depth: u64) -> Vec<String> {
335 let mut stemmed = Vec::new();
336
337 for path in tree.iter_mut() {
338 let s = stem_path(path, depth);
339 stemmed.extend(s);
340 }
341
342 tree.retain(|p| !is_empty_node(&p.tree));
344
345 stemmed
346}
347
348fn stem_path(path: &mut RevPath, depth: u64) -> Vec<String> {
350 let mut stemmed = Vec::new();
351
352 fn max_depth(node: &RevNode) -> u64 {
354 if node.children.is_empty() {
355 return 0;
356 }
357 node.children
358 .iter()
359 .map(|c| 1 + max_depth(c))
360 .max()
361 .unwrap_or(0)
362 }
363
364 let tree_depth = max_depth(&path.tree);
365
366 if tree_depth < depth {
367 return stemmed; }
369
370 let levels_to_remove = tree_depth - depth + 1;
373
374 for _ in 0..levels_to_remove {
375 if path.tree.children.len() <= 1 {
376 stemmed.push(path.tree.hash.clone());
377 if let Some(child) = path.tree.children.pop() {
378 path.tree = child;
379 path.pos += 1;
380 } else {
381 break;
383 }
384 } else {
385 break;
387 }
388 }
389
390 stemmed
391}
392
393fn is_empty_node(node: &RevNode) -> bool {
394 node.hash.is_empty() && node.children.is_empty()
395}
396
397pub fn latest_rev(tree: &RevTree, pos: u64, hash: &str) -> Option<Revision> {
405 for path in tree {
406 if let Some(rev) = find_latest_in_node(&path.tree, path.pos, pos, hash) {
407 return Some(rev);
408 }
409 }
410 None
411}
412
413fn find_latest_in_node(
414 node: &RevNode,
415 current_pos: u64,
416 target_pos: u64,
417 target_hash: &str,
418) -> Option<Revision> {
419 if current_pos == target_pos && node.hash == target_hash {
420 if node.status == RevStatus::Available {
423 return Some(Revision::new(current_pos, node.hash.clone()));
424 }
425 return find_first_available_leaf(node, current_pos);
426 }
427
428 for child in &node.children {
429 if let Some(rev) = find_latest_in_node(child, current_pos + 1, target_pos, target_hash) {
430 return Some(rev);
431 }
432 }
433
434 None
435}
436
437fn find_first_available_leaf(node: &RevNode, pos: u64) -> Option<Revision> {
438 if node.children.is_empty() {
439 if node.status == RevStatus::Available {
440 return Some(Revision::new(pos, node.hash.clone()));
441 }
442 return None;
443 }
444
445 for child in &node.children {
446 if let Some(rev) = find_first_available_leaf(child, pos + 1) {
447 return Some(rev);
448 }
449 }
450 None
451}
452
453#[cfg(test)]
458mod tests {
459 use super::*;
460 use crate::rev_tree::{build_path_from_revs, NodeOpts, RevNode, RevPath};
461
462 fn leaf(hash: &str) -> RevNode {
463 RevNode {
464 hash: hash.into(),
465 status: RevStatus::Available,
466 opts: NodeOpts::default(),
467 children: vec![],
468 }
469 }
470
471 fn deleted_leaf(hash: &str) -> RevNode {
472 RevNode {
473 hash: hash.into(),
474 status: RevStatus::Available,
475 opts: NodeOpts { deleted: true },
476 children: vec![],
477 }
478 }
479
480 fn node(hash: &str, children: Vec<RevNode>) -> RevNode {
481 RevNode {
482 hash: hash.into(),
483 status: RevStatus::Available,
484 opts: NodeOpts::default(),
485 children,
486 }
487 }
488
489 fn simple_tree() -> RevTree {
490 vec![RevPath {
492 pos: 1,
493 tree: node("a", vec![node("b", vec![leaf("c")])]),
494 }]
495 }
496
497 #[test]
500 fn winning_rev_simple() {
501 let tree = simple_tree();
502 let winner = winning_rev(&tree).unwrap();
503 assert_eq!(winner.pos, 3);
504 assert_eq!(winner.hash, "c");
505 }
506
507 #[test]
508 fn winning_rev_conflict_picks_higher_hash() {
509 let tree = vec![RevPath {
512 pos: 1,
513 tree: node("a", vec![leaf("b"), leaf("c")]),
514 }];
515 let winner = winning_rev(&tree).unwrap();
516 assert_eq!(winner.hash, "c"); }
518
519 #[test]
520 fn winning_rev_conflict_prefers_longer() {
521 let tree = vec![RevPath {
524 pos: 1,
525 tree: node("a", vec![node("b", vec![leaf("d")]), leaf("c")]),
526 }];
527 let winner = winning_rev(&tree).unwrap();
528 assert_eq!(winner.pos, 3);
529 assert_eq!(winner.hash, "d"); }
531
532 #[test]
533 fn winning_rev_non_deleted_beats_deleted() {
534 let tree = vec![RevPath {
537 pos: 1,
538 tree: node("a", vec![leaf("b"), deleted_leaf("z")]),
539 }];
540 let winner = winning_rev(&tree).unwrap();
541 assert_eq!(winner.hash, "b");
542 }
543
544 #[test]
547 fn no_conflicts_on_linear() {
548 let tree = simple_tree();
549 assert!(collect_conflicts(&tree).is_empty());
550 }
551
552 #[test]
553 fn conflicts_on_branches() {
554 let tree = vec![RevPath {
556 pos: 1,
557 tree: node("a", vec![leaf("b"), leaf("c")]),
558 }];
559 let conflicts = collect_conflicts(&tree);
560 assert_eq!(conflicts.len(), 1);
561 assert_eq!(conflicts[0].hash, "b"); }
563
564 #[test]
567 fn is_deleted_false_for_normal() {
568 assert!(!is_deleted(&simple_tree()));
569 }
570
571 #[test]
572 fn is_deleted_true_when_winner_deleted() {
573 let tree = vec![RevPath {
574 pos: 1,
575 tree: deleted_leaf("a"),
576 }];
577 assert!(is_deleted(&tree));
578 }
579
580 #[test]
583 fn merge_extends_linear_chain() {
584 let tree = vec![RevPath {
586 pos: 1,
587 tree: node("a", vec![leaf("b")]),
588 }];
589
590 let new_path = build_path_from_revs(
592 3,
593 &["c".into(), "b".into()],
594 NodeOpts::default(),
595 RevStatus::Available,
596 );
597
598 let (merged, result) = merge_tree(&tree, &new_path, 1000);
599 assert_eq!(result, MergeResult::NewLeaf);
600
601 let winner = winning_rev(&merged).unwrap();
602 assert_eq!(winner.pos, 3);
603 assert_eq!(winner.hash, "c");
604 }
605
606 #[test]
607 fn merge_creates_conflict_branch() {
608 let tree = vec![RevPath {
610 pos: 1,
611 tree: node("a", vec![leaf("b")]),
612 }];
613
614 let new_path = build_path_from_revs(
616 2,
617 &["c".into(), "a".into()],
618 NodeOpts::default(),
619 RevStatus::Available,
620 );
621
622 let (merged, result) = merge_tree(&tree, &new_path, 1000);
623 assert_eq!(result, MergeResult::NewBranch);
624
625 let conflicts = collect_conflicts(&merged);
626 assert_eq!(conflicts.len(), 1);
627 }
628
629 #[test]
630 fn merge_duplicate_is_internal_node() {
631 let tree = vec![RevPath {
633 pos: 1,
634 tree: node("a", vec![leaf("b")]),
635 }];
636
637 let new_path = build_path_from_revs(
639 2,
640 &["b".into(), "a".into()],
641 NodeOpts::default(),
642 RevStatus::Available,
643 );
644
645 let (_merged, result) = merge_tree(&tree, &new_path, 1000);
646 assert_eq!(result, MergeResult::InternalNode);
647 }
648
649 #[test]
650 fn merge_disjoint_creates_new_root() {
651 let tree = vec![RevPath {
653 pos: 1,
654 tree: node("a", vec![leaf("b")]),
655 }];
656
657 let new_path = build_path_from_revs(
659 2,
660 &["y".into(), "x".into()],
661 NodeOpts::default(),
662 RevStatus::Available,
663 );
664
665 let (merged, result) = merge_tree(&tree, &new_path, 1000);
666 assert_eq!(result, MergeResult::NewBranch);
667 assert_eq!(merged.len(), 2); }
669
670 #[test]
673 fn stem_prunes_old_revisions() {
674 let mut tree = vec![RevPath {
676 pos: 1,
677 tree: node(
678 "a",
679 vec![node("b", vec![node("c", vec![node("d", vec![leaf("e")])])])],
680 ),
681 }];
682
683 let stemmed = stem(&mut tree, 3);
684 assert!(!stemmed.is_empty());
685
686 assert!(tree[0].pos > 1);
688
689 let leaves = collect_leaves(&tree);
691 assert_eq!(leaves[0].hash, "e");
692 }
693
694 #[test]
695 fn stem_stops_at_branch_point() {
696 let mut tree = vec![RevPath {
699 pos: 1,
700 tree: node("a", vec![node("b", vec![leaf("c"), leaf("d")])]),
701 }];
702
703 let stemmed = stem(&mut tree, 1);
705 assert!(stemmed.len() <= 1);
709 }
710
711 #[test]
712 fn stem_short_tree_unchanged() {
713 let mut tree = vec![RevPath {
715 pos: 1,
716 tree: node("a", vec![leaf("b")]),
717 }];
718
719 let stemmed = stem(&mut tree, 3);
720 assert!(stemmed.is_empty());
721 assert_eq!(tree[0].pos, 1);
722 }
723
724 #[test]
727 fn latest_rev_finds_available_node() {
728 let tree = simple_tree(); let rev = latest_rev(&tree, 3, "c").unwrap();
730 assert_eq!(rev.pos, 3);
731 assert_eq!(rev.hash, "c");
732 }
733
734 #[test]
735 fn latest_rev_walks_to_leaf_from_missing() {
736 let tree = vec![RevPath {
738 pos: 1,
739 tree: RevNode {
740 hash: "a".into(),
741 status: RevStatus::Missing,
742 opts: NodeOpts::default(),
743 children: vec![leaf("b")],
744 },
745 }];
746 let rev = latest_rev(&tree, 1, "a").unwrap();
747 assert_eq!(rev.pos, 2);
748 assert_eq!(rev.hash, "b");
749 }
750
751 #[test]
752 fn latest_rev_none_for_nonexistent() {
753 let tree = simple_tree();
754 assert!(latest_rev(&tree, 5, "zzz").is_none());
755 }
756
757 #[test]
758 fn latest_rev_finds_internal_node() {
759 let tree = simple_tree(); let rev = latest_rev(&tree, 2, "b").unwrap();
761 assert_eq!(rev.pos, 2);
762 assert_eq!(rev.hash, "b");
763 }
764
765 #[test]
766 fn latest_rev_on_empty_tree() {
767 let tree: RevTree = vec![];
768 assert!(latest_rev(&tree, 1, "a").is_none());
769 }
770
771 #[test]
774 fn merge_exact_root_match_no_children() {
775 let tree = vec![RevPath {
777 pos: 1,
778 tree: leaf("a"),
779 }];
780
781 let new_path = RevPath {
783 pos: 1,
784 tree: leaf("a"),
785 };
786
787 let (_, result) = merge_tree(&tree, &new_path, 1000);
788 assert_eq!(result, MergeResult::InternalNode);
789 }
790
791 #[test]
792 fn merge_same_branch_extends_deeper() {
793 let tree = simple_tree();
795
796 let new_path = build_path_from_revs(
798 4,
799 &["d".into(), "c".into(), "b".into(), "a".into()],
800 NodeOpts::default(),
801 RevStatus::Available,
802 );
803
804 let (merged, result) = merge_tree(&tree, &new_path, 1000);
805 assert_eq!(result, MergeResult::NewLeaf);
806 let winner = winning_rev(&merged).unwrap();
807 assert_eq!(winner.pos, 4);
808 assert_eq!(winner.hash, "d");
809 }
810
811 #[test]
812 fn winning_rev_empty_tree() {
813 let tree: RevTree = vec![];
814 assert!(winning_rev(&tree).is_none());
815 }
816
817 #[test]
818 fn is_deleted_empty_tree() {
819 let tree: RevTree = vec![];
820 assert!(!is_deleted(&tree));
821 }
822
823 #[test]
824 fn collect_conflicts_deleted_leaves_excluded() {
825 let tree = vec![RevPath {
828 pos: 1,
829 tree: node("a", vec![leaf("b"), deleted_leaf("c")]),
830 }];
831 let conflicts = collect_conflicts(&tree);
832 assert!(conflicts.is_empty());
833 }
834}