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