1pub use self::diff::diff;
70use crate::value_map::ValueMap;
71use crate::{Value, ValueMapHelper};
72use serde::{Deserialize, Serialize};
73use std::borrow::Cow;
74use thiserror::Error;
75mod diff;
76
77#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
79pub struct Patch(pub Vec<PatchOperation>);
80
81impl std::ops::Deref for Patch {
82 type Target = [PatchOperation];
83
84 fn deref(&self) -> &[PatchOperation] {
85 &self.0
86 }
87}
88
89#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
91pub struct AddOperation {
92 pub path: String,
93 pub value: Value,
95}
96
97#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
99pub struct RemoveOperation {
100 pub path: String,
101}
102
103#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
105pub struct ReplaceOperation {
106 pub path: String,
108 pub value: Value,
110}
111
112#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
114pub struct MoveOperation {
115 pub from: String,
117 pub path: String,
119}
120
121#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
123pub struct CopyOperation {
124 pub from: String,
126 pub path: String,
128}
129
130#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
132pub struct TestOperation {
133 pub path: String,
135 pub value: Value,
137}
138
139#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
141#[serde(tag = "op")]
142#[serde(rename_all = "lowercase")]
143pub enum PatchOperation {
144 Add(AddOperation),
146 Remove(RemoveOperation),
148 Replace(ReplaceOperation),
150 Move(MoveOperation),
152 Copy(CopyOperation),
154 Test(TestOperation),
156}
157
158#[derive(Debug, Error)]
160#[non_exhaustive]
161pub enum PatchErrorKind {
162 #[error("value did not match")]
164 TestFailed,
165 #[error("\"from\" path is invalid")]
167 InvalidFromPointer,
168 #[error("path is invalid")]
170 InvalidPointer,
171 #[error("cannot move the value inside itself")]
173 CannotMoveInsideItself,
174}
175
176#[derive(Debug, Error)]
178#[error("Operation '/{operation}' failed at path '{path}': {kind}")]
179#[non_exhaustive]
180pub struct PatchError {
181 pub operation: usize,
183 pub path: String,
185 pub kind: PatchErrorKind,
187}
188
189fn translate_error(kind: PatchErrorKind, operation: usize, path: &str) -> PatchError {
190 PatchError {
191 operation,
192 path: path.to_owned(),
193 kind,
194 }
195}
196
197fn unescape(s: &str) -> Cow<'_, str> {
198 if s.contains('~') {
199 Cow::Owned(s.replace("~1", "/").replace("~0", "~"))
200 } else {
201 Cow::Borrowed(s)
202 }
203}
204
205fn parse_index(str: &str, len: usize) -> Result<usize, PatchErrorKind> {
206 if (str.starts_with('0') && str.len() != 1) || str.starts_with('+') {
208 return Err(PatchErrorKind::InvalidPointer);
209 }
210 match str.parse::<usize>() {
211 Ok(index) if index < len => Ok(index),
212 _ => Err(PatchErrorKind::InvalidPointer),
213 }
214}
215
216fn split_pointer(pointer: &str) -> Result<(&str, &str), PatchErrorKind> {
217 pointer
218 .rfind('/')
219 .ok_or(PatchErrorKind::InvalidPointer)
220 .map(|idx| (&pointer[0..idx], &pointer[idx + 1..]))
221}
222
223fn add(doc: &mut Value, path: &str, value: Value) -> Result<Option<Value>, PatchErrorKind> {
224 if path.is_empty() {
225 return Ok(Some(std::mem::replace(doc, value)));
226 }
227
228 let (parent, last_unescaped) = split_pointer(path)?;
229 let parent = doc
230 .pointer_mut(parent)
231 .ok_or(PatchErrorKind::InvalidPointer)?;
232
233 match *parent {
234 Value::Map(ref mut obj) => {
235 obj.insert_string_key_value(unescape(last_unescaped).into_owned(), value.clone());
236 Ok(Some(value))
237 }
238 Value::Array(ref mut arr) if last_unescaped == "-" => {
239 arr.push(value);
240 Ok(None)
241 }
242 Value::Array(ref mut arr) => {
243 let idx = parse_index(last_unescaped, arr.len() + 1)?;
244 arr.insert(idx, value);
245 Ok(None)
246 }
247 _ => Err(PatchErrorKind::InvalidPointer),
248 }
249}
250
251fn remove(doc: &mut Value, path: &str, allow_last: bool) -> Result<Value, PatchErrorKind> {
252 let (parent, last_unescaped) = split_pointer(path)?;
253 let parent = doc
254 .pointer_mut(parent)
255 .ok_or(PatchErrorKind::InvalidPointer)?;
256
257 match *parent {
258 Value::Map(ref mut obj) => match obj.remove_optional_key(unescape(last_unescaped).as_ref())
259 {
260 None => Err(PatchErrorKind::InvalidPointer),
261 Some(val) => Ok(val),
262 },
263 Value::Array(ref mut arr) if allow_last && last_unescaped == "-" => Ok(arr.pop().unwrap()),
264 Value::Array(ref mut arr) => {
265 let idx = parse_index(last_unescaped, arr.len())?;
266 Ok(arr.remove(idx))
267 }
268 _ => Err(PatchErrorKind::InvalidPointer),
269 }
270}
271
272fn replace(doc: &mut Value, path: &str, value: Value) -> Result<Value, PatchErrorKind> {
273 let target = doc
274 .pointer_mut(path)
275 .ok_or(PatchErrorKind::InvalidPointer)?;
276 Ok(std::mem::replace(target, value))
277}
278
279fn mov(
280 doc: &mut Value,
281 from: &str,
282 path: &str,
283 allow_last: bool,
284) -> Result<Option<Value>, PatchErrorKind> {
285 if path.starts_with(from) && path[from.len()..].starts_with('/') {
287 return Err(PatchErrorKind::CannotMoveInsideItself);
288 }
289 let val = remove(doc, from, allow_last).map_err(|err| match err {
290 PatchErrorKind::InvalidPointer => PatchErrorKind::InvalidFromPointer,
291 err => err,
292 })?;
293 add(doc, path, val)
294}
295
296fn copy(doc: &mut Value, from: &str, path: &str) -> Result<Option<Value>, PatchErrorKind> {
297 let source = doc
298 .pointer(from)
299 .ok_or(PatchErrorKind::InvalidFromPointer)?
300 .clone();
301 add(doc, path, source)
302}
303
304fn test(doc: &Value, path: &str, expected: &Value) -> Result<(), PatchErrorKind> {
305 let target = doc.pointer(path).ok_or(PatchErrorKind::InvalidPointer)?;
306 if *target == *expected {
307 Ok(())
308 } else {
309 Err(PatchErrorKind::TestFailed)
310 }
311}
312
313pub fn patch(doc: &mut Value, patch: &[PatchOperation]) -> Result<(), PatchError> {
344 apply_patches(doc, 0, patch)
345}
346
347fn apply_patches(
350 doc: &mut Value,
351 operation: usize,
352 patches: &[PatchOperation],
353) -> Result<(), PatchError> {
354 let (patch, tail) = match patches.split_first() {
355 None => return Ok(()),
356 Some((patch, tail)) => (patch, tail),
357 };
358
359 match *patch {
360 PatchOperation::Add(ref op) => {
361 let prev = add(doc, &op.path, op.value.clone())
362 .map_err(|e| translate_error(e, operation, &op.path))?;
363 apply_patches(doc, operation + 1, tail).inspect_err(move |_| {
364 match prev {
365 None => remove(doc, &op.path, true).unwrap(),
366 Some(v) => add(doc, &op.path, v).unwrap().unwrap(),
367 };
368 })
369 }
370 PatchOperation::Remove(ref op) => {
371 let prev = remove(doc, &op.path, false)
372 .map_err(|e| translate_error(e, operation, &op.path))?;
373 apply_patches(doc, operation + 1, tail).inspect_err(move |_| {
374 assert!(add(doc, &op.path, prev).unwrap().is_none());
375 })
376 }
377 PatchOperation::Replace(ref op) => {
378 let prev = replace(doc, &op.path, op.value.clone())
379 .map_err(|e| translate_error(e, operation, &op.path))?;
380 apply_patches(doc, operation + 1, tail).inspect_err(move |_| {
381 replace(doc, &op.path, prev).unwrap();
382 })
383 }
384 PatchOperation::Move(ref op) => {
385 let prev = mov(doc, op.from.as_str(), &op.path, false)
386 .map_err(|e| translate_error(e, operation, &op.path))?;
387 apply_patches(doc, operation + 1, tail).inspect_err(move |_| {
388 mov(doc, &op.path, op.from.as_str(), true).unwrap();
389 if let Some(prev) = prev {
390 assert!(add(doc, &op.path, prev).unwrap().is_none());
391 }
392 })
393 }
394 PatchOperation::Copy(ref op) => {
395 let prev = copy(doc, op.from.as_str(), &op.path)
396 .map_err(|e| translate_error(e, operation, &op.path))?;
397 apply_patches(doc, operation + 1, tail).inspect_err(move |_| {
398 match prev {
399 None => remove(doc, &op.path, true).unwrap(),
400 Some(v) => add(doc, &op.path, v).unwrap().unwrap(),
401 };
402 })
403 }
404 PatchOperation::Test(ref op) => {
405 test(doc, &op.path, &op.value).map_err(|e| translate_error(e, operation, &op.path))?;
406 apply_patches(doc, operation + 1, tail)
407 }
408 }
409}
410
411pub fn merge(doc: &mut Value, patch: &Value) {
455 if !patch.is_map() {
456 *doc = patch.clone();
457 return;
458 }
459
460 if !doc.is_map() {
461 *doc = Value::Map(ValueMap::new());
462 }
463 let map = doc.as_map_mut().unwrap();
464 for (key, value) in patch.as_map().unwrap() {
465 if value.is_null() {
466 map.remove_optional_key_value(key);
467 } else {
468 merge(map.get_key_by_value_mut_or_insert(key, Value::Null), value);
469 }
470 }
471}
472
473#[cfg(test)]
474mod tests {
475 use super::*;
476 use crate::{from_value, platform_value};
477
478 #[test]
483 fn add_to_map_key() {
484 let mut doc = platform_value!({"a": 1});
485 let p: Patch = from_value(platform_value!([
486 { "op": "add", "path": "/b", "value": 2 }
487 ]))
488 .unwrap();
489 patch(&mut doc, &p).unwrap();
490 assert_eq!(doc.pointer("/b"), Some(&platform_value!(2)));
491 }
492
493 #[test]
494 fn add_to_array_push_with_dash() {
495 let mut doc = platform_value!({"arr": [1, 2]});
496 let p: Patch = from_value(platform_value!([
497 { "op": "add", "path": "/arr/-", "value": 3 }
498 ]))
499 .unwrap();
500 patch(&mut doc, &p).unwrap();
501 assert_eq!(doc, platform_value!({"arr": [1, 2, 3]}));
502 }
503
504 #[test]
505 fn add_to_array_insert_at_index() {
506 let mut doc = platform_value!({"arr": [1, 3]});
507 let p: Patch = from_value(platform_value!([
508 { "op": "add", "path": "/arr/1", "value": 2 }
509 ]))
510 .unwrap();
511 patch(&mut doc, &p).unwrap();
512 assert_eq!(doc, platform_value!({"arr": [1, 2, 3]}));
513 }
514
515 #[test]
516 fn add_empty_path_replaces_whole_document() {
517 let mut doc = platform_value!({"old": "value"});
518 let p: Patch = from_value(platform_value!([
519 { "op": "add", "path": "", "value": "replaced" }
520 ]))
521 .unwrap();
522 patch(&mut doc, &p).unwrap();
523 assert_eq!(doc, platform_value!("replaced"));
524 }
525
526 #[test]
527 fn add_to_nested_map() {
528 let mut doc = platform_value!({"a": {"b": 1}});
529 let p: Patch = from_value(platform_value!([
530 { "op": "add", "path": "/a/c", "value": 2 }
531 ]))
532 .unwrap();
533 patch(&mut doc, &p).unwrap();
534 assert_eq!(doc.pointer("/a/c"), Some(&platform_value!(2)));
535 }
536
537 #[test]
538 fn add_at_array_beginning() {
539 let mut doc = platform_value!([2, 3]);
540 let p: Patch = from_value(platform_value!([
541 { "op": "add", "path": "/0", "value": 1 }
542 ]))
543 .unwrap();
544 patch(&mut doc, &p).unwrap();
545 assert_eq!(doc, platform_value!([1, 2, 3]));
546 }
547
548 #[test]
553 fn remove_from_map() {
554 let mut doc = platform_value!({"a": 1, "b": 2});
555 let p: Patch = from_value(platform_value!([
556 { "op": "remove", "path": "/a" }
557 ]))
558 .unwrap();
559 patch(&mut doc, &p).unwrap();
560 assert_eq!(doc.pointer("/a"), None);
561 assert_eq!(doc.pointer("/b"), Some(&platform_value!(2)));
562 }
563
564 #[test]
565 fn remove_from_array_by_index() {
566 let mut doc = platform_value!({"arr": [1, 2, 3]});
567 let p: Patch = from_value(platform_value!([
568 { "op": "remove", "path": "/arr/1" }
569 ]))
570 .unwrap();
571 patch(&mut doc, &p).unwrap();
572 assert_eq!(doc, platform_value!({"arr": [1, 3]}));
573 }
574
575 #[test]
576 fn remove_missing_key_errors() {
577 let mut doc = platform_value!({"a": 1});
578 let p: Patch = from_value(platform_value!([
579 { "op": "remove", "path": "/nonexistent" }
580 ]))
581 .unwrap();
582 let err = patch(&mut doc, &p).unwrap_err();
583 assert!(matches!(err.kind, PatchErrorKind::InvalidPointer));
584 }
585
586 #[test]
587 fn remove_invalid_array_index_errors() {
588 let mut doc = platform_value!({"arr": [1]});
589 let p: Patch = from_value(platform_value!([
590 { "op": "remove", "path": "/arr/5" }
591 ]))
592 .unwrap();
593 let err = patch(&mut doc, &p).unwrap_err();
594 assert!(matches!(err.kind, PatchErrorKind::InvalidPointer));
595 }
596
597 #[test]
602 fn replace_existing_key() {
603 let mut doc = platform_value!({"a": 1});
604 let p: Patch = from_value(platform_value!([
605 { "op": "replace", "path": "/a", "value": 99 }
606 ]))
607 .unwrap();
608 patch(&mut doc, &p).unwrap();
609 assert_eq!(doc, platform_value!({"a": 99}));
610 }
611
612 #[test]
613 fn replace_missing_key_errors() {
614 let mut doc = platform_value!({"a": 1});
615 let p: Patch = from_value(platform_value!([
616 { "op": "replace", "path": "/b", "value": 2 }
617 ]))
618 .unwrap();
619 let err = patch(&mut doc, &p).unwrap_err();
620 assert!(matches!(err.kind, PatchErrorKind::InvalidPointer));
621 }
622
623 #[test]
624 fn replace_root_document() {
625 let mut doc = platform_value!({"a": 1});
626 let p: Patch = from_value(platform_value!([
627 { "op": "replace", "path": "", "value": [1, 2, 3] }
628 ]))
629 .unwrap();
630 patch(&mut doc, &p).unwrap();
631 assert_eq!(doc, platform_value!([1, 2, 3]));
632 }
633
634 #[test]
639 fn move_between_map_keys() {
640 let mut doc = platform_value!({"a": 1, "b": 2});
641 let p: Patch = from_value(platform_value!([
642 { "op": "move", "from": "/a", "path": "/c" }
643 ]))
644 .unwrap();
645 patch(&mut doc, &p).unwrap();
646 assert_eq!(doc.pointer("/a"), None);
647 assert_eq!(doc.pointer("/c"), Some(&platform_value!(1)));
648 assert_eq!(doc.pointer("/b"), Some(&platform_value!(2)));
649 }
650
651 #[test]
652 fn move_inside_self_errors() {
653 let mut doc = platform_value!({"a": {"b": 1}});
654 let p: Patch = from_value(platform_value!([
655 { "op": "move", "from": "/a", "path": "/a/b/c" }
656 ]))
657 .unwrap();
658 let err = patch(&mut doc, &p).unwrap_err();
659 assert!(matches!(err.kind, PatchErrorKind::CannotMoveInsideItself));
660 }
661
662 #[test]
663 fn move_from_invalid_path_errors() {
664 let mut doc = platform_value!({"a": 1});
665 let p: Patch = from_value(platform_value!([
666 { "op": "move", "from": "/nonexistent", "path": "/b" }
667 ]))
668 .unwrap();
669 let err = patch(&mut doc, &p).unwrap_err();
670 assert!(matches!(err.kind, PatchErrorKind::InvalidFromPointer));
671 }
672
673 #[test]
678 fn copy_between_map_keys() {
679 let mut doc = platform_value!({"a": 1});
680 let p: Patch = from_value(platform_value!([
681 { "op": "copy", "from": "/a", "path": "/b" }
682 ]))
683 .unwrap();
684 patch(&mut doc, &p).unwrap();
685 assert_eq!(doc.pointer("/a"), Some(&platform_value!(1)));
686 assert_eq!(doc.pointer("/b"), Some(&platform_value!(1)));
687 }
688
689 #[test]
690 fn copy_from_invalid_path_errors() {
691 let mut doc = platform_value!({"a": 1});
692 let p: Patch = from_value(platform_value!([
693 { "op": "copy", "from": "/missing", "path": "/b" }
694 ]))
695 .unwrap();
696 let err = patch(&mut doc, &p).unwrap_err();
697 assert!(matches!(err.kind, PatchErrorKind::InvalidFromPointer));
698 }
699
700 #[test]
701 fn copy_nested_value() {
702 let mut doc = platform_value!({"a": {"x": 10}});
703 let p: Patch = from_value(platform_value!([
704 { "op": "copy", "from": "/a", "path": "/b" }
705 ]))
706 .unwrap();
707 patch(&mut doc, &p).unwrap();
708 assert_eq!(doc.pointer("/b/x"), Some(&platform_value!(10)));
709 }
710
711 #[test]
716 fn test_matching_value_succeeds() {
717 let mut doc = platform_value!({"a": "hello"});
718 let p: Patch = from_value(platform_value!([
719 { "op": "test", "path": "/a", "value": "hello" }
720 ]))
721 .unwrap();
722 patch(&mut doc, &p).unwrap();
723 }
724
725 #[test]
726 fn test_mismatched_value_fails() {
727 let mut doc = platform_value!({"a": "hello"});
728 let p: Patch = from_value(platform_value!([
729 { "op": "test", "path": "/a", "value": "world" }
730 ]))
731 .unwrap();
732 let err = patch(&mut doc, &p).unwrap_err();
733 assert!(matches!(err.kind, PatchErrorKind::TestFailed));
734 }
735
736 #[test]
737 fn test_missing_path_errors() {
738 let mut doc = platform_value!({"a": 1});
739 let p: Patch = from_value(platform_value!([
740 { "op": "test", "path": "/nope", "value": 1 }
741 ]))
742 .unwrap();
743 let err = patch(&mut doc, &p).unwrap_err();
744 assert!(matches!(err.kind, PatchErrorKind::InvalidPointer));
745 }
746
747 #[test]
752 fn apply_patches_multi_operation() {
753 let mut doc = platform_value!({"a": 1});
754 let p: Patch = from_value(platform_value!([
755 { "op": "add", "path": "/b", "value": 2 },
756 { "op": "replace", "path": "/a", "value": 10 },
757 { "op": "remove", "path": "/b" }
758 ]))
759 .unwrap();
760 patch(&mut doc, &p).unwrap();
761 assert_eq!(doc, platform_value!({"a": 10}));
762 }
763
764 #[test]
765 fn apply_patches_rollback_add_new_map_key_on_failure() {
766 let mut doc = platform_value!({"a": 1});
771 let p: Patch = from_value(platform_value!([
772 { "op": "add", "path": "/b", "value": 2 },
773 { "op": "test", "path": "/a", "value": 999 }
774 ]))
775 .unwrap();
776 assert!(patch(&mut doc, &p).is_err());
778 }
781
782 #[test]
783 fn apply_patches_rollback_add_array_on_failure() {
784 let mut doc = platform_value!([1, 2, 3]);
786 let original = doc.clone();
787 let p: Patch = from_value(platform_value!([
788 { "op": "add", "path": "/1", "value": 99 },
789 { "op": "test", "path": "/0", "value": 999 }
790 ]))
791 .unwrap();
792 assert!(patch(&mut doc, &p).is_err());
793 assert_eq!(doc, original);
794 }
795
796 #[test]
797 fn apply_patches_rollback_replace_on_failure() {
798 let mut doc = platform_value!({"a": 1, "b": 2});
799 let original = doc.clone();
800 let p: Patch = from_value(platform_value!([
801 { "op": "replace", "path": "/a", "value": 100 },
802 { "op": "test", "path": "/b", "value": 999 }
803 ]))
804 .unwrap();
805 assert!(patch(&mut doc, &p).is_err());
806 assert_eq!(doc, original);
807 }
808
809 #[test]
810 fn apply_patches_rollback_remove_array_on_failure() {
811 let mut doc = platform_value!([1, 2, 3]);
812 let original = doc.clone();
813 let p: Patch = from_value(platform_value!([
814 { "op": "remove", "path": "/1" },
815 { "op": "test", "path": "/0", "value": 999 }
816 ]))
817 .unwrap();
818 assert!(patch(&mut doc, &p).is_err());
819 assert_eq!(doc, original);
820 }
821
822 #[test]
823 fn apply_patches_rollback_copy_array_on_failure() {
824 let mut doc = platform_value!({"items": [10, 20]});
825 let original = doc.clone();
826 let p: Patch = from_value(platform_value!([
827 { "op": "copy", "from": "/items/0", "path": "/items/-" },
828 { "op": "test", "path": "/items/0", "value": 999 }
829 ]))
830 .unwrap();
831 assert!(patch(&mut doc, &p).is_err());
832 assert_eq!(doc, original);
833 }
834
835 #[test]
836 fn apply_patches_empty_patch_list() {
837 let mut doc = platform_value!({"a": 1});
838 let p = Patch(vec![]);
839 patch(&mut doc, &p).unwrap();
840 assert_eq!(doc, platform_value!({"a": 1}));
841 }
842
843 #[test]
848 fn merge_recursive_map() {
849 let mut doc = platform_value!({
850 "a": { "b": 1, "c": 2 }
851 });
852 let p = platform_value!({
853 "a": { "b": 10, "d": 3 }
854 });
855 merge(&mut doc, &p);
856 assert_eq!(doc.pointer("/a/b"), Some(&platform_value!(10)));
857 assert_eq!(doc.pointer("/a/c"), Some(&platform_value!(2)));
858 assert_eq!(doc.pointer("/a/d"), Some(&platform_value!(3)));
859 }
860
861 #[test]
862 fn merge_null_removes_key() {
863 let mut doc = platform_value!({"a": 1, "b": 2});
864 let p = platform_value!({"a": null});
865 merge(&mut doc, &p);
866 assert_eq!(doc.pointer("/a"), None);
867 assert_eq!(doc.pointer("/b"), Some(&platform_value!(2)));
868 }
869
870 #[test]
871 fn merge_non_map_patch_replaces_entire_document() {
872 let mut doc = platform_value!({"a": 1});
873 let p = platform_value!("replaced");
874 merge(&mut doc, &p);
875 assert_eq!(doc, platform_value!("replaced"));
876 }
877
878 #[test]
879 fn merge_into_non_map_doc_creates_map() {
880 let mut doc = platform_value!("not a map");
881 let p = platform_value!({"x": 1});
882 merge(&mut doc, &p);
883 assert_eq!(doc.pointer("/x"), Some(&platform_value!(1)));
884 }
885
886 #[test]
887 fn merge_adds_new_keys() {
888 let mut doc = platform_value!({"a": 1});
889 let p = platform_value!({"b": 2});
890 merge(&mut doc, &p);
891 assert_eq!(doc.pointer("/a"), Some(&platform_value!(1)));
892 assert_eq!(doc.pointer("/b"), Some(&platform_value!(2)));
893 }
894
895 #[test]
896 fn merge_replaces_array_entirely() {
897 let mut doc = platform_value!({"tags": [1, 2, 3]});
898 let p = platform_value!({"tags": [4]});
899 merge(&mut doc, &p);
900 assert_eq!(doc.pointer("/tags"), Some(&platform_value!([4])));
901 }
902
903 #[test]
908 fn parse_index_valid() {
909 assert_eq!(parse_index("0", 5).unwrap(), 0);
910 assert_eq!(parse_index("3", 5).unwrap(), 3);
911 assert_eq!(parse_index("4", 5).unwrap(), 4);
912 }
913
914 #[test]
915 fn parse_index_leading_zero_errors() {
916 assert!(matches!(
917 parse_index("01", 5),
918 Err(PatchErrorKind::InvalidPointer)
919 ));
920 }
921
922 #[test]
923 fn parse_index_leading_plus_errors() {
924 assert!(matches!(
925 parse_index("+1", 5),
926 Err(PatchErrorKind::InvalidPointer)
927 ));
928 }
929
930 #[test]
931 fn parse_index_out_of_bounds_errors() {
932 assert!(matches!(
933 parse_index("5", 5),
934 Err(PatchErrorKind::InvalidPointer)
935 ));
936 }
937
938 #[test]
939 fn parse_index_non_numeric_errors() {
940 assert!(matches!(
941 parse_index("abc", 5),
942 Err(PatchErrorKind::InvalidPointer)
943 ));
944 }
945
946 #[test]
947 fn parse_index_single_zero_valid() {
948 assert_eq!(parse_index("0", 1).unwrap(), 0);
949 }
950
951 #[test]
956 fn unescape_tilde_zero_becomes_tilde() {
957 assert_eq!(unescape("a~0b"), "a~b");
958 }
959
960 #[test]
961 fn unescape_tilde_one_becomes_slash() {
962 assert_eq!(unescape("a~1b"), "a/b");
963 }
964
965 #[test]
966 fn unescape_both_sequences() {
967 assert_eq!(unescape("~0~1"), "~/");
968 }
969
970 #[test]
971 fn unescape_no_tilde_borrows() {
972 let result = unescape("plain");
973 assert!(matches!(result, Cow::Borrowed(_)));
974 assert_eq!(result, "plain");
975 }
976
977 #[test]
978 fn unescape_with_tilde_returns_owned() {
979 let result = unescape("a~0b");
980 assert!(matches!(result, Cow::Owned(_)));
981 }
982
983 #[test]
988 fn patch_error_reports_correct_operation_index() {
989 let mut doc = platform_value!({"a": 1});
990 let p: Patch = from_value(platform_value!([
991 { "op": "add", "path": "/b", "value": 2 },
992 { "op": "remove", "path": "/nonexistent" }
993 ]))
994 .unwrap();
995 let err = patch(&mut doc, &p).unwrap_err();
996 assert_eq!(err.operation, 1);
997 assert_eq!(err.path, "/nonexistent");
998 }
999
1000 #[test]
1005 fn split_pointer_valid() {
1006 let (parent, last) = split_pointer("/a/b").unwrap();
1007 assert_eq!(parent, "/a");
1008 assert_eq!(last, "b");
1009 }
1010
1011 #[test]
1012 fn split_pointer_root_child() {
1013 let (parent, last) = split_pointer("/x").unwrap();
1014 assert_eq!(parent, "");
1015 assert_eq!(last, "x");
1016 }
1017
1018 #[test]
1019 fn split_pointer_no_slash_errors() {
1020 assert!(split_pointer("noslash").is_err());
1021 }
1022
1023 #[test]
1028 fn add_to_scalar_parent_errors() {
1029 let mut doc = platform_value!({"a": 42});
1030 let p: Patch = from_value(platform_value!([
1031 { "op": "add", "path": "/a/b", "value": 1 }
1032 ]))
1033 .unwrap();
1034 let err = patch(&mut doc, &p).unwrap_err();
1035 assert!(matches!(err.kind, PatchErrorKind::InvalidPointer));
1036 }
1037}