1use crate::diffable::{DiffableValue, GenericPatch, GenericPatchOperation};
32use thiserror::Error;
33
34#[derive(Error, Debug, Clone, PartialEq, Eq)]
40pub enum PatchError {
41 #[error("Path not found: {0}")]
43 PathNotFound(String),
44
45 #[error("Invalid target at {path}: expected {expected}, found {found}")]
47 InvalidTarget {
48 path: String,
50 expected: String,
52 found: String,
54 },
55
56 #[error("Index {index} out of bounds at {path} (length {len})")]
58 IndexOutOfBounds {
59 path: String,
61 index: usize,
63 len: usize,
65 },
66
67 #[error("Test failed at {path}: values do not match")]
69 TestFailed {
70 path: String,
72 },
73
74 #[error("Cannot remove the root element")]
76 CannotRemoveRoot,
77
78 #[error("Invalid path syntax: {0}")]
80 InvalidPath(String),
81
82 #[error("Source path not found: {0}")]
84 SourceNotFound(String),
85}
86
87pub trait Patchable: DiffableValue + Sized {
102 fn apply_operation(&mut self, op: &GenericPatchOperation<Self>) -> Result<(), PatchError>;
108
109 fn get_mut_at_path(&mut self, path: &str) -> Result<&mut Self, PatchError>;
115
116 fn get_at_path(&self, path: &str) -> Result<&Self, PatchError>;
122
123 fn set_at_path(&mut self, path: &str, value: Self) -> Result<(), PatchError>;
129
130 fn remove_at_path(&mut self, path: &str) -> Result<Self, PatchError>;
136
137 fn apply_patch(&mut self, patch: &GenericPatch<Self>) -> Result<(), PatchError> {
146 for op in &patch.operations {
147 self.apply_operation(op)?;
148 }
149 Ok(())
150 }
151}
152
153pub fn parse_pointer(path: &str) -> Result<Vec<String>, PatchError> {
163 if path.is_empty() {
164 return Ok(vec![]);
165 }
166
167 if !path.starts_with('/') {
168 return Err(PatchError::InvalidPath(format!(
169 "JSON Pointer must start with '/': {path}"
170 )));
171 }
172
173 Ok(path[1..].split('/').map(unescape_json_pointer).collect())
174}
175
176fn unescape_json_pointer(s: &str) -> String {
178 s.replace("~1", "/").replace("~0", "~")
179}
180
181fn split_parent_key(path: &str) -> Result<(String, String), PatchError> {
183 let segments = parse_pointer(path)?;
184 if segments.is_empty() {
185 return Err(PatchError::CannotRemoveRoot);
186 }
187
188 let key = segments.last().unwrap().clone();
189 let parent_path = if segments.len() == 1 {
190 String::new()
191 } else {
192 format!(
193 "/{}",
194 segments[..segments.len() - 1]
195 .iter()
196 .map(|s| escape_json_pointer(s))
197 .collect::<Vec<_>>()
198 .join("/")
199 )
200 };
201
202 Ok((parent_path, key))
203}
204
205fn escape_json_pointer(s: &str) -> String {
206 s.replace('~', "~0").replace('/', "~1")
207}
208
209fn insert_into_array(
215 arr: &mut Vec<serde_json::Value>,
216 key: &str,
217 value: &serde_json::Value,
218 path: &str,
219) -> Result<(), PatchError> {
220 if key == "-" {
221 arr.push(value.clone());
222 } else {
223 let index: usize = key
224 .parse()
225 .map_err(|_| PatchError::InvalidPath(format!("Invalid array index: {key}")))?;
226 if index > arr.len() {
227 return Err(PatchError::IndexOutOfBounds {
228 path: path.to_string(),
229 index,
230 len: arr.len(),
231 });
232 }
233 arr.insert(index, value.clone());
234 }
235 Ok(())
236}
237
238fn remove_from_array(
240 arr: &mut Vec<serde_json::Value>,
241 key: &str,
242 path: &str,
243) -> Result<(), PatchError> {
244 let index: usize = key
245 .parse()
246 .map_err(|_| PatchError::InvalidPath(format!("Invalid array index: {key}")))?;
247 if index >= arr.len() {
248 return Err(PatchError::IndexOutOfBounds {
249 path: path.to_string(),
250 index,
251 len: arr.len(),
252 });
253 }
254 arr.remove(index);
255 Ok(())
256}
257
258fn apply_add(
260 doc: &mut serde_json::Value,
261 path: &str,
262 value: &serde_json::Value,
263) -> Result<(), PatchError> {
264 use crate::diffable::DiffableValue;
265
266 if path.is_empty() {
267 *doc = value.clone();
268 return Ok(());
269 }
270
271 let (parent_path, key) = split_parent_key(path)?;
272 let parent = if parent_path.is_empty() {
273 doc
274 } else {
275 <serde_json::Value as Patchable>::get_mut_at_path(doc, &parent_path)?
276 };
277
278 match parent {
279 serde_json::Value::Object(map) => {
280 map.insert(key, value.clone());
281 Ok(())
282 }
283 serde_json::Value::Array(arr) => insert_into_array(arr, &key, value, path),
284 _ => Err(PatchError::InvalidTarget {
285 path: parent_path,
286 expected: "object or array".to_string(),
287 found: format!("{:?}", parent.value_kind()),
288 }),
289 }
290}
291
292fn apply_remove(doc: &mut serde_json::Value, path: &str) -> Result<(), PatchError> {
294 use crate::diffable::DiffableValue;
295
296 if path.is_empty() {
297 return Err(PatchError::CannotRemoveRoot);
298 }
299
300 let (parent_path, key) = split_parent_key(path)?;
301 let parent = if parent_path.is_empty() {
302 doc
303 } else {
304 <serde_json::Value as Patchable>::get_mut_at_path(doc, &parent_path)?
305 };
306
307 match parent {
308 serde_json::Value::Object(map) => {
309 map.remove(&key)
310 .ok_or_else(|| PatchError::PathNotFound(path.to_string()))?;
311 Ok(())
312 }
313 serde_json::Value::Array(arr) => remove_from_array(arr, &key, path),
314 _ => Err(PatchError::InvalidTarget {
315 path: parent_path,
316 expected: "object or array".to_string(),
317 found: format!("{:?}", parent.value_kind()),
318 }),
319 }
320}
321
322fn apply_replace(
324 doc: &mut serde_json::Value,
325 path: &str,
326 value: &serde_json::Value,
327) -> Result<(), PatchError> {
328 if path.is_empty() {
329 *doc = value.clone();
330 return Ok(());
331 }
332
333 let target = <serde_json::Value as Patchable>::get_mut_at_path(doc, path)?;
334 *target = value.clone();
335 Ok(())
336}
337
338impl Patchable for serde_json::Value {
339 fn apply_operation(&mut self, op: &GenericPatchOperation<Self>) -> Result<(), PatchError> {
340 match op {
341 GenericPatchOperation::Add { path, value } => apply_add(self, path, value),
342 GenericPatchOperation::Remove { path } => apply_remove(self, path),
343 GenericPatchOperation::Replace { path, value } => apply_replace(self, path, value),
344 GenericPatchOperation::Move { from, path } => {
345 let val = self.remove_at_path(from)?;
346 self.set_at_path(path, val)
347 }
348 GenericPatchOperation::Copy { from, path } => {
349 let val = self.get_at_path(from)?.clone();
350 self.set_at_path(path, val)
351 }
352 GenericPatchOperation::Test { path, value } => {
353 let actual = self.get_at_path(path)?;
354 if actual == value {
355 Ok(())
356 } else {
357 Err(PatchError::TestFailed { path: path.clone() })
358 }
359 }
360 }
361 }
362
363 fn get_mut_at_path(&mut self, path: &str) -> Result<&mut Self, PatchError> {
364 let segments = parse_pointer(path)?;
365 let mut current = self;
366
367 for (i, segment) in segments.iter().enumerate() {
368 let path_so_far = format!(
369 "/{}",
370 segments[..=i]
371 .iter()
372 .map(|s| escape_json_pointer(s))
373 .collect::<Vec<_>>()
374 .join("/")
375 );
376
377 current = match current {
378 Self::Object(map) => map
379 .get_mut(segment)
380 .ok_or(PatchError::PathNotFound(path_so_far))?,
381 Self::Array(arr) => {
382 let index: usize = segment.parse().map_err(|_| {
383 PatchError::InvalidPath(format!("Invalid array index: {segment}"))
384 })?;
385 let arr_len = arr.len();
386 arr.get_mut(index).ok_or(PatchError::IndexOutOfBounds {
387 path: path_so_far,
388 index,
389 len: arr_len,
390 })?
391 }
392 _ => {
393 return Err(PatchError::InvalidTarget {
394 path: path_so_far,
395 expected: "object or array".to_string(),
396 found: format!("{:?}", current.value_kind()),
397 });
398 }
399 };
400 }
401
402 Ok(current)
403 }
404
405 fn get_at_path(&self, path: &str) -> Result<&Self, PatchError> {
406 let segments = parse_pointer(path)?;
407 let mut current = self;
408
409 for (i, segment) in segments.iter().enumerate() {
410 let path_so_far = format!(
411 "/{}",
412 segments[..=i]
413 .iter()
414 .map(|s| escape_json_pointer(s))
415 .collect::<Vec<_>>()
416 .join("/")
417 );
418
419 current = match current {
420 Self::Object(map) => map
421 .get(segment)
422 .ok_or(PatchError::PathNotFound(path_so_far))?,
423 Self::Array(arr) => {
424 let index: usize = segment.parse().map_err(|_| {
425 PatchError::InvalidPath(format!("Invalid array index: {segment}"))
426 })?;
427 arr.get(index).ok_or(PatchError::IndexOutOfBounds {
428 path: path_so_far,
429 index,
430 len: arr.len(),
431 })?
432 }
433 _ => {
434 return Err(PatchError::InvalidTarget {
435 path: path_so_far,
436 expected: "object or array".to_string(),
437 found: format!("{:?}", current.value_kind()),
438 });
439 }
440 };
441 }
442
443 Ok(current)
444 }
445
446 fn set_at_path(&mut self, path: &str, value: Self) -> Result<(), PatchError> {
447 if path.is_empty() {
448 *self = value;
449 return Ok(());
450 }
451
452 let (parent_path, key) = split_parent_key(path)?;
453 let parent = if parent_path.is_empty() {
454 self
455 } else {
456 self.get_mut_at_path(&parent_path)?
457 };
458
459 match parent {
460 Self::Object(map) => {
461 map.insert(key, value);
462 Ok(())
463 }
464 Self::Array(arr) => {
465 let index: usize = key
466 .parse()
467 .map_err(|_| PatchError::InvalidPath(format!("Invalid array index: {key}")))?;
468 while arr.len() <= index {
469 arr.push(Self::Null);
470 }
471 arr[index] = value;
472 Ok(())
473 }
474 _ => Err(PatchError::InvalidTarget {
475 path: parent_path,
476 expected: "object or array".to_string(),
477 found: format!("{:?}", parent.value_kind()),
478 }),
479 }
480 }
481
482 fn remove_at_path(&mut self, path: &str) -> Result<Self, PatchError> {
483 if path.is_empty() {
484 return Err(PatchError::CannotRemoveRoot);
485 }
486
487 let (parent_path, key) = split_parent_key(path)?;
488 let parent = if parent_path.is_empty() {
489 self
490 } else {
491 self.get_mut_at_path(&parent_path)?
492 };
493
494 match parent {
495 Self::Object(map) => map
496 .remove(&key)
497 .ok_or_else(|| PatchError::PathNotFound(path.to_string())),
498 Self::Array(arr) => {
499 let index: usize = key
500 .parse()
501 .map_err(|_| PatchError::InvalidPath(format!("Invalid array index: {key}")))?;
502 if index >= arr.len() {
503 return Err(PatchError::IndexOutOfBounds {
504 path: path.to_string(),
505 index,
506 len: arr.len(),
507 });
508 }
509 Ok(arr.remove(index))
510 }
511 _ => Err(PatchError::InvalidTarget {
512 path: parent_path,
513 expected: "object or array".to_string(),
514 found: format!("{:?}", parent.value_kind()),
515 }),
516 }
517 }
518}
519
520pub fn apply_patch<V: Patchable>(value: &mut V, patch: &GenericPatch<V>) -> Result<(), PatchError> {
532 value.apply_patch(patch)
533}
534
535pub fn apply_operation<V: Patchable>(
543 value: &mut V,
544 op: &GenericPatchOperation<V>,
545) -> Result<(), PatchError> {
546 value.apply_operation(op)
547}
548
549#[cfg(test)]
554mod tests {
555 use super::*;
556 use serde_json::json;
557
558 #[test]
559 fn test_parse_pointer_empty() {
560 let segments = parse_pointer("").unwrap();
561 assert!(segments.is_empty());
562 }
563
564 #[test]
565 fn test_parse_pointer_root() {
566 let segments = parse_pointer("/").unwrap();
567 assert_eq!(segments, vec![""]);
568 }
569
570 #[test]
571 fn test_parse_pointer_simple() {
572 let segments = parse_pointer("/foo/bar").unwrap();
573 assert_eq!(segments, vec!["foo", "bar"]);
574 }
575
576 #[test]
577 fn test_parse_pointer_escaped() {
578 let segments = parse_pointer("/a~1b/c~0d").unwrap();
579 assert_eq!(segments, vec!["a/b", "c~d"]);
580 }
581
582 #[test]
583 fn test_parse_pointer_invalid() {
584 assert!(parse_pointer("no-leading-slash").is_err());
585 }
586
587 #[test]
588 fn test_get_at_path_simple() {
589 let value = json!({"name": "Alice"});
590 let name = value.get_at_path("/name").unwrap();
591 assert_eq!(name, &json!("Alice"));
592 }
593
594 #[test]
595 fn test_get_at_path_nested() {
596 let value = json!({"user": {"name": "Alice"}});
597 let name = value.get_at_path("/user/name").unwrap();
598 assert_eq!(name, &json!("Alice"));
599 }
600
601 #[test]
602 fn test_get_at_path_array() {
603 let value = json!([1, 2, 3]);
604 assert_eq!(value.get_at_path("/0").unwrap(), &json!(1));
605 assert_eq!(value.get_at_path("/2").unwrap(), &json!(3));
606 }
607
608 #[test]
609 fn test_get_at_path_not_found() {
610 let value = json!({"name": "Alice"});
611 assert!(matches!(
612 value.get_at_path("/age"),
613 Err(PatchError::PathNotFound(_))
614 ));
615 }
616
617 #[test]
618 fn test_apply_add_to_object() {
619 let mut value = json!({"name": "Alice"});
620 let op = GenericPatchOperation::Add {
621 path: "/age".to_string(),
622 value: json!(30),
623 };
624
625 value.apply_operation(&op).unwrap();
626 assert_eq!(value["age"], json!(30));
627 }
628
629 #[test]
630 fn test_apply_add_to_array() {
631 let mut value = json!([1, 2]);
632 let op = GenericPatchOperation::Add {
633 path: "/-".to_string(),
634 value: json!(3),
635 };
636
637 value.apply_operation(&op).unwrap();
638 assert_eq!(value, json!([1, 2, 3]));
639 }
640
641 #[test]
642 fn test_apply_add_to_array_at_index() {
643 let mut value = json!([1, 3]);
644 let op = GenericPatchOperation::Add {
645 path: "/1".to_string(),
646 value: json!(2),
647 };
648
649 value.apply_operation(&op).unwrap();
650 assert_eq!(value, json!([1, 2, 3]));
651 }
652
653 #[test]
654 fn test_apply_remove_from_object() {
655 let mut value = json!({"name": "Alice", "age": 30});
656 let op = GenericPatchOperation::Remove {
657 path: "/age".to_string(),
658 };
659
660 value.apply_operation(&op).unwrap();
661 assert_eq!(value, json!({"name": "Alice"}));
662 }
663
664 #[test]
665 fn test_apply_remove_from_array() {
666 let mut value = json!([1, 2, 3]);
667 let op = GenericPatchOperation::Remove {
668 path: "/1".to_string(),
669 };
670
671 value.apply_operation(&op).unwrap();
672 assert_eq!(value, json!([1, 3]));
673 }
674
675 #[test]
676 fn test_apply_replace() {
677 let mut value = json!({"name": "Alice"});
678 let op = GenericPatchOperation::Replace {
679 path: "/name".to_string(),
680 value: json!("Bob"),
681 };
682
683 value.apply_operation(&op).unwrap();
684 assert_eq!(value["name"], json!("Bob"));
685 }
686
687 #[test]
688 fn test_apply_replace_root() {
689 let mut value = json!({"old": "data"});
690 let op = GenericPatchOperation::Replace {
691 path: String::new(),
692 value: json!({"new": "data"}),
693 };
694
695 value.apply_operation(&op).unwrap();
696 assert_eq!(value, json!({"new": "data"}));
697 }
698
699 #[test]
700 fn test_apply_move() {
701 let mut value = json!({"first": "Alice", "last": "Smith"});
702 let op = GenericPatchOperation::Move {
703 from: "/first".to_string(),
704 path: "/name".to_string(),
705 };
706
707 value.apply_operation(&op).unwrap();
708 assert!(value.get("first").is_none());
709 assert_eq!(value["name"], json!("Alice"));
710 }
711
712 #[test]
713 fn test_apply_copy() {
714 let mut value = json!({"name": "Alice"});
715 let op = GenericPatchOperation::Copy {
716 from: "/name".to_string(),
717 path: "/alias".to_string(),
718 };
719
720 value.apply_operation(&op).unwrap();
721 assert_eq!(value["name"], json!("Alice"));
722 assert_eq!(value["alias"], json!("Alice"));
723 }
724
725 #[test]
726 fn test_apply_test_success() {
727 let mut value = json!({"name": "Alice"});
728 let op = GenericPatchOperation::Test {
729 path: "/name".to_string(),
730 value: json!("Alice"),
731 };
732
733 assert!(value.apply_operation(&op).is_ok());
734 }
735
736 #[test]
737 fn test_apply_test_failure() {
738 let mut value = json!({"name": "Alice"});
739 let op = GenericPatchOperation::Test {
740 path: "/name".to_string(),
741 value: json!("Bob"),
742 };
743
744 assert!(matches!(
745 value.apply_operation(&op),
746 Err(PatchError::TestFailed { .. })
747 ));
748 }
749
750 #[test]
751 fn test_apply_patch() {
752 let mut value = json!({"name": "Alice"});
753 let patch = GenericPatch::with_operations(vec![
754 GenericPatchOperation::Add {
755 path: "/age".to_string(),
756 value: json!(30),
757 },
758 GenericPatchOperation::Replace {
759 path: "/name".to_string(),
760 value: json!("Alice Smith"),
761 },
762 ]);
763
764 apply_patch(&mut value, &patch).unwrap();
765 assert_eq!(value["name"], json!("Alice Smith"));
766 assert_eq!(value["age"], json!(30));
767 }
768
769 #[test]
770 fn test_remove_root_error() {
771 let mut value = json!({"name": "Alice"});
772 let op: GenericPatchOperation<serde_json::Value> = GenericPatchOperation::Remove {
773 path: String::new(),
774 };
775
776 assert!(matches!(
777 value.apply_operation(&op),
778 Err(PatchError::CannotRemoveRoot)
779 ));
780 }
781
782 #[test]
783 fn test_set_at_path() {
784 let mut value = json!({"user": {}});
785 value.set_at_path("/user/name", json!("Alice")).unwrap();
786 assert_eq!(value["user"]["name"], json!("Alice"));
787 }
788
789 #[test]
790 fn test_remove_at_path() {
791 let mut value = json!({"name": "Alice", "age": 30});
792 let removed = value.remove_at_path("/age").unwrap();
793 assert_eq!(removed, json!(30));
794 assert!(value.get("age").is_none());
795 }
796}