json_patch/
lib.rs

1//! A [JSON Patch (RFC 6902)](https://tools.ietf.org/html/rfc6902) and
2//! [JSON Merge Patch (RFC 7396)](https://tools.ietf.org/html/rfc7396) implementation for Rust.
3//!
4//! # Usage
5//!
6//! Add this to your *Cargo.toml*:
7//! ```toml
8//! [dependencies]
9//! json-patch = "*"
10//! ```
11//!
12//! # Examples
13//! Create and patch document using JSON Patch:
14//!
15//! ```rust
16//! #[macro_use]
17//! use json_patch::{Patch, patch};
18//! use serde_json::{from_value, json};
19//!
20//! # pub fn main() {
21//! let mut doc = json!([
22//!     { "name": "Andrew" },
23//!     { "name": "Maxim" }
24//! ]);
25//!
26//! let p: Patch = from_value(json!([
27//!   { "op": "test", "path": "/0/name", "value": "Andrew" },
28//!   { "op": "add", "path": "/0/happy", "value": true }
29//! ])).unwrap();
30//!
31//! patch(&mut doc, &p).unwrap();
32//! assert_eq!(doc, json!([
33//!   { "name": "Andrew", "happy": true },
34//!   { "name": "Maxim" }
35//! ]));
36//!
37//! # }
38//! ```
39//!
40//! Create and patch document using JSON Merge Patch:
41//!
42//! ```rust
43//! #[macro_use]
44//! use json_patch::merge;
45//! use serde_json::json;
46//!
47//! # pub fn main() {
48//! let mut doc = json!({
49//!   "title": "Goodbye!",
50//!   "author" : {
51//!     "givenName" : "John",
52//!     "familyName" : "Doe"
53//!   },
54//!   "tags":[ "example", "sample" ],
55//!   "content": "This will be unchanged"
56//! });
57//!
58//! let patch = json!({
59//!   "title": "Hello!",
60//!   "phoneNumber": "+01-123-456-7890",
61//!   "author": {
62//!     "familyName": null
63//!   },
64//!   "tags": [ "example" ]
65//! });
66//!
67//! merge(&mut doc, &patch);
68//! assert_eq!(doc, json!({
69//!   "title": "Hello!",
70//!   "author" : {
71//!     "givenName" : "John"
72//!   },
73//!   "tags": [ "example" ],
74//!   "content": "This will be unchanged",
75//!   "phoneNumber": "+01-123-456-7890"
76//! }));
77//! # }
78//! ```
79#![warn(missing_docs)]
80
81use jsonptr::{Pointer, PointerBuf};
82use serde::{Deserialize, Serialize};
83use serde_json::{Map, Value};
84use std::{
85    borrow::Cow,
86    fmt::{self, Display, Formatter},
87};
88use thiserror::Error;
89
90#[cfg(feature = "diff")]
91mod diff;
92
93#[cfg(feature = "diff")]
94pub use self::diff::diff;
95
96struct WriteAdapter<'a>(&'a mut dyn fmt::Write);
97
98impl<'a> std::io::Write for WriteAdapter<'a> {
99    fn write(&mut self, buf: &[u8]) -> Result<usize, std::io::Error> {
100        let s = std::str::from_utf8(buf).unwrap();
101        self.0
102            .write_str(s)
103            .map_err(|_| std::io::Error::from(std::io::ErrorKind::Other))?;
104        Ok(buf.len())
105    }
106
107    fn flush(&mut self) -> Result<(), std::io::Error> {
108        Ok(())
109    }
110}
111
112macro_rules! impl_display {
113    ($name:ident) => {
114        impl Display for $name {
115            fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
116                let alternate = f.alternate();
117                if alternate {
118                    serde_json::to_writer_pretty(WriteAdapter(f), self)
119                        .map_err(|_| std::fmt::Error)?;
120                } else {
121                    serde_json::to_writer(WriteAdapter(f), self).map_err(|_| std::fmt::Error)?;
122                }
123                Ok(())
124            }
125        }
126    };
127}
128
129/// Representation of JSON Patch (list of patch operations)
130#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
131#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
132pub struct Patch(pub Vec<PatchOperation>);
133
134impl_display!(Patch);
135
136impl std::ops::Deref for Patch {
137    type Target = [PatchOperation];
138
139    fn deref(&self) -> &[PatchOperation] {
140        &self.0
141    }
142}
143
144/// JSON Patch 'add' operation representation
145#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
146#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
147pub struct AddOperation {
148    /// JSON-Pointer value [RFC6901](https://tools.ietf.org/html/rfc6901) that references a location
149    /// within the target document where the operation is performed.
150    #[cfg_attr(feature = "utoipa", schema(value_type = String))]
151    pub path: PointerBuf,
152    /// Value to add to the target location.
153    pub value: Value,
154}
155
156impl_display!(AddOperation);
157
158/// JSON Patch 'remove' operation representation
159#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
160#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
161pub struct RemoveOperation {
162    /// JSON-Pointer value [RFC6901](https://tools.ietf.org/html/rfc6901) that references a location
163    /// within the target document where the operation is performed.
164    #[cfg_attr(feature = "utoipa", schema(value_type = String))]
165    pub path: PointerBuf,
166}
167
168impl_display!(RemoveOperation);
169
170/// JSON Patch 'replace' operation representation
171#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
172#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
173pub struct ReplaceOperation {
174    /// JSON-Pointer value [RFC6901](https://tools.ietf.org/html/rfc6901) that references a location
175    /// within the target document where the operation is performed.
176    #[cfg_attr(feature = "utoipa", schema(value_type = String))]
177    pub path: PointerBuf,
178    /// Value to replace with.
179    pub value: Value,
180}
181
182impl_display!(ReplaceOperation);
183
184/// JSON Patch 'move' operation representation
185#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
186#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
187pub struct MoveOperation {
188    /// JSON-Pointer value [RFC6901](https://tools.ietf.org/html/rfc6901) that references a location
189    /// to move value from.
190    #[cfg_attr(feature = "utoipa", schema(value_type = String))]
191    pub from: PointerBuf,
192    /// JSON-Pointer value [RFC6901](https://tools.ietf.org/html/rfc6901) that references a location
193    /// within the target document where the operation is performed.
194    #[cfg_attr(feature = "utoipa", schema(value_type = String))]
195    pub path: PointerBuf,
196}
197
198impl_display!(MoveOperation);
199
200/// JSON Patch 'copy' operation representation
201#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
202#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
203pub struct CopyOperation {
204    /// JSON-Pointer value [RFC6901](https://tools.ietf.org/html/rfc6901) that references a location
205    /// to copy value from.
206    #[cfg_attr(feature = "utoipa", schema(value_type = String))]
207    pub from: PointerBuf,
208    /// JSON-Pointer value [RFC6901](https://tools.ietf.org/html/rfc6901) that references a location
209    /// within the target document where the operation is performed.
210    #[cfg_attr(feature = "utoipa", schema(value_type = String))]
211    pub path: PointerBuf,
212}
213
214impl_display!(CopyOperation);
215
216/// JSON Patch 'test' operation representation
217#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
218#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
219pub struct TestOperation {
220    /// JSON-Pointer value [RFC6901](https://tools.ietf.org/html/rfc6901) that references a location
221    /// within the target document where the operation is performed.
222    #[cfg_attr(feature = "utoipa", schema(value_type = String))]
223    pub path: PointerBuf,
224    /// Value to test against.
225    pub value: Value,
226}
227
228impl_display!(TestOperation);
229
230/// JSON Patch single patch operation
231#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
232#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
233#[serde(tag = "op")]
234#[serde(rename_all = "lowercase")]
235pub enum PatchOperation {
236    /// 'add' operation
237    Add(AddOperation),
238    /// 'remove' operation
239    Remove(RemoveOperation),
240    /// 'replace' operation
241    Replace(ReplaceOperation),
242    /// 'move' operation
243    Move(MoveOperation),
244    /// 'copy' operation
245    Copy(CopyOperation),
246    /// 'test' operation
247    Test(TestOperation),
248}
249
250impl_display!(PatchOperation);
251
252impl PatchOperation {
253    /// Returns a reference to the path the operation applies to.
254    pub fn path(&self) -> &Pointer {
255        match self {
256            Self::Add(op) => &op.path,
257            Self::Remove(op) => &op.path,
258            Self::Replace(op) => &op.path,
259            Self::Move(op) => &op.path,
260            Self::Copy(op) => &op.path,
261            Self::Test(op) => &op.path,
262        }
263    }
264}
265
266impl Default for PatchOperation {
267    fn default() -> Self {
268        PatchOperation::Test(TestOperation::default())
269    }
270}
271
272/// This type represents all possible errors that can occur when applying JSON patch
273#[derive(Debug, Error)]
274#[non_exhaustive]
275pub enum PatchErrorKind {
276    /// `test` operation failed because values did not match.
277    #[error("value did not match")]
278    TestFailed,
279    /// `from` JSON pointer in a `move` or a `copy` operation was incorrect.
280    #[error("\"from\" path is invalid")]
281    InvalidFromPointer,
282    /// `path` JSON pointer is incorrect.
283    #[error("path is invalid")]
284    InvalidPointer,
285    /// `move` operation failed because target is inside the `from` location.
286    #[error("cannot move the value inside itself")]
287    CannotMoveInsideItself,
288}
289
290/// This type represents all possible errors that can occur when applying JSON patch
291#[derive(Debug, Error)]
292#[error("operation '/{operation}' failed at path '{path}': {kind}")]
293#[non_exhaustive]
294pub struct PatchError {
295    /// Index of the operation that has failed.
296    pub operation: usize,
297    /// `path` of the operation.
298    pub path: PointerBuf,
299    /// Kind of the error.
300    pub kind: PatchErrorKind,
301}
302
303fn translate_error(kind: PatchErrorKind, operation: usize, path: &Pointer) -> PatchError {
304    PatchError {
305        operation,
306        path: path.to_owned(),
307        kind,
308    }
309}
310
311fn unescape(s: &str) -> Cow<str> {
312    if s.contains('~') {
313        Cow::Owned(s.replace("~1", "/").replace("~0", "~"))
314    } else {
315        Cow::Borrowed(s)
316    }
317}
318
319fn parse_index(str: &str, len: usize) -> Result<usize, PatchErrorKind> {
320    // RFC 6901 prohibits leading zeroes in index
321    if (str.starts_with('0') && str.len() != 1) || str.starts_with('+') {
322        return Err(PatchErrorKind::InvalidPointer);
323    }
324    match str.parse::<usize>() {
325        Ok(index) if index < len => Ok(index),
326        _ => Err(PatchErrorKind::InvalidPointer),
327    }
328}
329
330fn split_pointer(pointer: &str) -> Result<(&str, &str), PatchErrorKind> {
331    pointer
332        .rfind('/')
333        .ok_or(PatchErrorKind::InvalidPointer)
334        .map(|idx| (&pointer[0..idx], &pointer[idx + 1..]))
335}
336
337fn add(doc: &mut Value, path: &str, value: Value) -> Result<Option<Value>, PatchErrorKind> {
338    if path.is_empty() {
339        return Ok(Some(std::mem::replace(doc, value)));
340    }
341
342    let (parent, last_unescaped) = split_pointer(path)?;
343    let parent = doc
344        .pointer_mut(parent)
345        .ok_or(PatchErrorKind::InvalidPointer)?;
346
347    match *parent {
348        Value::Object(ref mut obj) => Ok(obj.insert(unescape(last_unescaped).into_owned(), value)),
349        Value::Array(ref mut arr) if last_unescaped == "-" => {
350            arr.push(value);
351            Ok(None)
352        }
353        Value::Array(ref mut arr) => {
354            let idx = parse_index(last_unescaped, arr.len() + 1)?;
355            arr.insert(idx, value);
356            Ok(None)
357        }
358        _ => Err(PatchErrorKind::InvalidPointer),
359    }
360}
361
362fn remove(doc: &mut Value, path: &str, allow_last: bool) -> Result<Value, PatchErrorKind> {
363    let (parent, last_unescaped) = split_pointer(path)?;
364    let parent = doc
365        .pointer_mut(parent)
366        .ok_or(PatchErrorKind::InvalidPointer)?;
367
368    match *parent {
369        Value::Object(ref mut obj) => match obj.remove(unescape(last_unescaped).as_ref()) {
370            None => Err(PatchErrorKind::InvalidPointer),
371            Some(val) => Ok(val),
372        },
373        Value::Array(ref mut arr) if allow_last && last_unescaped == "-" => Ok(arr.pop().unwrap()),
374        Value::Array(ref mut arr) => {
375            let idx = parse_index(last_unescaped, arr.len())?;
376            Ok(arr.remove(idx))
377        }
378        _ => Err(PatchErrorKind::InvalidPointer),
379    }
380}
381
382fn replace(doc: &mut Value, path: &str, value: Value) -> Result<Value, PatchErrorKind> {
383    let target = doc
384        .pointer_mut(path)
385        .ok_or(PatchErrorKind::InvalidPointer)?;
386    Ok(std::mem::replace(target, value))
387}
388
389fn mov(
390    doc: &mut Value,
391    from: &str,
392    path: &str,
393    allow_last: bool,
394) -> Result<Option<Value>, PatchErrorKind> {
395    // Check we are not moving inside own child
396    if path.starts_with(from) && path[from.len()..].starts_with('/') {
397        return Err(PatchErrorKind::CannotMoveInsideItself);
398    }
399    let val = remove(doc, from, allow_last).map_err(|err| match err {
400        PatchErrorKind::InvalidPointer => PatchErrorKind::InvalidFromPointer,
401        err => err,
402    })?;
403    add(doc, path, val)
404}
405
406fn copy(doc: &mut Value, from: &str, path: &str) -> Result<Option<Value>, PatchErrorKind> {
407    let source = doc
408        .pointer(from)
409        .ok_or(PatchErrorKind::InvalidFromPointer)?
410        .clone();
411    add(doc, path, source)
412}
413
414fn test(doc: &Value, path: &str, expected: &Value) -> Result<(), PatchErrorKind> {
415    let target = doc.pointer(path).ok_or(PatchErrorKind::InvalidPointer)?;
416    if *target == *expected {
417        Ok(())
418    } else {
419        Err(PatchErrorKind::TestFailed)
420    }
421}
422
423/// Patch provided JSON document (given as `serde_json::Value`) in-place. If any of the patch is
424/// failed, all previous operations are reverted. In case of internal error resulting in panic,
425/// document might be left in inconsistent state.
426///
427/// # Example
428/// Create and patch document:
429///
430/// ```rust
431/// #[macro_use]
432/// use json_patch::{Patch, patch};
433/// use serde_json::{from_value, json};
434///
435/// # pub fn main() {
436/// let mut doc = json!([
437///     { "name": "Andrew" },
438///     { "name": "Maxim" }
439/// ]);
440///
441/// let p: Patch = from_value(json!([
442///   { "op": "test", "path": "/0/name", "value": "Andrew" },
443///   { "op": "add", "path": "/0/happy", "value": true }
444/// ])).unwrap();
445///
446/// patch(&mut doc, &p).unwrap();
447/// assert_eq!(doc, json!([
448///   { "name": "Andrew", "happy": true },
449///   { "name": "Maxim" }
450/// ]));
451///
452/// # }
453/// ```
454pub fn patch(doc: &mut Value, patch: &[PatchOperation]) -> Result<(), PatchError> {
455    let mut undo_stack = Vec::with_capacity(patch.len());
456    if let Err(e) = apply_patches(doc, patch, Some(&mut undo_stack)) {
457        if let Err(e) = undo_patches(doc, &undo_stack) {
458            unreachable!("unable to undo applied patches: {e}")
459        }
460        return Err(e);
461    }
462    Ok(())
463}
464
465/// Patch provided JSON document (given as `serde_json::Value`) in-place. Different from [`patch`]
466/// if any patch failed, the document is left in an inconsistent state. In case of internal error
467/// resulting in panic, document might be left in inconsistent state.
468///
469/// # Example
470/// Create and patch document:
471///
472/// ```rust
473/// #[macro_use]
474/// use json_patch::{Patch, patch_unsafe};
475/// use serde_json::{from_value, json};
476///
477/// # pub fn main() {
478/// let mut doc = json!([
479///     { "name": "Andrew" },
480///     { "name": "Maxim" }
481/// ]);
482///
483/// let p: Patch = from_value(json!([
484///   { "op": "test", "path": "/0/name", "value": "Andrew" },
485///   { "op": "add", "path": "/0/happy", "value": true }
486/// ])).unwrap();
487///
488/// patch_unsafe(&mut doc, &p).unwrap();
489/// assert_eq!(doc, json!([
490///   { "name": "Andrew", "happy": true },
491///   { "name": "Maxim" }
492/// ]));
493///
494/// # }
495/// ```
496pub fn patch_unsafe(doc: &mut Value, patch: &[PatchOperation]) -> Result<(), PatchError> {
497    apply_patches(doc, patch, None)
498}
499
500/// Undoes operations performed by `apply_patches`. This is useful to recover the original document
501/// in case of an error.
502fn undo_patches(doc: &mut Value, undo_patches: &[PatchOperation]) -> Result<(), PatchError> {
503    for (operation, patch) in undo_patches.iter().enumerate().rev() {
504        match patch {
505            PatchOperation::Add(op) => {
506                add(doc, op.path.as_str(), op.value.clone())
507                    .map_err(|e| translate_error(e, operation, &op.path))?;
508            }
509            PatchOperation::Remove(op) => {
510                remove(doc, op.path.as_str(), true)
511                    .map_err(|e| translate_error(e, operation, &op.path))?;
512            }
513            PatchOperation::Replace(op) => {
514                replace(doc, op.path.as_str(), op.value.clone())
515                    .map_err(|e| translate_error(e, operation, &op.path))?;
516            }
517            PatchOperation::Move(op) => {
518                mov(doc, op.from.as_str(), op.path.as_str(), true)
519                    .map_err(|e| translate_error(e, operation, &op.path))?;
520            }
521            PatchOperation::Copy(op) => {
522                copy(doc, op.from.as_str(), op.path.as_str())
523                    .map_err(|e| translate_error(e, operation, &op.path))?;
524            }
525            _ => unreachable!(),
526        }
527    }
528
529    Ok(())
530}
531
532// Apply patches while tracking all the changes being made so they can be reverted back in case
533// subsequent patches fail. The inverse of all state changes is recorded in the `undo_stack` which
534// can be reapplied using `undo_patches` to get back to the original document.
535fn apply_patches(
536    doc: &mut Value,
537    patches: &[PatchOperation],
538    undo_stack: Option<&mut Vec<PatchOperation>>,
539) -> Result<(), PatchError> {
540    for (operation, patch) in patches.iter().enumerate() {
541        match patch {
542            PatchOperation::Add(ref op) => {
543                let prev = add(doc, op.path.as_str(), op.value.clone())
544                    .map_err(|e| translate_error(e, operation, &op.path))?;
545                if let Some(&mut ref mut undo_stack) = undo_stack {
546                    undo_stack.push(match prev {
547                        None => PatchOperation::Remove(RemoveOperation {
548                            path: op.path.clone(),
549                        }),
550                        Some(v) => PatchOperation::Add(AddOperation {
551                            path: op.path.clone(),
552                            value: v,
553                        }),
554                    })
555                }
556            }
557            PatchOperation::Remove(ref op) => {
558                let prev = remove(doc, op.path.as_str(), false)
559                    .map_err(|e| translate_error(e, operation, &op.path))?;
560                if let Some(&mut ref mut undo_stack) = undo_stack {
561                    undo_stack.push(PatchOperation::Add(AddOperation {
562                        path: op.path.clone(),
563                        value: prev,
564                    }))
565                }
566            }
567            PatchOperation::Replace(ref op) => {
568                let prev = replace(doc, op.path.as_str(), op.value.clone())
569                    .map_err(|e| translate_error(e, operation, &op.path))?;
570                if let Some(&mut ref mut undo_stack) = undo_stack {
571                    undo_stack.push(PatchOperation::Replace(ReplaceOperation {
572                        path: op.path.clone(),
573                        value: prev,
574                    }))
575                }
576            }
577            PatchOperation::Move(ref op) => {
578                let prev = mov(doc, op.from.as_str(), op.path.as_str(), false)
579                    .map_err(|e| translate_error(e, operation, &op.path))?;
580                if let Some(&mut ref mut undo_stack) = undo_stack {
581                    if let Some(prev) = prev {
582                        undo_stack.push(PatchOperation::Add(AddOperation {
583                            path: op.path.clone(),
584                            value: prev,
585                        }));
586                    }
587                    undo_stack.push(PatchOperation::Move(MoveOperation {
588                        from: op.path.clone(),
589                        path: op.from.clone(),
590                    }));
591                }
592            }
593            PatchOperation::Copy(ref op) => {
594                let prev = copy(doc, op.from.as_str(), op.path.as_str())
595                    .map_err(|e| translate_error(e, operation, &op.path))?;
596                if let Some(&mut ref mut undo_stack) = undo_stack {
597                    undo_stack.push(match prev {
598                        None => PatchOperation::Remove(RemoveOperation {
599                            path: op.path.clone(),
600                        }),
601                        Some(v) => PatchOperation::Add(AddOperation {
602                            path: op.path.clone(),
603                            value: v,
604                        }),
605                    })
606                }
607            }
608            PatchOperation::Test(ref op) => {
609                test(doc, op.path.as_str(), &op.value)
610                    .map_err(|e| translate_error(e, operation, &op.path))?;
611            }
612        }
613    }
614
615    Ok(())
616}
617
618/// Patch provided JSON document (given as `serde_json::Value`) in place with JSON Merge Patch
619/// (RFC 7396).
620///
621/// # Example
622/// Create and patch document:
623///
624/// ```rust
625/// #[macro_use]
626/// use json_patch::merge;
627/// use serde_json::json;
628///
629/// # pub fn main() {
630/// let mut doc = json!({
631///   "title": "Goodbye!",
632///   "author" : {
633///     "givenName" : "John",
634///     "familyName" : "Doe"
635///   },
636///   "tags":[ "example", "sample" ],
637///   "content": "This will be unchanged"
638/// });
639///
640/// let patch = json!({
641///   "title": "Hello!",
642///   "phoneNumber": "+01-123-456-7890",
643///   "author": {
644///     "familyName": null
645///   },
646///   "tags": [ "example" ]
647/// });
648///
649/// merge(&mut doc, &patch);
650/// assert_eq!(doc, json!({
651///   "title": "Hello!",
652///   "author" : {
653///     "givenName" : "John"
654///   },
655///   "tags": [ "example" ],
656///   "content": "This will be unchanged",
657///   "phoneNumber": "+01-123-456-7890"
658/// }));
659/// # }
660/// ```
661pub fn merge(doc: &mut Value, patch: &Value) {
662    if !patch.is_object() {
663        *doc = patch.clone();
664        return;
665    }
666
667    if !doc.is_object() {
668        *doc = Value::Object(Map::new());
669    }
670    let map = doc.as_object_mut().unwrap();
671    for (key, value) in patch.as_object().unwrap() {
672        if value.is_null() {
673            map.remove(key.as_str());
674        } else {
675            merge(map.entry(key.as_str()).or_insert(Value::Null), value);
676        }
677    }
678}