1use crate::error::{CollabError, Result};
4use serde::{Deserialize, Serialize};
5use similar::{ChangeTag, TextDiff};
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
9#[serde(rename_all = "lowercase")]
10pub enum MergeStrategy {
11 Ours,
13 Theirs,
15 Auto,
17 Manual,
19}
20
21#[derive(Debug, Clone, Serialize, Deserialize)]
23pub struct ConflictResolution {
24 pub has_conflicts: bool,
26 pub resolved: serde_json::Value,
28 pub conflicts: Vec<Conflict>,
30}
31
32#[derive(Debug, Clone, Serialize, Deserialize)]
34pub struct Conflict {
35 pub path: String,
37 pub ours: serde_json::Value,
39 pub theirs: serde_json::Value,
41 pub base: Option<serde_json::Value>,
43}
44
45pub struct ConflictResolver {
47 default_strategy: MergeStrategy,
49}
50
51impl ConflictResolver {
52 pub fn new(default_strategy: MergeStrategy) -> Self {
54 Self { default_strategy }
55 }
56
57 pub fn resolve(
59 &self,
60 base: Option<&serde_json::Value>,
61 ours: &serde_json::Value,
62 theirs: &serde_json::Value,
63 strategy: Option<MergeStrategy>,
64 ) -> Result<ConflictResolution> {
65 let strategy = strategy.unwrap_or(self.default_strategy);
66
67 if ours == theirs {
69 return Ok(ConflictResolution {
70 has_conflicts: false,
71 resolved: ours.clone(),
72 conflicts: Vec::new(),
73 });
74 }
75
76 match strategy {
77 MergeStrategy::Ours => Ok(ConflictResolution {
78 has_conflicts: false,
79 resolved: ours.clone(),
80 conflicts: Vec::new(),
81 }),
82 MergeStrategy::Theirs => Ok(ConflictResolution {
83 has_conflicts: false,
84 resolved: theirs.clone(),
85 conflicts: Vec::new(),
86 }),
87 MergeStrategy::Auto => self.auto_merge(base, ours, theirs),
88 MergeStrategy::Manual => {
89 let conflicts = self.detect_conflicts("", base, ours, theirs);
91 Ok(ConflictResolution {
92 has_conflicts: !conflicts.is_empty(),
93 resolved: ours.clone(), conflicts,
95 })
96 }
97 }
98 }
99
100 fn auto_merge(
102 &self,
103 base: Option<&serde_json::Value>,
104 ours: &serde_json::Value,
105 theirs: &serde_json::Value,
106 ) -> Result<ConflictResolution> {
107 match (ours, theirs) {
109 (serde_json::Value::Object(ours_obj), serde_json::Value::Object(theirs_obj)) => {
110 let mut resolved = serde_json::Map::new();
111 let mut conflicts = Vec::new();
112
113 let base_obj = base.and_then(|b| b.as_object());
114
115 let all_keys: std::collections::HashSet<_> =
117 ours_obj.keys().chain(theirs_obj.keys()).collect();
118
119 for key in all_keys {
120 let ours_val = ours_obj.get(key);
121 let theirs_val = theirs_obj.get(key);
122 let base_val = base_obj.and_then(|b| b.get(key));
123
124 match (ours_val, theirs_val) {
125 (Some(o), Some(t)) if o == t => {
126 resolved.insert(key.clone(), o.clone());
128 }
129 (Some(o), Some(t)) => {
130 if let Some(base_val) = base_val {
132 if o == base_val {
133 resolved.insert(key.clone(), t.clone());
135 } else if t == base_val {
136 resolved.insert(key.clone(), o.clone());
138 } else {
139 conflicts.push(Conflict {
141 path: key.clone(),
142 ours: o.clone(),
143 theirs: t.clone(),
144 base: Some(base_val.clone()),
145 });
146 resolved.insert(key.clone(), o.clone()); }
148 } else {
149 conflicts.push(Conflict {
151 path: key.clone(),
152 ours: o.clone(),
153 theirs: t.clone(),
154 base: None,
155 });
156 resolved.insert(key.clone(), o.clone());
157 }
158 }
159 (Some(o), None) => {
160 resolved.insert(key.clone(), o.clone());
162 }
163 (None, Some(t)) => {
164 resolved.insert(key.clone(), t.clone());
166 }
167 (None, None) => unreachable!(),
168 }
169 }
170
171 Ok(ConflictResolution {
172 has_conflicts: !conflicts.is_empty(),
173 resolved: serde_json::Value::Object(resolved),
174 conflicts,
175 })
176 }
177 _ => {
178 Ok(ConflictResolution {
180 has_conflicts: true,
181 resolved: ours.clone(),
182 conflicts: vec![Conflict {
183 path: String::new(),
184 ours: ours.clone(),
185 theirs: theirs.clone(),
186 base: base.cloned(),
187 }],
188 })
189 }
190 }
191 }
192
193 fn detect_conflicts(
195 &self,
196 path: &str,
197 base: Option<&serde_json::Value>,
198 ours: &serde_json::Value,
199 theirs: &serde_json::Value,
200 ) -> Vec<Conflict> {
201 let mut conflicts = Vec::new();
202
203 if ours == theirs {
204 return conflicts;
205 }
206
207 match (ours, theirs) {
208 (serde_json::Value::Object(ours_obj), serde_json::Value::Object(theirs_obj)) => {
209 let base_obj = base.and_then(|b| b.as_object());
210 let all_keys: std::collections::HashSet<_> =
211 ours_obj.keys().chain(theirs_obj.keys()).collect();
212
213 for key in all_keys {
214 let new_path = if path.is_empty() {
215 key.clone()
216 } else {
217 format!("{}.{}", path, key)
218 };
219
220 let ours_val = ours_obj.get(key);
221 let theirs_val = theirs_obj.get(key);
222 let base_val = base_obj.and_then(|b| b.get(key));
223
224 if let (Some(o), Some(t)) = (ours_val, theirs_val) {
225 conflicts.extend(self.detect_conflicts(&new_path, base_val, o, t));
226 } else if ours_val != theirs_val {
227 conflicts.push(Conflict {
228 path: new_path,
229 ours: ours_val.cloned().unwrap_or(serde_json::Value::Null),
230 theirs: theirs_val.cloned().unwrap_or(serde_json::Value::Null),
231 base: base_val.cloned(),
232 });
233 }
234 }
235 }
236 _ => {
237 conflicts.push(Conflict {
238 path: path.to_string(),
239 ours: ours.clone(),
240 theirs: theirs.clone(),
241 base: base.cloned(),
242 });
243 }
244 }
245
246 conflicts
247 }
248
249 pub fn merge_text(&self, base: &str, ours: &str, theirs: &str) -> Result<String> {
251 if ours == theirs {
252 return Ok(ours.to_string());
253 }
254
255 let diff_ours = TextDiff::from_lines(base, ours);
257 let _diff_theirs = TextDiff::from_lines(base, theirs);
258
259 let mut result = String::new();
260 let has_conflict = false;
261
262 for change in diff_ours.iter_all_changes() {
264 match change.tag() {
265 ChangeTag::Equal => result.push_str(change.value()),
266 ChangeTag::Delete => {}
267 ChangeTag::Insert => result.push_str(change.value()),
268 }
269 }
270
271 if has_conflict {
272 Err(CollabError::ConflictDetected("Text merge conflict".to_string()))
273 } else {
274 Ok(result)
275 }
276 }
277}
278
279impl Default for ConflictResolver {
280 fn default() -> Self {
281 Self::new(MergeStrategy::Auto)
282 }
283}
284
285#[cfg(test)]
286mod tests {
287 use super::*;
288 use serde_json::json;
289
290 #[test]
291 fn test_no_conflict() {
292 let resolver = ConflictResolver::default();
293 let value = json!({"key": "value"});
294
295 let result = resolver.resolve(None, &value, &value, None).unwrap();
296
297 assert!(!result.has_conflicts);
298 assert_eq!(result.resolved, value);
299 assert!(result.conflicts.is_empty());
300 }
301
302 #[test]
303 fn test_strategy_ours() {
304 let resolver = ConflictResolver::default();
305 let ours = json!({"key": "ours"});
306 let theirs = json!({"key": "theirs"});
307
308 let result = resolver.resolve(None, &ours, &theirs, Some(MergeStrategy::Ours)).unwrap();
309
310 assert!(!result.has_conflicts);
311 assert_eq!(result.resolved, ours);
312 }
313
314 #[test]
315 fn test_strategy_theirs() {
316 let resolver = ConflictResolver::default();
317 let ours = json!({"key": "ours"});
318 let theirs = json!({"key": "theirs"});
319
320 let result = resolver.resolve(None, &ours, &theirs, Some(MergeStrategy::Theirs)).unwrap();
321
322 assert!(!result.has_conflicts);
323 assert_eq!(result.resolved, theirs);
324 }
325
326 #[test]
327 fn test_auto_merge_no_base() {
328 let resolver = ConflictResolver::default();
329 let ours = json!({"key1": "value1"});
330 let theirs = json!({"key2": "value2"});
331
332 let result = resolver.resolve(None, &ours, &theirs, Some(MergeStrategy::Auto)).unwrap();
333
334 assert!(!result.has_conflicts);
336 assert_eq!(result.resolved["key1"], "value1");
337 assert_eq!(result.resolved["key2"], "value2");
338 }
339
340 #[test]
341 fn test_auto_merge_with_base() {
342 let resolver = ConflictResolver::default();
343 let base = json!({"key": "base"});
344 let ours = json!({"key": "ours"});
345 let theirs = json!({"key": "base"}); let result = resolver
348 .resolve(Some(&base), &ours, &theirs, Some(MergeStrategy::Auto))
349 .unwrap();
350
351 assert!(!result.has_conflicts);
352 assert_eq!(result.resolved["key"], "ours");
353 }
354
355 #[test]
356 fn test_conflict_detection() {
357 let resolver = ConflictResolver::default();
358 let base = json!({"key": "base"});
359 let ours = json!({"key": "ours"});
360 let theirs = json!({"key": "theirs"});
361
362 let result = resolver
363 .resolve(Some(&base), &ours, &theirs, Some(MergeStrategy::Auto))
364 .unwrap();
365
366 assert!(result.has_conflicts);
367 assert_eq!(result.conflicts.len(), 1);
368 assert_eq!(result.conflicts[0].path, "key");
369 }
370}