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
324fn max_depth(node: &RevNode) -> u64 {
330 if node.children.is_empty() {
331 return 0;
332 }
333 node.children
334 .iter()
335 .map(|c| 1 + max_depth(c))
336 .max()
337 .unwrap_or(0)
338}
339
340pub fn stem(tree: &mut RevTree, depth: u64) -> Vec<String> {
348 let mut stemmed = Vec::new();
349 let mut new_roots: RevTree = Vec::new();
350 for path in tree.drain(..) {
351 new_roots.extend(stem_path(path, depth, &mut stemmed));
352 }
353 new_roots.retain(|p| !is_empty_node(&p.tree));
355 *tree = new_roots;
356 stemmed
357}
358
359fn stem_path(path: RevPath, depth: u64, stemmed: &mut Vec<String>) -> Vec<RevPath> {
362 let mut pos = path.pos;
363 let mut node = path.tree;
364 loop {
365 if max_depth(&node) < depth {
366 return vec![RevPath { pos, tree: node }];
368 }
369 if node.children.len() <= 1 {
370 stemmed.push(node.hash.clone());
372 match node.children.pop() {
373 Some(child) => {
374 node = child;
375 pos += 1;
376 }
377 None => return vec![], }
379 } else {
380 stemmed.push(node.hash.clone());
383 let children = std::mem::take(&mut node.children);
384 let mut out = Vec::new();
385 for child in children {
386 out.extend(stem_path(
387 RevPath {
388 pos: pos + 1,
389 tree: child,
390 },
391 depth,
392 stemmed,
393 ));
394 }
395 return out;
396 }
397 }
398}
399
400fn is_empty_node(node: &RevNode) -> bool {
401 node.hash.is_empty() && node.children.is_empty()
402}
403
404pub fn latest_leaf(tree: &RevTree, pos: u64, hash: &str) -> Option<Revision> {
411 fn find_node<'a>(
412 node: &'a RevNode,
413 cur: u64,
414 tpos: u64,
415 thash: &str,
416 ) -> Option<(&'a RevNode, u64)> {
417 if cur == tpos && node.hash == thash {
418 return Some((node, cur));
419 }
420 for c in &node.children {
421 if let Some(found) = find_node(c, cur + 1, tpos, thash) {
422 return Some(found);
423 }
424 }
425 None
426 }
427
428 fn collect(node: &RevNode, pos: u64, out: &mut Vec<(u64, String, bool)>) {
429 if node.children.is_empty() {
430 out.push((pos, node.hash.clone(), node.opts.deleted));
431 } else {
432 for c in &node.children {
433 collect(c, pos + 1, out);
434 }
435 }
436 }
437
438 for path in tree {
439 if let Some((node, node_pos)) = find_node(&path.tree, path.pos, pos, hash) {
440 let mut leaves: Vec<(u64, String, bool)> = Vec::new();
441 collect(node, node_pos, &mut leaves);
442 leaves.sort_by(|a, b| {
444 a.2.cmp(&b.2)
445 .then_with(|| b.0.cmp(&a.0))
446 .then_with(|| b.1.cmp(&a.1))
447 });
448 return leaves
449 .into_iter()
450 .next()
451 .map(|(p, h, _)| Revision::new(p, h));
452 }
453 }
454 None
455}
456
457pub fn latest_rev(tree: &RevTree, pos: u64, hash: &str) -> Option<Revision> {
465 for path in tree {
466 if let Some(rev) = find_latest_in_node(&path.tree, path.pos, pos, hash) {
467 return Some(rev);
468 }
469 }
470 None
471}
472
473fn find_latest_in_node(
474 node: &RevNode,
475 current_pos: u64,
476 target_pos: u64,
477 target_hash: &str,
478) -> Option<Revision> {
479 if current_pos == target_pos && node.hash == target_hash {
480 if node.status == RevStatus::Available {
483 return Some(Revision::new(current_pos, node.hash.clone()));
484 }
485 return find_first_available_leaf(node, current_pos);
486 }
487
488 for child in &node.children {
489 if let Some(rev) = find_latest_in_node(child, current_pos + 1, target_pos, target_hash) {
490 return Some(rev);
491 }
492 }
493
494 None
495}
496
497fn find_first_available_leaf(node: &RevNode, pos: u64) -> Option<Revision> {
498 if node.children.is_empty() {
499 if node.status == RevStatus::Available {
500 return Some(Revision::new(pos, node.hash.clone()));
501 }
502 return None;
503 }
504
505 for child in &node.children {
506 if let Some(rev) = find_first_available_leaf(child, pos + 1) {
507 return Some(rev);
508 }
509 }
510 None
511}
512
513#[cfg(test)]
518mod tests {
519 use super::*;
520 use crate::rev_tree::{NodeOpts, RevNode, RevPath, build_path_from_revs};
521
522 fn leaf(hash: &str) -> RevNode {
523 RevNode {
524 hash: hash.into(),
525 status: RevStatus::Available,
526 opts: NodeOpts::default(),
527 children: vec![],
528 }
529 }
530
531 fn deleted_leaf(hash: &str) -> RevNode {
532 RevNode {
533 hash: hash.into(),
534 status: RevStatus::Available,
535 opts: NodeOpts { deleted: true },
536 children: vec![],
537 }
538 }
539
540 fn node(hash: &str, children: Vec<RevNode>) -> RevNode {
541 RevNode {
542 hash: hash.into(),
543 status: RevStatus::Available,
544 opts: NodeOpts::default(),
545 children,
546 }
547 }
548
549 fn simple_tree() -> RevTree {
550 vec![RevPath {
552 pos: 1,
553 tree: node("a", vec![node("b", vec![leaf("c")])]),
554 }]
555 }
556
557 #[test]
560 fn winning_rev_simple() {
561 let tree = simple_tree();
562 let winner = winning_rev(&tree).unwrap();
563 assert_eq!(winner.pos, 3);
564 assert_eq!(winner.hash, "c");
565 }
566
567 #[test]
568 fn winning_rev_conflict_picks_higher_hash() {
569 let tree = vec![RevPath {
572 pos: 1,
573 tree: node("a", vec![leaf("b"), leaf("c")]),
574 }];
575 let winner = winning_rev(&tree).unwrap();
576 assert_eq!(winner.hash, "c"); }
578
579 #[test]
580 fn winning_rev_conflict_prefers_longer() {
581 let tree = vec![RevPath {
584 pos: 1,
585 tree: node("a", vec![node("b", vec![leaf("d")]), leaf("c")]),
586 }];
587 let winner = winning_rev(&tree).unwrap();
588 assert_eq!(winner.pos, 3);
589 assert_eq!(winner.hash, "d"); }
591
592 #[test]
593 fn winning_rev_non_deleted_beats_deleted() {
594 let tree = vec![RevPath {
597 pos: 1,
598 tree: node("a", vec![leaf("b"), deleted_leaf("z")]),
599 }];
600 let winner = winning_rev(&tree).unwrap();
601 assert_eq!(winner.hash, "b");
602 }
603
604 #[test]
607 fn no_conflicts_on_linear() {
608 let tree = simple_tree();
609 assert!(collect_conflicts(&tree).is_empty());
610 }
611
612 #[test]
613 fn conflicts_on_branches() {
614 let tree = vec![RevPath {
616 pos: 1,
617 tree: node("a", vec![leaf("b"), leaf("c")]),
618 }];
619 let conflicts = collect_conflicts(&tree);
620 assert_eq!(conflicts.len(), 1);
621 assert_eq!(conflicts[0].hash, "b"); }
623
624 #[test]
627 fn is_deleted_false_for_normal() {
628 assert!(!is_deleted(&simple_tree()));
629 }
630
631 #[test]
632 fn is_deleted_true_when_winner_deleted() {
633 let tree = vec![RevPath {
634 pos: 1,
635 tree: deleted_leaf("a"),
636 }];
637 assert!(is_deleted(&tree));
638 }
639
640 #[test]
643 fn merge_extends_linear_chain() {
644 let tree = vec![RevPath {
646 pos: 1,
647 tree: node("a", vec![leaf("b")]),
648 }];
649
650 let new_path = build_path_from_revs(
652 3,
653 &["c".into(), "b".into()],
654 NodeOpts::default(),
655 RevStatus::Available,
656 );
657
658 let (merged, result) = merge_tree(&tree, &new_path, 1000);
659 assert_eq!(result, MergeResult::NewLeaf);
660
661 let winner = winning_rev(&merged).unwrap();
662 assert_eq!(winner.pos, 3);
663 assert_eq!(winner.hash, "c");
664 }
665
666 #[test]
667 fn merge_creates_conflict_branch() {
668 let tree = vec![RevPath {
670 pos: 1,
671 tree: node("a", vec![leaf("b")]),
672 }];
673
674 let new_path = build_path_from_revs(
676 2,
677 &["c".into(), "a".into()],
678 NodeOpts::default(),
679 RevStatus::Available,
680 );
681
682 let (merged, result) = merge_tree(&tree, &new_path, 1000);
683 assert_eq!(result, MergeResult::NewBranch);
684
685 let conflicts = collect_conflicts(&merged);
686 assert_eq!(conflicts.len(), 1);
687 }
688
689 #[test]
690 fn merge_duplicate_is_internal_node() {
691 let tree = vec![RevPath {
693 pos: 1,
694 tree: node("a", vec![leaf("b")]),
695 }];
696
697 let new_path = build_path_from_revs(
699 2,
700 &["b".into(), "a".into()],
701 NodeOpts::default(),
702 RevStatus::Available,
703 );
704
705 let (_merged, result) = merge_tree(&tree, &new_path, 1000);
706 assert_eq!(result, MergeResult::InternalNode);
707 }
708
709 #[test]
710 fn merge_disjoint_creates_new_root() {
711 let tree = vec![RevPath {
713 pos: 1,
714 tree: node("a", vec![leaf("b")]),
715 }];
716
717 let new_path = build_path_from_revs(
719 2,
720 &["y".into(), "x".into()],
721 NodeOpts::default(),
722 RevStatus::Available,
723 );
724
725 let (merged, result) = merge_tree(&tree, &new_path, 1000);
726 assert_eq!(result, MergeResult::NewBranch);
727 assert_eq!(merged.len(), 2); }
729
730 #[test]
733 fn stem_prunes_old_revisions() {
734 let mut tree = vec![RevPath {
736 pos: 1,
737 tree: node(
738 "a",
739 vec![node("b", vec![node("c", vec![node("d", vec![leaf("e")])])])],
740 ),
741 }];
742
743 let stemmed = stem(&mut tree, 3);
744 assert!(!stemmed.is_empty());
745
746 assert!(tree[0].pos > 1);
748
749 let leaves = collect_leaves(&tree);
751 assert_eq!(leaves[0].hash, "e");
752 }
753
754 #[test]
755 fn stem_splits_at_branch_point() {
756 let mut tree = vec![RevPath {
759 pos: 1,
760 tree: node("a", vec![node("b", vec![leaf("c"), leaf("d")])]),
761 }];
762
763 let stemmed = stem(&mut tree, 1);
766 assert_eq!(stemmed.len(), 2); assert!(stemmed.contains(&"a".to_string()));
768 assert!(stemmed.contains(&"b".to_string()));
769
770 assert_eq!(tree.len(), 2); let leaves = collect_leaves(&tree);
772 let hashes: Vec<&str> = leaves.iter().map(|l| l.hash.as_str()).collect();
773 assert!(hashes.contains(&"c"));
774 assert!(hashes.contains(&"d"));
775 assert!(leaves.iter().all(|l| l.pos == 3));
776 }
777
778 #[test]
779 fn stem_prunes_deep_branch_above_cut_line() {
780 let mut tree = vec![RevPath {
785 pos: 1,
786 tree: node(
787 "a",
788 vec![
789 leaf("b"),
790 node("c", vec![node("d", vec![node("e", vec![leaf("f")])])]),
791 ],
792 ),
793 }];
794
795 let stemmed = stem(&mut tree, 2);
796 assert!(stemmed.contains(&"a".to_string()));
798 assert!(stemmed.contains(&"c".to_string()));
799 assert!(stemmed.contains(&"d".to_string()));
800
801 assert_eq!(tree.len(), 2);
803 for path in &tree {
804 assert!(max_depth(&path.tree) < 2, "every chain must fit the limit");
805 }
806 let winner = winning_rev(&tree).unwrap();
808 assert_eq!(winner.pos, 5);
809 assert_eq!(winner.hash, "f");
810 }
811
812 #[test]
813 fn stem_short_tree_unchanged() {
814 let mut tree = vec![RevPath {
816 pos: 1,
817 tree: node("a", vec![leaf("b")]),
818 }];
819
820 let stemmed = stem(&mut tree, 3);
821 assert!(stemmed.is_empty());
822 assert_eq!(tree[0].pos, 1);
823 }
824
825 #[test]
828 fn latest_rev_finds_available_node() {
829 let tree = simple_tree(); let rev = latest_rev(&tree, 3, "c").unwrap();
831 assert_eq!(rev.pos, 3);
832 assert_eq!(rev.hash, "c");
833 }
834
835 #[test]
836 fn latest_rev_walks_to_leaf_from_missing() {
837 let tree = vec![RevPath {
839 pos: 1,
840 tree: RevNode {
841 hash: "a".into(),
842 status: RevStatus::Missing,
843 opts: NodeOpts::default(),
844 children: vec![leaf("b")],
845 },
846 }];
847 let rev = latest_rev(&tree, 1, "a").unwrap();
848 assert_eq!(rev.pos, 2);
849 assert_eq!(rev.hash, "b");
850 }
851
852 #[test]
853 fn latest_rev_none_for_nonexistent() {
854 let tree = simple_tree();
855 assert!(latest_rev(&tree, 5, "zzz").is_none());
856 }
857
858 #[test]
859 fn latest_rev_finds_internal_node() {
860 let tree = simple_tree(); let rev = latest_rev(&tree, 2, "b").unwrap();
862 assert_eq!(rev.pos, 2);
863 assert_eq!(rev.hash, "b");
864 }
865
866 #[test]
867 fn latest_rev_on_empty_tree() {
868 let tree: RevTree = vec![];
869 assert!(latest_rev(&tree, 1, "a").is_none());
870 }
871
872 #[test]
873 fn latest_leaf_walks_linear_chain_to_tip() {
874 let tree = simple_tree();
876 let rev = latest_leaf(&tree, 1, "a").unwrap();
877 assert_eq!(rev.pos, 3);
878 assert_eq!(rev.hash, "c");
879 assert_eq!(latest_leaf(&tree, 3, "c").unwrap().hash, "c");
881 }
882
883 #[test]
884 fn latest_leaf_stays_on_requested_branch() {
885 let tree = vec![RevPath {
888 pos: 1,
889 tree: node(
890 "a",
891 vec![
892 node("b", vec![leaf("c")]),
893 node("z", vec![node("d", vec![leaf("e")])]),
894 ],
895 ),
896 }];
897 assert_eq!(latest_leaf(&tree, 2, "b").unwrap().to_string(), "3-c");
899 assert_eq!(latest_leaf(&tree, 2, "z").unwrap().to_string(), "4-e");
900 }
901
902 #[test]
905 fn merge_exact_root_match_no_children() {
906 let tree = vec![RevPath {
908 pos: 1,
909 tree: leaf("a"),
910 }];
911
912 let new_path = RevPath {
914 pos: 1,
915 tree: leaf("a"),
916 };
917
918 let (_, result) = merge_tree(&tree, &new_path, 1000);
919 assert_eq!(result, MergeResult::InternalNode);
920 }
921
922 #[test]
923 fn merge_same_branch_extends_deeper() {
924 let tree = simple_tree();
926
927 let new_path = build_path_from_revs(
929 4,
930 &["d".into(), "c".into(), "b".into(), "a".into()],
931 NodeOpts::default(),
932 RevStatus::Available,
933 );
934
935 let (merged, result) = merge_tree(&tree, &new_path, 1000);
936 assert_eq!(result, MergeResult::NewLeaf);
937 let winner = winning_rev(&merged).unwrap();
938 assert_eq!(winner.pos, 4);
939 assert_eq!(winner.hash, "d");
940 }
941
942 #[test]
943 fn winning_rev_empty_tree() {
944 let tree: RevTree = vec![];
945 assert!(winning_rev(&tree).is_none());
946 }
947
948 #[test]
949 fn is_deleted_empty_tree() {
950 let tree: RevTree = vec![];
951 assert!(!is_deleted(&tree));
952 }
953
954 #[test]
955 fn collect_conflicts_deleted_leaves_excluded() {
956 let tree = vec![RevPath {
959 pos: 1,
960 tree: node("a", vec![leaf("b"), deleted_leaf("c")]),
961 }];
962 let conflicts = collect_conflicts(&tree);
963 assert!(conflicts.is_empty());
964 }
965}