1use claw_core::types::PatchOp;
2use serde_json::Value;
3
4use crate::codec::Codec;
5use crate::PatchError;
6
7pub struct JsonTreeCodec;
9
10impl Codec for JsonTreeCodec {
11 fn id(&self) -> &str {
12 "json/tree"
13 }
14
15 fn diff(&self, old: &[u8], new: &[u8]) -> Result<Vec<PatchOp>, PatchError> {
16 let old_val: Value =
17 serde_json::from_slice(old).map_err(|e| PatchError::InvalidJson(e.to_string()))?;
18 let new_val: Value =
19 serde_json::from_slice(new).map_err(|e| PatchError::InvalidJson(e.to_string()))?;
20
21 let mut ops = Vec::new();
22 diff_values("", &old_val, &new_val, &mut ops)?;
23 Ok(ops)
24 }
25
26 fn apply(&self, base: &[u8], ops: &[PatchOp]) -> Result<Vec<u8>, PatchError> {
27 let mut val: Value =
28 serde_json::from_slice(base).map_err(|e| PatchError::InvalidJson(e.to_string()))?;
29
30 for op in ops {
31 apply_op(&mut val, op)?;
32 }
33
34 serde_json::to_vec_pretty(&val).map_err(|e| PatchError::ApplyFailed(e.to_string()))
35 }
36
37 fn invert(&self, ops: &[PatchOp]) -> Result<Vec<PatchOp>, PatchError> {
38 let mut inverted: Vec<PatchOp> = ops
39 .iter()
40 .map(|op| match op.op_type.as_str() {
41 "insert" => PatchOp {
42 address: op.address.clone(),
43 op_type: "delete".to_string(),
44 old_data: op.new_data.clone(),
45 new_data: None,
46 context_hash: None,
47 },
48 "delete" => PatchOp {
49 address: op.address.clone(),
50 op_type: "insert".to_string(),
51 old_data: None,
52 new_data: op.old_data.clone(),
53 context_hash: None,
54 },
55 "replace" => PatchOp {
56 address: op.address.clone(),
57 op_type: "replace".to_string(),
58 old_data: op.new_data.clone(),
59 new_data: op.old_data.clone(),
60 context_hash: None,
61 },
62 _ => op.clone(),
63 })
64 .collect();
65 inverted.reverse();
66 Ok(inverted)
67 }
68
69 fn commute(
70 &self,
71 left: &[PatchOp],
72 right: &[PatchOp],
73 ) -> Result<(Vec<PatchOp>, Vec<PatchOp>), PatchError> {
74 for l in left {
76 for r in right {
77 let rel = path_relationship(&l.address, &r.address);
78 match rel {
79 PathRelation::Equal | PathRelation::AncestorOf | PathRelation::DescendantOf => {
80 return Err(PatchError::CommuteFailed);
81 }
82 PathRelation::Independent => {}
83 PathRelation::SiblingArrayElements => {
84 return Err(PatchError::CommuteFailed);
88 }
89 }
90 }
91 }
92 Ok((right.to_vec(), left.to_vec()))
94 }
95
96 fn merge3(&self, base: &[u8], left: &[u8], right: &[u8]) -> Result<Vec<u8>, PatchError> {
97 let base_val: Value =
98 serde_json::from_slice(base).map_err(|e| PatchError::InvalidJson(e.to_string()))?;
99 let left_val: Value =
100 serde_json::from_slice(left).map_err(|e| PatchError::InvalidJson(e.to_string()))?;
101 let right_val: Value =
102 serde_json::from_slice(right).map_err(|e| PatchError::InvalidJson(e.to_string()))?;
103
104 let merged = merge3_values(&base_val, &left_val, &right_val)?;
105 serde_json::to_vec_pretty(&merged).map_err(|e| PatchError::Merge3Failed(e.to_string()))
106 }
107}
108
109fn value_bytes(value: &Value) -> Result<Vec<u8>, PatchError> {
110 serde_json::to_vec(value).map_err(|e| PatchError::InvalidJson(e.to_string()))
111}
112
113fn diff_values(
114 path: &str,
115 old: &Value,
116 new: &Value,
117 ops: &mut Vec<PatchOp>,
118) -> Result<(), PatchError> {
119 if old == new {
120 return Ok(());
121 }
122
123 match (old, new) {
124 (Value::Object(old_map), Value::Object(new_map)) => {
125 for key in old_map.keys() {
127 if !new_map.contains_key(key) {
128 let child_path = format!("{path}/{key}");
129 ops.push(PatchOp {
130 address: child_path,
131 op_type: "delete".to_string(),
132 old_data: Some(value_bytes(&old_map[key])?),
133 new_data: None,
134 context_hash: None,
135 });
136 }
137 }
138 for key in new_map.keys() {
140 if !old_map.contains_key(key) {
141 let child_path = format!("{path}/{key}");
142 ops.push(PatchOp {
143 address: child_path,
144 op_type: "insert".to_string(),
145 old_data: None,
146 new_data: Some(value_bytes(&new_map[key])?),
147 context_hash: None,
148 });
149 }
150 }
151 for key in old_map.keys() {
153 if let Some(new_val) = new_map.get(key) {
154 let child_path = format!("{path}/{key}");
155 diff_values(&child_path, &old_map[key], new_val, ops)?;
156 }
157 }
158 }
159 (Value::Array(old_arr), Value::Array(new_arr)) => {
160 if old_arr.len() != new_arr.len() {
162 ops.push(PatchOp {
163 address: path.to_string(),
164 op_type: "replace".to_string(),
165 old_data: Some(value_bytes(old)?),
166 new_data: Some(value_bytes(new)?),
167 context_hash: None,
168 });
169 } else {
170 for (i, (o, n)) in old_arr.iter().zip(new_arr.iter()).enumerate() {
171 let child_path = format!("{path}/{i}");
172 diff_values(&child_path, o, n, ops)?;
173 }
174 }
175 }
176 _ => {
177 ops.push(PatchOp {
179 address: path.to_string(),
180 op_type: "replace".to_string(),
181 old_data: Some(value_bytes(old)?),
182 new_data: Some(value_bytes(new)?),
183 context_hash: None,
184 });
185 }
186 }
187 Ok(())
188}
189
190fn apply_op(val: &mut Value, op: &PatchOp) -> Result<(), PatchError> {
191 let parts: Vec<&str> = op.address.split('/').filter(|s| !s.is_empty()).collect();
192
193 if parts.is_empty() {
194 match op.op_type.as_str() {
196 "replace" => {
197 let new_val: Value =
198 serde_json::from_slice(op.new_data.as_ref().ok_or_else(|| {
199 PatchError::ApplyFailed("replace missing new_data".into())
200 })?)
201 .map_err(|e| PatchError::ApplyFailed(e.to_string()))?;
202 *val = new_val;
203 }
204 _ => {
205 return Err(PatchError::ApplyFailed(format!(
206 "unsupported root op: {}",
207 op.op_type
208 )))
209 }
210 }
211 return Ok(());
212 }
213
214 let parent_parts = &parts[..parts.len() - 1];
216 let last_key = parts[parts.len() - 1];
217
218 let mut current = val as &mut Value;
219 for &part in parent_parts {
220 current = navigate_mut(current, part)?;
221 }
222
223 match op.op_type.as_str() {
224 "insert" => {
225 let new_val: Value = serde_json::from_slice(
226 op.new_data
227 .as_ref()
228 .ok_or_else(|| PatchError::ApplyFailed("insert missing new_data".into()))?,
229 )
230 .map_err(|e| PatchError::ApplyFailed(e.to_string()))?;
231
232 match current {
233 Value::Object(map) => {
234 map.insert(last_key.to_string(), new_val);
235 }
236 Value::Array(arr) => {
237 let idx: usize = last_key
238 .parse()
239 .map_err(|_| PatchError::ApplyFailed("invalid array index".into()))?;
240 if idx > arr.len() {
241 return Err(PatchError::ApplyFailed("array index out of bounds".into()));
242 }
243 arr.insert(idx, new_val);
244 }
245 _ => {
246 return Err(PatchError::ApplyFailed(
247 "cannot insert into non-container".into(),
248 ))
249 }
250 }
251 }
252 "delete" => match current {
253 Value::Object(map) => {
254 map.remove(last_key);
255 }
256 Value::Array(arr) => {
257 let idx: usize = last_key
258 .parse()
259 .map_err(|_| PatchError::ApplyFailed("invalid array index".into()))?;
260 if idx >= arr.len() {
261 return Err(PatchError::ApplyFailed("array index out of bounds".into()));
262 }
263 arr.remove(idx);
264 }
265 _ => {
266 return Err(PatchError::ApplyFailed(
267 "cannot delete from non-container".into(),
268 ))
269 }
270 },
271 "replace" => {
272 let new_val: Value = serde_json::from_slice(
273 op.new_data
274 .as_ref()
275 .ok_or_else(|| PatchError::ApplyFailed("replace missing new_data".into()))?,
276 )
277 .map_err(|e| PatchError::ApplyFailed(e.to_string()))?;
278
279 match current {
280 Value::Object(map) => {
281 map.insert(last_key.to_string(), new_val);
282 }
283 Value::Array(arr) => {
284 let idx: usize = last_key
285 .parse()
286 .map_err(|_| PatchError::ApplyFailed("invalid array index".into()))?;
287 if idx >= arr.len() {
288 return Err(PatchError::ApplyFailed("array index out of bounds".into()));
289 }
290 arr[idx] = new_val;
291 }
292 _ => {
293 return Err(PatchError::ApplyFailed(
294 "cannot replace in non-container".into(),
295 ))
296 }
297 }
298 }
299 other => return Err(PatchError::ApplyFailed(format!("unknown op type: {other}"))),
300 }
301
302 Ok(())
303}
304
305fn navigate_mut<'a>(val: &'a mut Value, key: &str) -> Result<&'a mut Value, PatchError> {
306 match val {
307 Value::Object(map) => map
308 .get_mut(key)
309 .ok_or_else(|| PatchError::ApplyFailed(format!("key not found: {key}"))),
310 Value::Array(arr) => {
311 let idx: usize = key
312 .parse()
313 .map_err(|_| PatchError::ApplyFailed(format!("invalid index: {key}")))?;
314 arr.get_mut(idx)
315 .ok_or_else(|| PatchError::ApplyFailed(format!("index out of bounds: {idx}")))
316 }
317 _ => Err(PatchError::ApplyFailed(format!(
318 "cannot navigate into scalar at {key}"
319 ))),
320 }
321}
322
323#[derive(Debug, PartialEq)]
324enum PathRelation {
325 Equal,
326 AncestorOf,
327 DescendantOf,
328 SiblingArrayElements,
329 Independent,
330}
331
332fn path_relationship(a: &str, b: &str) -> PathRelation {
333 if a == b {
334 return PathRelation::Equal;
335 }
336 if b.starts_with(a) && b.as_bytes().get(a.len()) == Some(&b'/') {
337 return PathRelation::AncestorOf;
338 }
339 if a.starts_with(b) && a.as_bytes().get(b.len()) == Some(&b'/') {
340 return PathRelation::DescendantOf;
341 }
342 let a_parts: Vec<&str> = a.split('/').collect();
344 let b_parts: Vec<&str> = b.split('/').collect();
345 if a_parts.len() == b_parts.len() && a_parts.len() > 1 {
346 let a_parent = &a_parts[..a_parts.len() - 1];
347 let b_parent = &b_parts[..b_parts.len() - 1];
348 if a_parent == b_parent {
349 let Some(a_last) = a_parts.last() else {
350 return PathRelation::Independent;
351 };
352 let Some(b_last) = b_parts.last() else {
353 return PathRelation::Independent;
354 };
355 if a_last.parse::<usize>().is_ok() && b_last.parse::<usize>().is_ok() {
356 return PathRelation::SiblingArrayElements;
357 }
358 }
359 }
360 PathRelation::Independent
361}
362
363fn merge3_values(base: &Value, left: &Value, right: &Value) -> Result<Value, PatchError> {
364 if left == right {
365 return Ok(left.clone());
366 }
367 if left == base {
368 return Ok(right.clone());
369 }
370 if right == base {
371 return Ok(left.clone());
372 }
373
374 match (base, left, right) {
375 (Value::Object(base_map), Value::Object(left_map), Value::Object(right_map)) => {
376 let mut merged = serde_json::Map::new();
377 let all_keys: std::collections::BTreeSet<&String> = base_map
378 .keys()
379 .chain(left_map.keys())
380 .chain(right_map.keys())
381 .collect();
382
383 for key in all_keys {
384 let b = base_map.get(key);
385 let l = left_map.get(key);
386 let r = right_map.get(key);
387
388 match (b, l, r) {
389 (Some(bv), Some(lv), Some(rv)) => {
390 merged.insert(key.clone(), merge3_values(bv, lv, rv)?);
391 }
392 (Some(_), Some(_lv), None) => {
393 if l == b {
395 } else {
397 return Err(PatchError::Merge3Failed(format!(
399 "conflict at /{key}: left modified, right deleted"
400 )));
401 }
402 }
403 (Some(_), None, Some(_rv)) => {
404 if r == b {
405 } else {
407 return Err(PatchError::Merge3Failed(format!(
408 "conflict at /{key}: left deleted, right modified"
409 )));
410 }
411 }
412 (None, Some(lv), Some(rv)) => {
413 if lv == rv {
415 merged.insert(key.clone(), lv.clone());
416 } else {
417 return Err(PatchError::Merge3Failed(format!(
418 "conflict at /{key}: both sides added different values"
419 )));
420 }
421 }
422 (None, Some(lv), None) => {
423 merged.insert(key.clone(), lv.clone());
424 }
425 (None, None, Some(rv)) => {
426 merged.insert(key.clone(), rv.clone());
427 }
428 (Some(_), None, None) => {
429 }
431 (None, None, None) => unreachable!(),
432 }
433 }
434 Ok(Value::Object(merged))
435 }
436 _ => {
437 Err(PatchError::Merge3Failed(
439 "both sides changed scalar/array value differently".to_string(),
440 ))
441 }
442 }
443}
444
445#[cfg(test)]
446mod tests {
447 use super::*;
448 use serde_json::json;
449
450 #[test]
451 fn diff_and_apply_json() {
452 let codec = JsonTreeCodec;
453 let old = serde_json::to_vec(&json!({"a": 1, "b": 2})).unwrap();
454 let new = serde_json::to_vec(&json!({"a": 1, "b": 3, "c": 4})).unwrap();
455 let ops = codec.diff(&old, &new).unwrap();
456 let result = codec.apply(&old, &ops).unwrap();
457 let result_val: Value = serde_json::from_slice(&result).unwrap();
458 assert_eq!(result_val, json!({"a": 1, "b": 3, "c": 4}));
459 }
460
461 #[test]
462 fn json_merge3_no_conflict() {
463 let codec = JsonTreeCodec;
464 let base = serde_json::to_vec(&json!({"a": 1, "b": 2, "c": 3})).unwrap();
465 let left = serde_json::to_vec(&json!({"a": 10, "b": 2, "c": 3})).unwrap();
466 let right = serde_json::to_vec(&json!({"a": 1, "b": 20, "c": 3})).unwrap();
467 let merged = codec.merge3(&base, &left, &right).unwrap();
468 let merged_val: Value = serde_json::from_slice(&merged).unwrap();
469 assert_eq!(merged_val, json!({"a": 10, "b": 20, "c": 3}));
470 }
471
472 #[test]
473 fn json_merge3_both_add_same_key_different_value() {
474 let codec = JsonTreeCodec;
475 let base = serde_json::to_vec(&json!({"a": 1})).unwrap();
476 let left = serde_json::to_vec(&json!({"a": 1, "new": "left"})).unwrap();
477 let right = serde_json::to_vec(&json!({"a": 1, "new": "right"})).unwrap();
478 assert!(codec.merge3(&base, &left, &right).is_err());
479 }
480
481 #[test]
482 fn json_invert_roundtrip() {
483 let codec = JsonTreeCodec;
484 let old = serde_json::to_vec(&json!({"x": 1, "y": 2})).unwrap();
485 let new = serde_json::to_vec(&json!({"x": 10, "y": 2, "z": 3})).unwrap();
486 let ops = codec.diff(&old, &new).unwrap();
487 let applied = codec.apply(&old, &ops).unwrap();
488 let applied_val: Value = serde_json::from_slice(&applied).unwrap();
489 assert_eq!(applied_val, json!({"x": 10, "y": 2, "z": 3}));
490
491 let inv = codec.invert(&ops).unwrap();
492 let restored = codec.apply(&applied, &inv).unwrap();
493 let restored_val: Value = serde_json::from_slice(&restored).unwrap();
494 assert_eq!(restored_val, json!({"x": 1, "y": 2}));
495 }
496
497 #[test]
498 fn path_relation_tests() {
499 assert_eq!(path_relationship("/a/b", "/a/b"), PathRelation::Equal);
500 assert_eq!(path_relationship("/a", "/a/b"), PathRelation::AncestorOf);
501 assert_eq!(path_relationship("/a/b", "/a"), PathRelation::DescendantOf);
502 assert_eq!(
503 path_relationship("/a/0", "/a/1"),
504 PathRelation::SiblingArrayElements
505 );
506 assert_eq!(path_relationship("/a", "/b"), PathRelation::Independent);
507 }
508}