1use serde_json::Value;
2use suture_driver::{DriverError, SemanticChange, SutureDriver};
3
4pub struct JsonDriver;
5
6impl JsonDriver {
7 pub fn new() -> Self {
8 Self
9 }
10
11 fn json_pointer_escape(s: &str) -> String {
12 s.replace('~', "~0").replace('/', "~1")
13 }
14
15 fn diff_values(old: &Value, new: &Value, path: &str) -> Vec<SemanticChange> {
16 let mut changes = Vec::new();
17
18 match (old, new) {
19 (Value::Object(old_map), Value::Object(new_map)) => {
20 let old_keys: std::collections::HashSet<&str> =
21 old_map.keys().map(|s| s.as_str()).collect();
22 let new_keys: std::collections::HashSet<&str> =
23 new_map.keys().map(|s| s.as_str()).collect();
24
25 for key in &old_keys {
26 if !new_keys.contains(key) {
27 let escaped = Self::json_pointer_escape(key);
28 let child_path = if path == "/" {
29 format!("/{escaped}")
30 } else {
31 format!("{path}/{escaped}")
32 };
33 changes.push(SemanticChange::Removed {
34 path: child_path,
35 old_value: old_map[*key].to_string(),
36 });
37 }
38 }
39
40 for key in &new_keys {
41 if !old_keys.contains(key) {
42 let escaped = Self::json_pointer_escape(key);
43 let child_path = if path == "/" {
44 format!("/{escaped}")
45 } else {
46 format!("{path}/{escaped}")
47 };
48 changes.push(SemanticChange::Added {
49 path: child_path,
50 value: new_map[*key].to_string(),
51 });
52 }
53 }
54
55 for key in &old_keys {
56 if let Some(new_val) = new_keys.contains(key).then(|| &new_map[*key]) {
57 let escaped = Self::json_pointer_escape(key);
58 let child_path = if path == "/" {
59 format!("/{escaped}")
60 } else {
61 format!("{path}/{escaped}")
62 };
63 changes.extend(Self::diff_values(&old_map[*key], new_val, &child_path));
64 }
65 }
66 }
67 (Value::Array(old_arr), Value::Array(new_arr)) => {
68 let max_len = old_arr.len().max(new_arr.len());
69
70 for i in 0..max_len {
71 let child_path = format!("{path}/{i}");
72 match (old_arr.get(i), new_arr.get(i)) {
73 (None, Some(new_val)) => {
74 changes.push(SemanticChange::Added {
75 path: child_path,
76 value: new_val.to_string(),
77 });
78 }
79 (Some(old_val), None) => {
80 changes.push(SemanticChange::Removed {
81 path: child_path,
82 old_value: old_val.to_string(),
83 });
84 }
85 (Some(old_val), Some(new_val)) => {
86 changes.extend(Self::diff_values(old_val, new_val, &child_path));
87 }
88 (None, None) => {}
89 }
90 }
91 }
92 (old_val, new_val) if old_val != new_val => {
93 changes.push(SemanticChange::Modified {
94 path: path.to_string(),
95 old_value: old_val.to_string(),
96 new_value: new_val.to_string(),
97 });
98 }
99 _ => {}
100 }
101
102 changes
103 }
104
105 fn merge_values(
106 base: &Value,
107 ours: &Value,
108 theirs: &Value,
109 ) -> Result<Option<Value>, DriverError> {
110 match (base, ours, theirs) {
111 (Value::Object(base_map), Value::Object(ours_map), Value::Object(theirs_map)) => {
112 let base_keys: std::collections::HashSet<&str> =
113 base_map.keys().map(|s| s.as_str()).collect();
114 let ours_keys: std::collections::HashSet<&str> =
115 ours_map.keys().map(|s| s.as_str()).collect();
116 let theirs_keys: std::collections::HashSet<&str> =
117 theirs_map.keys().map(|s| s.as_str()).collect();
118
119 let all_keys: std::collections::HashSet<&str> = base_keys
120 .iter()
121 .chain(ours_keys.iter())
122 .chain(theirs_keys.iter())
123 .copied()
124 .collect();
125
126 let mut merged = serde_json::Map::new();
127
128 for key in &all_keys {
129 let in_base = base_keys.contains(key);
130 let in_ours = ours_keys.contains(key);
131 let in_theirs = theirs_keys.contains(key);
132
133 match (in_base, in_ours, in_theirs) {
134 (true, true, false) => {
135 merged.insert((*key).to_string(), ours_map[*key].clone());
136 }
137 (true, false, true) => {
138 merged.insert((*key).to_string(), theirs_map[*key].clone());
139 }
140 (true, true, true) => {
141 let base_val = &base_map[*key];
142 let ours_val = &ours_map[*key];
143 let theirs_val = &theirs_map[*key];
144
145 if ours_val == theirs_val {
146 merged.insert((*key).to_string(), ours_val.clone());
147 } else if ours_val == base_val {
148 merged.insert((*key).to_string(), theirs_val.clone());
149 } else if theirs_val == base_val {
150 merged.insert((*key).to_string(), ours_val.clone());
151 } else if let Some(m) =
152 Self::merge_values(base_val, ours_val, theirs_val)?
153 {
154 merged.insert((*key).to_string(), m);
155 } else {
156 return Ok(None);
157 }
158 }
159 (false, true, true) => {
160 if ours_map[*key] == theirs_map[*key] {
161 merged.insert((*key).to_string(), ours_map[*key].clone());
162 } else {
163 return Ok(None);
164 }
165 }
166 (false, true, false) => {
167 merged.insert((*key).to_string(), ours_map[*key].clone());
168 }
169 (false, false, true) => {
170 merged.insert((*key).to_string(), theirs_map[*key].clone());
171 }
172 (true, false, false) => {}
173 (false, false, false) => {}
174 }
175 }
176
177 Ok(Some(Value::Object(merged)))
178 }
179 (Value::Array(base_arr), Value::Array(ours_arr), Value::Array(theirs_arr)) => {
180 let max_len = base_arr.len().max(ours_arr.len()).max(theirs_arr.len());
181 let mut merged = Vec::new();
182
183 for i in 0..max_len {
184 let base_val = base_arr.get(i);
185 let ours_val = ours_arr.get(i);
186 let theirs_val = theirs_arr.get(i);
187
188 match (base_val, ours_val, theirs_val) {
189 (None, Some(o), None) => merged.push(o.clone()),
190 (None, None, Some(t)) => merged.push(t.clone()),
191 (None, Some(o), Some(t)) => {
192 if o == t {
193 merged.push(o.clone());
194 } else {
195 return Ok(None);
196 }
197 }
198 (None, None, _) => {}
199 (Some(_), Some(o), None) => merged.push(o.clone()),
200 (Some(_), None, Some(t)) => merged.push(t.clone()),
201 (Some(_), None, None) => {}
202 (Some(b), Some(o), Some(t)) => {
203 if o == t {
204 merged.push(o.clone());
205 } else if o == b {
206 merged.push(t.clone());
207 } else if t == b {
208 merged.push(o.clone());
209 } else if let Some(m) = Self::merge_values(b, o, t)? {
210 merged.push(m);
211 } else {
212 return Ok(None);
213 }
214 }
215 }
216 }
217
218 Ok(Some(Value::Array(merged)))
219 }
220 (base_val, ours_val, theirs_val) => {
221 if ours_val == theirs_val {
222 Ok(Some(ours_val.clone()))
223 } else if ours_val == base_val {
224 Ok(Some(theirs_val.clone()))
225 } else if theirs_val == base_val {
226 Ok(Some(ours_val.clone()))
227 } else {
228 Ok(None)
229 }
230 }
231 }
232 }
233
234 fn format_change(change: &SemanticChange) -> String {
235 match change {
236 SemanticChange::Added { path, value } => {
237 format!(" ADDED {path}: {value}")
238 }
239 SemanticChange::Removed { path, old_value } => {
240 format!(" REMOVED {path}: {old_value}")
241 }
242 SemanticChange::Modified {
243 path,
244 old_value,
245 new_value,
246 } => {
247 format!(" MODIFIED {path}: {old_value} → {new_value}")
248 }
249 SemanticChange::Moved {
250 old_path,
251 new_path,
252 value,
253 } => {
254 format!(" MOVED {old_path} → {new_path}: {value}")
255 }
256 }
257 }
258}
259
260impl Default for JsonDriver {
261 fn default() -> Self {
262 Self::new()
263 }
264}
265
266impl SutureDriver for JsonDriver {
267 fn name(&self) -> &str {
268 "JSON"
269 }
270
271 fn supported_extensions(&self) -> &[&str] {
272 &[".json"]
273 }
274
275 fn diff(
276 &self,
277 base_content: Option<&str>,
278 new_content: &str,
279 ) -> Result<Vec<SemanticChange>, DriverError> {
280 let new_val: Value = serde_json::from_str(new_content)
281 .map_err(|e| DriverError::ParseError(e.to_string()))?;
282
283 match base_content {
284 None => {
285 let mut changes = Vec::new();
286 collect_all_paths(&new_val, "/".to_string(), &mut changes);
287 Ok(changes)
288 }
289 Some(base) => {
290 let old_val: Value = serde_json::from_str(base)
291 .map_err(|e| DriverError::ParseError(e.to_string()))?;
292 Ok(Self::diff_values(&old_val, &new_val, "/"))
293 }
294 }
295 }
296
297 fn format_diff(
298 &self,
299 base_content: Option<&str>,
300 new_content: &str,
301 ) -> Result<String, DriverError> {
302 let changes = self.diff(base_content, new_content)?;
303
304 if changes.is_empty() {
305 return Ok("no changes".to_string());
306 }
307
308 let lines: Vec<String> = changes.iter().map(Self::format_change).collect();
309 Ok(lines.join("\n"))
310 }
311
312 fn merge(&self, base: &str, ours: &str, theirs: &str) -> Result<Option<String>, DriverError> {
313 let base_val: Value =
314 serde_json::from_str(base).map_err(|e| DriverError::ParseError(e.to_string()))?;
315 let ours_val: Value =
316 serde_json::from_str(ours).map_err(|e| DriverError::ParseError(e.to_string()))?;
317 let theirs_val: Value =
318 serde_json::from_str(theirs).map_err(|e| DriverError::ParseError(e.to_string()))?;
319
320 match Self::merge_values(&base_val, &ours_val, &theirs_val)? {
321 Some(merged) => Ok(Some(
322 serde_json::to_string_pretty(&merged)
323 .map_err(|e| DriverError::SerializationError(e.to_string()))?,
324 )),
325 None => Ok(None),
326 }
327 }
328}
329
330fn collect_all_paths(val: &Value, path: String, out: &mut Vec<SemanticChange>) {
331 match val {
332 Value::Object(map) => {
333 for (key, child) in map {
334 let escaped = JsonDriver::json_pointer_escape(key);
335 let child_path = if path == "/" {
336 format!("/{escaped}")
337 } else {
338 format!("{path}/{escaped}")
339 };
340 collect_all_paths(child, child_path, out);
341 }
342 }
343 Value::Array(arr) => {
344 for (i, child) in arr.iter().enumerate() {
345 let child_path = format!("{path}/{i}");
346 collect_all_paths(child, child_path, out);
347 }
348 }
349 other => {
350 out.push(SemanticChange::Added {
351 path,
352 value: other.to_string(),
353 });
354 }
355 }
356}
357
358#[cfg(test)]
359mod tests {
360 use super::*;
361
362 #[test]
363 fn test_json_driver_name() {
364 let driver = JsonDriver::new();
365 assert_eq!(driver.name(), "JSON");
366 }
367
368 #[test]
369 fn test_json_driver_extensions() {
370 let driver = JsonDriver::new();
371 assert_eq!(driver.supported_extensions(), &[".json"]);
372 }
373
374 #[test]
375 fn test_diff_added_key() {
376 let driver = JsonDriver::new();
377 let old = r#"{"name": "Alice"}"#;
378 let new = r#"{"name": "Alice", "email": "alice@example.com"}"#;
379
380 let changes = driver.diff(Some(old), new).unwrap();
381 assert!(changes.contains(&SemanticChange::Added {
382 path: "/email".to_string(),
383 value: "\"alice@example.com\"".to_string(),
384 }));
385 }
386
387 #[test]
388 fn test_diff_removed_key() {
389 let driver = JsonDriver::new();
390 let old = r#"{"name": "Alice", "phone": "+1234567890"}"#;
391 let new = r#"{"name": "Alice"}"#;
392
393 let changes = driver.diff(Some(old), new).unwrap();
394 assert!(changes.contains(&SemanticChange::Removed {
395 path: "/phone".to_string(),
396 old_value: "\"+1234567890\"".to_string(),
397 }));
398 }
399
400 #[test]
401 fn test_diff_modified_key() {
402 let driver = JsonDriver::new();
403 let old = r#"{"name": "Alice"}"#;
404 let new = r#"{"name": "Bob"}"#;
405
406 let changes = driver.diff(Some(old), new).unwrap();
407 assert_eq!(changes.len(), 1);
408 assert_eq!(
409 changes[0],
410 SemanticChange::Modified {
411 path: "/name".to_string(),
412 old_value: "\"Alice\"".to_string(),
413 new_value: "\"Bob\"".to_string(),
414 }
415 );
416 }
417
418 #[test]
419 fn test_diff_nested() {
420 let driver = JsonDriver::new();
421 let old = r#"{"address": {"city": "NYC", "zip": "10001"}}"#;
422 let new = r#"{"address": {"city": "San Francisco", "zip": "10001"}}"#;
423
424 let changes = driver.diff(Some(old), new).unwrap();
425 assert!(changes.contains(&SemanticChange::Modified {
426 path: "/address/city".to_string(),
427 old_value: "\"NYC\"".to_string(),
428 new_value: "\"San Francisco\"".to_string(),
429 }));
430 }
431
432 #[test]
433 fn test_diff_new_file() {
434 let driver = JsonDriver::new();
435 let new = r#"{"name": "Alice", "age": 30}"#;
436
437 let changes = driver.diff(None, new).unwrap();
438 assert!(!changes.is_empty());
439 for change in &changes {
440 assert!(matches!(change, SemanticChange::Added { .. }));
441 }
442 }
443
444 #[test]
445 fn test_format_diff() {
446 let driver = JsonDriver::new();
447 let old = r#"{"name": "Alice"}"#;
448 let new = r#"{"name": "Bob", "email": "bob@example.com"}"#;
449
450 let output = driver.format_diff(Some(old), new).unwrap();
451 assert!(output.contains("MODIFIED"));
452 assert!(output.contains("ADDED"));
453 assert!(output.contains("/name"));
454 assert!(output.contains("/email"));
455 }
456
457 #[test]
458 fn test_format_diff_empty() {
459 let driver = JsonDriver::new();
460 let content = r#"{"name": "Alice"}"#;
461
462 let output = driver.format_diff(Some(content), content).unwrap();
463 assert_eq!(output, "no changes");
464 }
465
466 #[test]
467 fn test_array_changes() {
468 let driver = JsonDriver::new();
469 let old = r#"{"items": ["a", "b"]}"#;
470 let new = r#"{"items": ["a", "c", "d"]}"#;
471
472 let changes = driver.diff(Some(old), new).unwrap();
473 assert!(changes.contains(&SemanticChange::Modified {
474 path: "/items/1".to_string(),
475 old_value: "\"b\"".to_string(),
476 new_value: "\"c\"".to_string(),
477 }));
478 assert!(changes.contains(&SemanticChange::Added {
479 path: "/items/2".to_string(),
480 value: "\"d\"".to_string(),
481 }));
482 }
483
484 #[test]
485 fn test_merge_no_conflict() {
486 let driver = JsonDriver::new();
487 let base = r#"{"a": 1, "b": 2, "c": 3}"#;
488 let ours = r#"{"a": 10, "b": 2, "c": 3}"#;
489 let theirs = r#"{"a": 1, "b": 2, "c": 30}"#;
490
491 let result = driver.merge(base, ours, theirs).unwrap();
492 assert!(result.is_some());
493 let merged: Value = serde_json::from_str(&result.unwrap()).unwrap();
494 assert_eq!(merged["a"], 10);
495 assert_eq!(merged["b"], 2);
496 assert_eq!(merged["c"], 30);
497 }
498
499 #[test]
500 fn test_merge_conflict() {
501 let driver = JsonDriver::new();
502 let base = r#"{"key": "original"}"#;
503 let ours = r#"{"key": "ours"}"#;
504 let theirs = r#"{"key": "theirs"}"#;
505
506 let result = driver.merge(base, ours, theirs).unwrap();
507 assert!(result.is_none());
508 }
509
510 #[test]
511 fn test_merge_both_add_different_keys() {
512 let driver = JsonDriver::new();
513 let base = r#"{"a": 1}"#;
514 let ours = r#"{"a": 1, "x": 100}"#;
515 let theirs = r#"{"a": 1, "y": 200}"#;
516
517 let result = driver.merge(base, ours, theirs).unwrap();
518 assert!(result.is_some());
519 let merged: Value = serde_json::from_str(&result.unwrap()).unwrap();
520 assert_eq!(merged["a"], 1);
521 assert_eq!(merged["x"], 100);
522 assert_eq!(merged["y"], 200);
523 }
524
525 #[test]
526 fn test_merge_both_add_same_key() {
527 let driver = JsonDriver::new();
528 let base = r#"{"a": 1}"#;
529 let ours = r#"{"a": 1, "x": 100}"#;
530 let theirs = r#"{"a": 1, "x": 999}"#;
531
532 let result = driver.merge(base, ours, theirs).unwrap();
533 assert!(result.is_none());
534 }
535
536 #[test]
537 fn test_merge_nested() {
538 let driver = JsonDriver::new();
539 let base = r#"{"outer": {"inner": "base", "other": "keep"}}"#;
540 let ours = r#"{"outer": {"inner": "ours", "other": "keep"}}"#;
541 let theirs = r#"{"outer": {"inner": "base", "other": "changed"}}"#;
542
543 let result = driver.merge(base, ours, theirs).unwrap();
544 assert!(result.is_some());
545 let merged: Value = serde_json::from_str(&result.unwrap()).unwrap();
546 assert_eq!(merged["outer"]["inner"], "ours");
547 assert_eq!(merged["outer"]["other"], "changed");
548 }
549
550 #[test]
551 fn test_merge_identical() {
552 let driver = JsonDriver::new();
553 let content = r#"{"a": 1, "b": 2}"#;
554
555 let result = driver.merge(content, content, content).unwrap();
556 assert!(result.is_some());
557 let merged: Value = serde_json::from_str(&result.unwrap()).unwrap();
558 assert_eq!(merged["a"], 1);
559 assert_eq!(merged["b"], 2);
560 }
561}