reddb_server/application/
merge_json.rs1use crate::json::{Map, Value};
17
18pub type JsonPath = String;
21
22#[derive(Debug, Clone)]
23pub struct MergeConflict {
24 pub path: JsonPath,
25 pub base: Value,
26 pub ours: Value,
27 pub theirs: Value,
28}
29
30#[derive(Debug, Clone)]
31pub struct MergeResult {
32 pub merged: Value,
35 pub conflicts: Vec<MergeConflict>,
36}
37
38impl MergeResult {
39 pub fn is_clean(&self) -> bool {
40 self.conflicts.is_empty()
41 }
42}
43
44pub fn three_way_merge(base: &Value, ours: &Value, theirs: &Value) -> MergeResult {
46 let mut conflicts = Vec::new();
47 let merged = merge_at(base, ours, theirs, "", &mut conflicts);
48 MergeResult { merged, conflicts }
49}
50
51fn merge_at(
52 base: &Value,
53 ours: &Value,
54 theirs: &Value,
55 path: &str,
56 conflicts: &mut Vec<MergeConflict>,
57) -> Value {
58 if ours == theirs {
59 return ours.clone();
60 }
61 if ours == base {
62 return theirs.clone();
63 }
64 if theirs == base {
65 return ours.clone();
66 }
67
68 match (ours, theirs) {
69 (Value::Object(o), Value::Object(t)) => {
70 let empty = Map::new();
71 let b = match base {
72 Value::Object(m) => m,
73 _ => &empty,
74 };
75 merge_objects(b, o, t, path, conflicts)
76 }
77 (Value::Array(o), Value::Array(t)) => {
78 let empty: Vec<Value> = Vec::new();
79 let b: &Vec<Value> = match base {
80 Value::Array(a) => a,
81 _ => &empty,
82 };
83 merge_arrays(b, o, t, path, conflicts)
84 }
85 _ => {
86 conflicts.push(MergeConflict {
87 path: path.to_string(),
88 base: base.clone(),
89 ours: ours.clone(),
90 theirs: theirs.clone(),
91 });
92 ours.clone()
93 }
94 }
95}
96
97fn merge_objects(
98 base: &Map<String, Value>,
99 ours: &Map<String, Value>,
100 theirs: &Map<String, Value>,
101 path: &str,
102 conflicts: &mut Vec<MergeConflict>,
103) -> Value {
104 let mut out = Map::new();
105 let null = Value::Null;
106
107 let mut keys: Vec<&str> = Vec::new();
108 for k in ours.keys() {
109 keys.push(k.as_str());
110 }
111 for k in theirs.keys() {
112 if !ours.contains_key(k) {
113 keys.push(k.as_str());
114 }
115 }
116 for k in base.keys() {
117 if !ours.contains_key(k) && !theirs.contains_key(k) {
118 keys.push(k.as_str());
119 }
120 }
121
122 for k in keys {
123 let b = base.get(k).unwrap_or(&null);
124 let o = ours.get(k).unwrap_or(&null);
125 let t = theirs.get(k).unwrap_or(&null);
126
127 let child_path = if path.is_empty() {
128 k.to_string()
129 } else {
130 format!("{path}.{k}")
131 };
132
133 let ours_deleted = !ours.contains_key(k) && base.contains_key(k);
135 let theirs_deleted = !theirs.contains_key(k) && base.contains_key(k);
136
137 if ours_deleted && theirs_deleted {
138 continue; }
140 if ours_deleted && t == b {
141 continue; }
143 if theirs_deleted && o == b {
144 continue; }
146 if ours_deleted && t != b {
147 conflicts.push(MergeConflict {
149 path: child_path,
150 base: b.clone(),
151 ours: Value::Null,
152 theirs: t.clone(),
153 });
154 out.insert(k.to_string(), t.clone());
155 continue;
156 }
157 if theirs_deleted && o != b {
158 conflicts.push(MergeConflict {
160 path: child_path,
161 base: b.clone(),
162 ours: o.clone(),
163 theirs: Value::Null,
164 });
165 out.insert(k.to_string(), o.clone());
166 continue;
167 }
168
169 let merged = merge_at(b, o, t, &child_path, conflicts);
170 out.insert(k.to_string(), merged);
171 }
172
173 Value::Object(out)
174}
175
176fn merge_arrays(
180 base: &[Value],
181 ours: &[Value],
182 theirs: &[Value],
183 path: &str,
184 conflicts: &mut Vec<MergeConflict>,
185) -> Value {
186 let ours_same_len = ours.len() == base.len();
188 let theirs_same_len = theirs.len() == base.len();
189
190 if ours_same_len && theirs_same_len {
191 let mut out = Vec::with_capacity(ours.len());
193 for i in 0..ours.len() {
194 let child = format!("{path}[{i}]");
195 out.push(merge_at(&base[i], &ours[i], &theirs[i], &child, conflicts));
196 }
197 return Value::Array(out);
198 }
199
200 if theirs_same_len && !ours_same_len {
201 return Value::Array(ours.to_vec());
203 }
204 if ours_same_len && !theirs_same_len {
205 return Value::Array(theirs.to_vec());
207 }
208
209 conflicts.push(MergeConflict {
211 path: path.to_string(),
212 base: Value::Array(base.to_vec()),
213 ours: Value::Array(ours.to_vec()),
214 theirs: Value::Array(theirs.to_vec()),
215 });
216 Value::Array(ours.to_vec())
217}
218
219#[cfg(test)]
220mod tests {
221 use super::*;
222 use crate::json::json;
223
224 #[test]
225 fn identical_trivial() {
226 let v = json!({"a": 1});
227 let r = three_way_merge(&v, &v, &v);
228 assert!(r.is_clean());
229 assert_eq!(r.merged, v);
230 }
231
232 #[test]
233 fn one_sided_change_ours() {
234 let base = json!({"a": 1});
235 let ours = json!({"a": 2});
236 let theirs = json!({"a": 1});
237 let r = three_way_merge(&base, &ours, &theirs);
238 assert!(r.is_clean());
239 assert_eq!(r.merged, ours);
240 }
241
242 #[test]
243 fn one_sided_change_theirs() {
244 let base = json!({"a": 1});
245 let ours = json!({"a": 1});
246 let theirs = json!({"a": 9});
247 let r = three_way_merge(&base, &ours, &theirs);
248 assert!(r.is_clean());
249 assert_eq!(r.merged, theirs);
250 }
251
252 #[test]
253 fn disjoint_object_edits_merge_clean() {
254 let base = json!({"a": 1, "b": 2});
255 let ours = json!({"a": 10, "b": 2});
256 let theirs = json!({"a": 1, "b": 20});
257 let r = three_way_merge(&base, &ours, &theirs);
258 assert!(r.is_clean());
259 assert_eq!(r.merged, json!({"a": 10, "b": 20}));
260 }
261
262 #[test]
263 fn conflicting_object_value() {
264 let base = json!({"a": 1});
265 let ours = json!({"a": 2});
266 let theirs = json!({"a": 3});
267 let r = three_way_merge(&base, &ours, &theirs);
268 assert_eq!(r.conflicts.len(), 1);
269 assert_eq!(r.conflicts[0].path, "a");
270 }
271
272 #[test]
273 fn nested_disjoint_edits() {
274 let base = json!({"user": json!({"name": "a", "age": 30})});
275 let ours = json!({"user": json!({"name": "b", "age": 30})});
276 let theirs = json!({"user": json!({"name": "a", "age": 31})});
277 let r = three_way_merge(&base, &ours, &theirs);
278 assert!(r.is_clean());
279 assert_eq!(r.merged, json!({"user": json!({"name": "b", "age": 31})}));
280 }
281
282 #[test]
283 fn both_added_same_key_conflict() {
284 let base = json!({});
285 let ours = json!({"x": 1});
286 let theirs = json!({"x": 2});
287 let r = three_way_merge(&base, &ours, &theirs);
288 assert_eq!(r.conflicts.len(), 1);
289 }
290
291 #[test]
292 fn both_added_same_key_equal() {
293 let base = json!({});
294 let ours = json!({"x": 1});
295 let theirs = json!({"x": 1});
296 let r = three_way_merge(&base, &ours, &theirs);
297 assert!(r.is_clean());
298 }
299
300 #[test]
301 fn array_elementwise_disjoint() {
302 let base = json!([1, 2, 3]);
303 let ours = json!([10, 2, 3]);
304 let theirs = json!([1, 2, 30]);
305 let r = three_way_merge(&base, &ours, &theirs);
306 assert!(r.is_clean());
307 assert_eq!(r.merged, json!([10, 2, 30]));
308 }
309
310 #[test]
311 fn array_elementwise_conflict() {
312 let base = json!([1]);
313 let ours = json!([2]);
314 let theirs = json!([3]);
315 let r = three_way_merge(&base, &ours, &theirs);
316 assert_eq!(r.conflicts.len(), 1);
317 assert_eq!(r.conflicts[0].path, "[0]");
318 }
319
320 #[test]
321 fn array_one_sided_length_change() {
322 let base = json!([1, 2]);
323 let ours = json!([1, 2, 3]);
324 let theirs = json!([1, 2]);
325 let r = three_way_merge(&base, &ours, &theirs);
326 assert!(r.is_clean());
327 assert_eq!(r.merged, ours);
328 }
329
330 #[test]
331 fn array_both_extended_conflict() {
332 let base = json!([1]);
333 let ours = json!([1, 2]);
334 let theirs = json!([1, 3]);
335 let r = three_way_merge(&base, &ours, &theirs);
336 assert_eq!(r.conflicts.len(), 1);
337 }
338
339 #[test]
340 fn deletion_both_sides_clean() {
341 let base = json!({"a": 1, "b": 2});
342 let ours = json!({"a": 1});
343 let theirs = json!({"a": 1});
344 let r = three_way_merge(&base, &ours, &theirs);
345 assert!(r.is_clean());
346 assert_eq!(r.merged, json!({"a": 1}));
347 }
348
349 #[test]
350 fn deletion_vs_modification_conflict() {
351 let base = json!({"a": 1, "b": 2});
352 let ours = json!({"a": 1});
353 let theirs = json!({"a": 1, "b": 99});
354 let r = three_way_merge(&base, &ours, &theirs);
355 assert_eq!(r.conflicts.len(), 1);
356 assert_eq!(r.conflicts[0].path, "b");
357 }
358
359 #[test]
360 fn type_mismatch_conflict() {
361 let base = json!(null);
362 let ours = json!({"a": 1});
363 let theirs = json!([1, 2]);
364 let r = three_way_merge(&base, &ours, &theirs);
365 assert_eq!(r.conflicts.len(), 1);
366 }
367}