oxihuman_core/
merge_conflict_resolver.rs1#![allow(dead_code)]
4
5#[derive(Debug, Clone, PartialEq, Eq)]
9pub enum MergeResult {
10 Clean(Vec<String>),
11 Conflict {
12 ours: Vec<String>,
13 theirs: Vec<String>,
14 },
15}
16
17pub struct MergeConfig {
19 pub label_ours: String,
20 pub label_theirs: String,
21 pub auto_resolve: bool,
22}
23
24impl MergeConfig {
25 pub fn new(label_ours: &str, label_theirs: &str) -> Self {
26 MergeConfig {
27 label_ours: label_ours.to_string(),
28 label_theirs: label_theirs.to_string(),
29 auto_resolve: false,
30 }
31 }
32}
33
34impl Default for MergeConfig {
35 fn default() -> Self {
36 Self::new("ours", "theirs")
37 }
38}
39
40pub fn three_way_merge(base: &[&str], ours: &[&str], theirs: &[&str]) -> MergeResult {
42 if ours == base && theirs == base {
44 return MergeResult::Clean(base.iter().map(|s| s.to_string()).collect());
45 }
46 if theirs == base {
48 return MergeResult::Clean(ours.iter().map(|s| s.to_string()).collect());
49 }
50 if ours == base {
52 return MergeResult::Clean(theirs.iter().map(|s| s.to_string()).collect());
53 }
54 MergeResult::Conflict {
56 ours: ours.iter().map(|s| s.to_string()).collect(),
57 theirs: theirs.iter().map(|s| s.to_string()).collect(),
58 }
59}
60
61pub fn count_conflicts(results: &[MergeResult]) -> usize {
63 results
64 .iter()
65 .filter(|r| matches!(r, MergeResult::Conflict { .. }))
66 .count()
67}
68
69pub fn format_conflict(cfg: &MergeConfig, ours: &[String], theirs: &[String]) -> Vec<String> {
71 let mut out = Vec::new();
72 out.push(format!("<<<<<<< {}", cfg.label_ours));
73 out.extend(ours.iter().cloned());
74 out.push("=======".to_string());
75 out.extend(theirs.iter().cloned());
76 out.push(format!(">>>>>>> {}", cfg.label_theirs));
77 out
78}
79
80pub fn auto_resolve_ours(result: MergeResult) -> Vec<String> {
82 match result {
83 MergeResult::Clean(lines) => lines,
84 MergeResult::Conflict { ours, .. } => ours,
85 }
86}
87
88pub fn auto_resolve_theirs(result: MergeResult) -> Vec<String> {
90 match result {
91 MergeResult::Clean(lines) => lines,
92 MergeResult::Conflict { theirs, .. } => theirs,
93 }
94}
95
96#[cfg(test)]
97mod tests {
98 use super::*;
99
100 #[test]
101 fn test_no_change() {
102 let r = three_way_merge(&["a"], &["a"], &["a"]);
103 assert_eq!(r, MergeResult::Clean(vec!["a".to_string()]));
104 }
105
106 #[test]
107 fn test_ours_only_change() {
108 let r = three_way_merge(&["a"], &["b"], &["a"]);
109 assert_eq!(r, MergeResult::Clean(vec!["b".to_string()]));
110 }
111
112 #[test]
113 fn test_theirs_only_change() {
114 let r = three_way_merge(&["a"], &["a"], &["c"]);
115 assert_eq!(r, MergeResult::Clean(vec!["c".to_string()]));
116 }
117
118 #[test]
119 fn test_conflict() {
120 let r = three_way_merge(&["a"], &["b"], &["c"]);
121 assert!(matches!(r, MergeResult::Conflict { .. }));
122 }
123
124 #[test]
125 fn test_count_conflicts() {
126 let results = vec![
127 MergeResult::Clean(vec![]),
128 MergeResult::Conflict {
129 ours: vec![],
130 theirs: vec![],
131 },
132 ];
133 assert_eq!(count_conflicts(&results), 1);
134 }
135
136 #[test]
137 fn test_format_conflict() {
138 let cfg = MergeConfig::default();
139 let lines = format_conflict(&cfg, &["ours".to_string()], &["theirs".to_string()]);
140 assert!(lines[0].contains("ours"));
141 assert!(lines[lines.len() - 1].contains("theirs"));
142 }
143
144 #[test]
145 fn test_auto_resolve_ours() {
146 let r = MergeResult::Conflict {
147 ours: vec!["mine".to_string()],
148 theirs: vec!["yours".to_string()],
149 };
150 let resolved = auto_resolve_ours(r);
151 assert_eq!(resolved, vec!["mine".to_string()]);
152 }
153
154 #[test]
155 fn test_auto_resolve_theirs() {
156 let r = MergeResult::Conflict {
157 ours: vec!["mine".to_string()],
158 theirs: vec!["yours".to_string()],
159 };
160 let resolved = auto_resolve_theirs(r);
161 assert_eq!(resolved, vec!["yours".to_string()]);
162 }
163
164 #[test]
165 fn test_merge_config_default() {
166 let cfg = MergeConfig::default();
167 assert_eq!(cfg.label_ours, "ours");
168 assert_eq!(cfg.label_theirs, "theirs");
169 }
170
171 #[test]
172 fn test_clean_result_is_clean() {
173 let r = three_way_merge(&["x", "y"], &["x", "y"], &["x", "y"]);
174 assert!(matches!(r, MergeResult::Clean(_)));
175 }
176}