1use crate::document::Revision;
8use crate::rev_tree::{RevNode, RevPath, RevStatus, RevTree, collect_leaves};
9
10#[derive(Debug, Clone, PartialEq, Eq)]
12pub enum MergeResult {
13 NewLeaf,
15 NewBranch,
17 InternalNode,
19}
20
21pub fn merge_tree(tree: &RevTree, new_path: &RevPath, rev_limit: u64) -> (RevTree, MergeResult) {
25 let mut result_tree = tree.clone();
26 let merge_result = do_merge(&mut result_tree, new_path);
27
28 if rev_limit > 0 {
30 let _stemmed = stem(&mut result_tree, rev_limit);
31 }
32
33 (result_tree, merge_result)
34}
35
36fn do_merge(tree: &mut RevTree, new_path: &RevPath) -> MergeResult {
39 for existing in tree.iter_mut() {
41 let result = try_merge_path(existing, new_path);
42 if let Some(merge_result) = result {
43 return merge_result;
44 }
45 }
46
47 tree.push(new_path.clone());
49 MergeResult::NewBranch
50}
51
52fn try_merge_path(existing: &mut RevPath, new_path: &RevPath) -> Option<MergeResult> {
58 let overlap = find_overlap(existing, new_path);
60
61 match overlap {
62 None => None, Some(OverlapInfo {
64 existing_node_path,
65 new_remainder,
66 is_exact_match,
67 }) => {
68 if is_exact_match && new_remainder.is_empty() {
69 return Some(MergeResult::InternalNode);
71 }
72
73 let target = navigate_to_mut(&mut existing.tree, &existing_node_path);
75
76 if new_remainder.is_empty() {
77 Some(MergeResult::InternalNode)
79 } else {
80 let result = graft_nodes(target, &new_remainder);
82 Some(result)
83 }
84 }
85 }
86}
87
88struct OverlapInfo {
89 existing_node_path: Vec<usize>,
91 new_remainder: Vec<RevNode>,
93 is_exact_match: bool,
95}
96
97fn find_overlap(existing: &RevPath, new_path: &RevPath) -> Option<OverlapInfo> {
99 let new_chain = flatten_chain(&new_path.tree, new_path.pos);
101
102 for (i, (new_pos, new_hash)) in new_chain.iter().enumerate() {
104 if let Some(path_indices) = find_node_path(&existing.tree, existing.pos, *new_pos, new_hash)
105 {
106 let remainder = build_remainder_from_chain(&new_chain, i, &new_path.tree, new_path.pos);
108
109 return Some(OverlapInfo {
110 existing_node_path: path_indices,
111 new_remainder: remainder,
112 is_exact_match: true,
113 });
114 }
115 }
116
117 let existing_leaves = collect_leaf_positions(existing);
121 let new_root_pos = new_path.pos;
122 let new_root_hash = &new_path.tree.hash;
123
124 for (leaf_pos, leaf_hash, leaf_path) in &existing_leaves {
126 if *leaf_pos == new_root_pos && leaf_hash == new_root_hash {
128 let remainder = if new_path.tree.children.is_empty() {
129 vec![]
130 } else {
131 new_path.tree.children.clone()
132 };
133 return Some(OverlapInfo {
134 existing_node_path: leaf_path.clone(),
135 new_remainder: remainder,
136 is_exact_match: true,
137 });
138 }
139 }
140
141 None
142}
143
144fn flatten_chain(node: &RevNode, start_pos: u64) -> Vec<(u64, String)> {
146 let mut chain = Vec::new();
147 fn walk(node: &RevNode, pos: u64, chain: &mut Vec<(u64, String)>) {
148 chain.push((pos, node.hash.clone()));
149 if let Some(child) = node.children.first() {
151 walk(child, pos + 1, chain);
152 }
153 }
154 walk(node, start_pos, &mut chain);
155 chain
156}
157
158fn find_node_path(
160 node: &RevNode,
161 current_pos: u64,
162 target_pos: u64,
163 target_hash: &str,
164) -> Option<Vec<usize>> {
165 if current_pos == target_pos && node.hash == target_hash {
166 return Some(vec![]);
167 }
168
169 for (i, child) in node.children.iter().enumerate() {
170 if let Some(mut path) = find_node_path(child, current_pos + 1, target_pos, target_hash) {
171 path.insert(0, i);
172 return Some(path);
173 }
174 }
175
176 None
177}
178
179fn collect_leaf_positions(path: &RevPath) -> Vec<(u64, String, Vec<usize>)> {
181 let mut leaves = Vec::new();
182 fn walk(
183 node: &RevNode,
184 pos: u64,
185 current_path: &mut Vec<usize>,
186 leaves: &mut Vec<(u64, String, Vec<usize>)>,
187 ) {
188 if node.children.is_empty() {
189 leaves.push((pos, node.hash.clone(), current_path.clone()));
190 }
191 for (i, child) in node.children.iter().enumerate() {
192 current_path.push(i);
193 walk(child, pos + 1, current_path, leaves);
194 current_path.pop();
195 }
196 }
197 let mut current = Vec::new();
198 walk(&path.tree, path.pos, &mut current, &mut leaves);
199 leaves
200}
201
202fn build_remainder_from_chain(
204 _chain: &[(u64, String)],
205 overlap_index: usize,
206 original_tree: &RevNode,
207 _original_pos: u64,
208) -> Vec<RevNode> {
209 let depth_to_overlap = overlap_index;
212
213 fn get_subtree_at_depth(node: &RevNode, depth: usize) -> Option<&RevNode> {
214 if depth == 0 {
215 return Some(node);
216 }
217 if let Some(child) = node.children.first() {
218 get_subtree_at_depth(child, depth - 1)
219 } else {
220 None
221 }
222 }
223
224 if let Some(overlap_node) = get_subtree_at_depth(original_tree, depth_to_overlap) {
225 overlap_node.children.clone()
226 } else {
227 vec![]
228 }
229}
230
231fn navigate_to_mut<'a>(node: &'a mut RevNode, path: &[usize]) -> &'a mut RevNode {
233 let mut current = node;
234 for &idx in path {
235 current = &mut current.children[idx];
236 }
237 current
238}
239
240fn graft_nodes(target: &mut RevNode, new_nodes: &[RevNode]) -> MergeResult {
243 let mut is_new_branch = false;
244 let mut added_anything = false;
245
246 for new_node in new_nodes {
247 let existing_child = target.children.iter_mut().find(|c| c.hash == new_node.hash);
249
250 match existing_child {
251 Some(existing) => {
252 for grandchild in &new_node.children {
254 let sub_nodes = vec![grandchild.clone()];
255 let result = graft_nodes(existing, &sub_nodes);
256 match result {
257 MergeResult::NewBranch => {
258 is_new_branch = true;
259 added_anything = true;
260 }
261 MergeResult::NewLeaf => {
262 added_anything = true;
263 }
264 MergeResult::InternalNode => {}
265 }
266 }
267 }
268 None => {
269 if !target.children.is_empty() {
271 is_new_branch = true;
272 }
273 target.children.push(new_node.clone());
274 added_anything = true;
275 }
276 }
277 }
278
279 if !added_anything {
280 MergeResult::InternalNode
281 } else if is_new_branch {
282 MergeResult::NewBranch
283 } else {
284 MergeResult::NewLeaf
285 }
286}
287
288pub fn winning_rev(tree: &RevTree) -> Option<Revision> {
301 let leaves = collect_leaves(tree);
302 leaves.first().map(|l| Revision::new(l.pos, l.hash.clone()))
303}
304
305pub fn is_deleted(tree: &RevTree) -> bool {
307 collect_leaves(tree)
308 .first()
309 .map(|l| l.deleted)
310 .unwrap_or(false)
311}
312
313pub fn collect_conflicts(tree: &RevTree) -> Vec<Revision> {
315 let leaves = collect_leaves(tree);
316 leaves
317 .iter()
318 .skip(1) .filter(|l| !l.deleted)
320 .map(|l| Revision::new(l.pos, l.hash.clone()))
321 .collect()
322}
323
324pub fn stem(tree: &mut RevTree, depth: u64) -> Vec<String> {
331 let mut stemmed = Vec::new();
332
333 for path in tree.iter_mut() {
334 let s = stem_path(path, depth);
335 stemmed.extend(s);
336 }
337
338 tree.retain(|p| !is_empty_node(&p.tree));
340
341 stemmed
342}
343
344fn stem_path(path: &mut RevPath, depth: u64) -> Vec<String> {
346 let mut stemmed = Vec::new();
347
348 fn max_depth(node: &RevNode) -> u64 {
350 if node.children.is_empty() {
351 return 0;
352 }
353 node.children
354 .iter()
355 .map(|c| 1 + max_depth(c))
356 .max()
357 .unwrap_or(0)
358 }
359
360 let tree_depth = max_depth(&path.tree);
361
362 if tree_depth < depth {
363 return stemmed; }
365
366 let levels_to_remove = tree_depth - depth + 1;
369
370 for _ in 0..levels_to_remove {
371 if path.tree.children.len() <= 1 {
372 stemmed.push(path.tree.hash.clone());
373 if let Some(child) = path.tree.children.pop() {
374 path.tree = child;
375 path.pos += 1;
376 } else {
377 break;
379 }
380 } else {
381 break;
383 }
384 }
385
386 stemmed
387}
388
389fn is_empty_node(node: &RevNode) -> bool {
390 node.hash.is_empty() && node.children.is_empty()
391}
392
393pub fn latest_rev(tree: &RevTree, pos: u64, hash: &str) -> Option<Revision> {
401 for path in tree {
402 if let Some(rev) = find_latest_in_node(&path.tree, path.pos, pos, hash) {
403 return Some(rev);
404 }
405 }
406 None
407}
408
409fn find_latest_in_node(
410 node: &RevNode,
411 current_pos: u64,
412 target_pos: u64,
413 target_hash: &str,
414) -> Option<Revision> {
415 if current_pos == target_pos && node.hash == target_hash {
416 if node.status == RevStatus::Available {
419 return Some(Revision::new(current_pos, node.hash.clone()));
420 }
421 return find_first_available_leaf(node, current_pos);
422 }
423
424 for child in &node.children {
425 if let Some(rev) = find_latest_in_node(child, current_pos + 1, target_pos, target_hash) {
426 return Some(rev);
427 }
428 }
429
430 None
431}
432
433fn find_first_available_leaf(node: &RevNode, pos: u64) -> Option<Revision> {
434 if node.children.is_empty() {
435 if node.status == RevStatus::Available {
436 return Some(Revision::new(pos, node.hash.clone()));
437 }
438 return None;
439 }
440
441 for child in &node.children {
442 if let Some(rev) = find_first_available_leaf(child, pos + 1) {
443 return Some(rev);
444 }
445 }
446 None
447}
448
449#[cfg(test)]
454mod tests {
455 use super::*;
456 use crate::rev_tree::{NodeOpts, RevNode, RevPath, build_path_from_revs};
457
458 fn leaf(hash: &str) -> RevNode {
459 RevNode {
460 hash: hash.into(),
461 status: RevStatus::Available,
462 opts: NodeOpts::default(),
463 children: vec![],
464 }
465 }
466
467 fn deleted_leaf(hash: &str) -> RevNode {
468 RevNode {
469 hash: hash.into(),
470 status: RevStatus::Available,
471 opts: NodeOpts { deleted: true },
472 children: vec![],
473 }
474 }
475
476 fn node(hash: &str, children: Vec<RevNode>) -> RevNode {
477 RevNode {
478 hash: hash.into(),
479 status: RevStatus::Available,
480 opts: NodeOpts::default(),
481 children,
482 }
483 }
484
485 fn simple_tree() -> RevTree {
486 vec![RevPath {
488 pos: 1,
489 tree: node("a", vec![node("b", vec![leaf("c")])]),
490 }]
491 }
492
493 #[test]
496 fn winning_rev_simple() {
497 let tree = simple_tree();
498 let winner = winning_rev(&tree).unwrap();
499 assert_eq!(winner.pos, 3);
500 assert_eq!(winner.hash, "c");
501 }
502
503 #[test]
504 fn winning_rev_conflict_picks_higher_hash() {
505 let tree = vec![RevPath {
508 pos: 1,
509 tree: node("a", vec![leaf("b"), leaf("c")]),
510 }];
511 let winner = winning_rev(&tree).unwrap();
512 assert_eq!(winner.hash, "c"); }
514
515 #[test]
516 fn winning_rev_conflict_prefers_longer() {
517 let tree = vec![RevPath {
520 pos: 1,
521 tree: node("a", vec![node("b", vec![leaf("d")]), leaf("c")]),
522 }];
523 let winner = winning_rev(&tree).unwrap();
524 assert_eq!(winner.pos, 3);
525 assert_eq!(winner.hash, "d"); }
527
528 #[test]
529 fn winning_rev_non_deleted_beats_deleted() {
530 let tree = vec![RevPath {
533 pos: 1,
534 tree: node("a", vec![leaf("b"), deleted_leaf("z")]),
535 }];
536 let winner = winning_rev(&tree).unwrap();
537 assert_eq!(winner.hash, "b");
538 }
539
540 #[test]
543 fn no_conflicts_on_linear() {
544 let tree = simple_tree();
545 assert!(collect_conflicts(&tree).is_empty());
546 }
547
548 #[test]
549 fn conflicts_on_branches() {
550 let tree = vec![RevPath {
552 pos: 1,
553 tree: node("a", vec![leaf("b"), leaf("c")]),
554 }];
555 let conflicts = collect_conflicts(&tree);
556 assert_eq!(conflicts.len(), 1);
557 assert_eq!(conflicts[0].hash, "b"); }
559
560 #[test]
563 fn is_deleted_false_for_normal() {
564 assert!(!is_deleted(&simple_tree()));
565 }
566
567 #[test]
568 fn is_deleted_true_when_winner_deleted() {
569 let tree = vec![RevPath {
570 pos: 1,
571 tree: deleted_leaf("a"),
572 }];
573 assert!(is_deleted(&tree));
574 }
575
576 #[test]
579 fn merge_extends_linear_chain() {
580 let tree = vec![RevPath {
582 pos: 1,
583 tree: node("a", vec![leaf("b")]),
584 }];
585
586 let new_path = build_path_from_revs(
588 3,
589 &["c".into(), "b".into()],
590 NodeOpts::default(),
591 RevStatus::Available,
592 );
593
594 let (merged, result) = merge_tree(&tree, &new_path, 1000);
595 assert_eq!(result, MergeResult::NewLeaf);
596
597 let winner = winning_rev(&merged).unwrap();
598 assert_eq!(winner.pos, 3);
599 assert_eq!(winner.hash, "c");
600 }
601
602 #[test]
603 fn merge_creates_conflict_branch() {
604 let tree = vec![RevPath {
606 pos: 1,
607 tree: node("a", vec![leaf("b")]),
608 }];
609
610 let new_path = build_path_from_revs(
612 2,
613 &["c".into(), "a".into()],
614 NodeOpts::default(),
615 RevStatus::Available,
616 );
617
618 let (merged, result) = merge_tree(&tree, &new_path, 1000);
619 assert_eq!(result, MergeResult::NewBranch);
620
621 let conflicts = collect_conflicts(&merged);
622 assert_eq!(conflicts.len(), 1);
623 }
624
625 #[test]
626 fn merge_duplicate_is_internal_node() {
627 let tree = vec![RevPath {
629 pos: 1,
630 tree: node("a", vec![leaf("b")]),
631 }];
632
633 let new_path = build_path_from_revs(
635 2,
636 &["b".into(), "a".into()],
637 NodeOpts::default(),
638 RevStatus::Available,
639 );
640
641 let (_merged, result) = merge_tree(&tree, &new_path, 1000);
642 assert_eq!(result, MergeResult::InternalNode);
643 }
644
645 #[test]
646 fn merge_disjoint_creates_new_root() {
647 let tree = vec![RevPath {
649 pos: 1,
650 tree: node("a", vec![leaf("b")]),
651 }];
652
653 let new_path = build_path_from_revs(
655 2,
656 &["y".into(), "x".into()],
657 NodeOpts::default(),
658 RevStatus::Available,
659 );
660
661 let (merged, result) = merge_tree(&tree, &new_path, 1000);
662 assert_eq!(result, MergeResult::NewBranch);
663 assert_eq!(merged.len(), 2); }
665
666 #[test]
669 fn stem_prunes_old_revisions() {
670 let mut tree = vec![RevPath {
672 pos: 1,
673 tree: node(
674 "a",
675 vec![node("b", vec![node("c", vec![node("d", vec![leaf("e")])])])],
676 ),
677 }];
678
679 let stemmed = stem(&mut tree, 3);
680 assert!(!stemmed.is_empty());
681
682 assert!(tree[0].pos > 1);
684
685 let leaves = collect_leaves(&tree);
687 assert_eq!(leaves[0].hash, "e");
688 }
689
690 #[test]
691 fn stem_stops_at_branch_point() {
692 let mut tree = vec![RevPath {
695 pos: 1,
696 tree: node("a", vec![node("b", vec![leaf("c"), leaf("d")])]),
697 }];
698
699 let stemmed = stem(&mut tree, 1);
701 assert!(stemmed.len() <= 1);
705 }
706
707 #[test]
708 fn stem_short_tree_unchanged() {
709 let mut tree = vec![RevPath {
711 pos: 1,
712 tree: node("a", vec![leaf("b")]),
713 }];
714
715 let stemmed = stem(&mut tree, 3);
716 assert!(stemmed.is_empty());
717 assert_eq!(tree[0].pos, 1);
718 }
719
720 #[test]
723 fn latest_rev_finds_available_node() {
724 let tree = simple_tree(); let rev = latest_rev(&tree, 3, "c").unwrap();
726 assert_eq!(rev.pos, 3);
727 assert_eq!(rev.hash, "c");
728 }
729
730 #[test]
731 fn latest_rev_walks_to_leaf_from_missing() {
732 let tree = vec![RevPath {
734 pos: 1,
735 tree: RevNode {
736 hash: "a".into(),
737 status: RevStatus::Missing,
738 opts: NodeOpts::default(),
739 children: vec![leaf("b")],
740 },
741 }];
742 let rev = latest_rev(&tree, 1, "a").unwrap();
743 assert_eq!(rev.pos, 2);
744 assert_eq!(rev.hash, "b");
745 }
746
747 #[test]
748 fn latest_rev_none_for_nonexistent() {
749 let tree = simple_tree();
750 assert!(latest_rev(&tree, 5, "zzz").is_none());
751 }
752
753 #[test]
754 fn latest_rev_finds_internal_node() {
755 let tree = simple_tree(); let rev = latest_rev(&tree, 2, "b").unwrap();
757 assert_eq!(rev.pos, 2);
758 assert_eq!(rev.hash, "b");
759 }
760
761 #[test]
762 fn latest_rev_on_empty_tree() {
763 let tree: RevTree = vec![];
764 assert!(latest_rev(&tree, 1, "a").is_none());
765 }
766
767 #[test]
770 fn merge_exact_root_match_no_children() {
771 let tree = vec![RevPath {
773 pos: 1,
774 tree: leaf("a"),
775 }];
776
777 let new_path = RevPath {
779 pos: 1,
780 tree: leaf("a"),
781 };
782
783 let (_, result) = merge_tree(&tree, &new_path, 1000);
784 assert_eq!(result, MergeResult::InternalNode);
785 }
786
787 #[test]
788 fn merge_same_branch_extends_deeper() {
789 let tree = simple_tree();
791
792 let new_path = build_path_from_revs(
794 4,
795 &["d".into(), "c".into(), "b".into(), "a".into()],
796 NodeOpts::default(),
797 RevStatus::Available,
798 );
799
800 let (merged, result) = merge_tree(&tree, &new_path, 1000);
801 assert_eq!(result, MergeResult::NewLeaf);
802 let winner = winning_rev(&merged).unwrap();
803 assert_eq!(winner.pos, 4);
804 assert_eq!(winner.hash, "d");
805 }
806
807 #[test]
808 fn winning_rev_empty_tree() {
809 let tree: RevTree = vec![];
810 assert!(winning_rev(&tree).is_none());
811 }
812
813 #[test]
814 fn is_deleted_empty_tree() {
815 let tree: RevTree = vec![];
816 assert!(!is_deleted(&tree));
817 }
818
819 #[test]
820 fn collect_conflicts_deleted_leaves_excluded() {
821 let tree = vec![RevPath {
824 pos: 1,
825 tree: node("a", vec![leaf("b"), deleted_leaf("c")]),
826 }];
827 let conflicts = collect_conflicts(&tree);
828 assert!(conflicts.is_empty());
829 }
830}