diff --git a/Cargo.toml b/Cargo.toml index 59fe2ce..291367c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "json-patch" -version = "2.0.0" +version = "3.0.0" authors = ["Ivan Dubrov "] categories = [] keywords = ["json", "json-patch"] @@ -15,7 +15,7 @@ default = ["diff"] diff = [] [dependencies] -jsonptr = "0.4.7" +jsonptr = "0.6.0" serde = { version = "1.0.159", features = ["derive"] } serde_json = "1.0.95" thiserror = "1.0.40" diff --git a/benches/generator/mod.rs b/benches/generator/mod.rs index 0d30eb2..5e7f2c9 100644 --- a/benches/generator/mod.rs +++ b/benches/generator/mod.rs @@ -1,5 +1,5 @@ use json_patch::{AddOperation, Patch, PatchOperation, RemoveOperation}; -use jsonptr::Pointer; +use jsonptr::PointerBuf; use rand::distributions::Alphanumeric; use rand::prelude::*; use serde_json::{Map, Value}; @@ -98,24 +98,24 @@ pub fn gen_add_remove_patches( vec } -fn all_leaves(value: &Value) -> Vec { +fn all_leaves(value: &Value) -> Vec { let mut result = Vec::new(); - collect_leaves(value, &mut Pointer::root(), &mut result); + collect_leaves(value, &mut PointerBuf::new(), &mut result); result } -fn collect_leaves(value: &Value, prefix: &mut Pointer, result: &mut Vec) { +fn collect_leaves(value: &Value, prefix: &mut PointerBuf, result: &mut Vec) { match *value { Value::Array(ref arr) => { for (idx, val) in arr.iter().enumerate() { - prefix.push_back(idx.into()); + prefix.push_back(idx); collect_leaves(val, prefix, result); prefix.pop_back(); } } Value::Object(ref map) => { for (key, val) in map.iter() { - prefix.push_back(key.into()); + prefix.push_back(key); collect_leaves(val, prefix, result); prefix.pop_back(); } diff --git a/src/diff.rs b/src/diff.rs index 533c204..89c8392 100644 --- a/src/diff.rs +++ b/src/diff.rs @@ -1,8 +1,8 @@ use crate::Patch; -use jsonptr::Pointer; +use jsonptr::PointerBuf; use serde_json::{Map, Value}; -fn diff_impl(left: &Value, right: &Value, pointer: &mut Pointer, patch: &mut super::Patch) { +fn diff_impl(left: &Value, right: &Value, pointer: &mut PointerBuf, patch: &mut super::Patch) { match (left, right) { (Value::Object(ref left_obj), Value::Object(ref right_obj)) => { diff_object(left_obj, right_obj, pointer, patch); @@ -25,11 +25,11 @@ fn diff_impl(left: &Value, right: &Value, pointer: &mut Pointer, patch: &mut sup } } -fn diff_array(left: &[Value], right: &[Value], pointer: &mut Pointer, patch: &mut Patch) { +fn diff_array(left: &[Value], right: &[Value], pointer: &mut PointerBuf, patch: &mut Patch) { let len = left.len().max(right.len()); let mut shift = 0usize; for idx in 0..len { - pointer.push_back((idx - shift).into()); + pointer.push_back(idx - shift); match (left.get(idx), right.get(idx)) { (Some(left), Some(right)) => { // Both array have an element at this index @@ -64,12 +64,12 @@ fn diff_array(left: &[Value], right: &[Value], pointer: &mut Pointer, patch: &mu fn diff_object( left: &Map, right: &Map, - pointer: &mut Pointer, + pointer: &mut PointerBuf, patch: &mut Patch, ) { // Add or replace keys in the right object for (key, right_value) in right { - pointer.push_back(key.into()); + pointer.push_back(key); match left.get(key) { Some(left_value) => { diff_impl(left_value, right_value, pointer, patch); @@ -89,7 +89,7 @@ fn diff_object( // Remove keys that are not in the right object for key in left.keys() { if !right.contains_key(key) { - pointer.push_back(key.into()); + pointer.push_back(key); patch .0 .push(super::PatchOperation::Remove(super::RemoveOperation { @@ -147,7 +147,7 @@ fn diff_object( /// ``` pub fn diff(left: &Value, right: &Value) -> super::Patch { let mut patch = super::Patch::default(); - let mut path = Pointer::root(); + let mut path = PointerBuf::new(); diff_impl(left, right, &mut path, &mut patch); patch } diff --git a/src/lib.rs b/src/lib.rs index 5d704fd..e32c839 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -78,7 +78,7 @@ //! ``` #![warn(missing_docs)] -use jsonptr::Pointer; +use jsonptr::{Pointer, PointerBuf}; use serde::{Deserialize, Serialize}; use serde_json::{Map, Value}; use std::{ @@ -148,7 +148,7 @@ pub struct AddOperation { /// JSON-Pointer value [RFC6901](https://tools.ietf.org/html/rfc6901) that references a location /// within the target document where the operation is performed. #[cfg_attr(feature = "utoipa", schema(value_type = String))] - pub path: Pointer, + pub path: PointerBuf, /// Value to add to the target location. pub value: Value, } @@ -162,7 +162,7 @@ pub struct RemoveOperation { /// JSON-Pointer value [RFC6901](https://tools.ietf.org/html/rfc6901) that references a location /// within the target document where the operation is performed. #[cfg_attr(feature = "utoipa", schema(value_type = String))] - pub path: Pointer, + pub path: PointerBuf, } impl_display!(RemoveOperation); @@ -174,7 +174,7 @@ pub struct ReplaceOperation { /// JSON-Pointer value [RFC6901](https://tools.ietf.org/html/rfc6901) that references a location /// within the target document where the operation is performed. #[cfg_attr(feature = "utoipa", schema(value_type = String))] - pub path: Pointer, + pub path: PointerBuf, /// Value to replace with. pub value: Value, } @@ -188,11 +188,11 @@ pub struct MoveOperation { /// JSON-Pointer value [RFC6901](https://tools.ietf.org/html/rfc6901) that references a location /// to move value from. #[cfg_attr(feature = "utoipa", schema(value_type = String))] - pub from: Pointer, + pub from: PointerBuf, /// JSON-Pointer value [RFC6901](https://tools.ietf.org/html/rfc6901) that references a location /// within the target document where the operation is performed. #[cfg_attr(feature = "utoipa", schema(value_type = String))] - pub path: Pointer, + pub path: PointerBuf, } impl_display!(MoveOperation); @@ -204,11 +204,11 @@ pub struct CopyOperation { /// JSON-Pointer value [RFC6901](https://tools.ietf.org/html/rfc6901) that references a location /// to copy value from. #[cfg_attr(feature = "utoipa", schema(value_type = String))] - pub from: Pointer, + pub from: PointerBuf, /// JSON-Pointer value [RFC6901](https://tools.ietf.org/html/rfc6901) that references a location /// within the target document where the operation is performed. #[cfg_attr(feature = "utoipa", schema(value_type = String))] - pub path: Pointer, + pub path: PointerBuf, } impl_display!(CopyOperation); @@ -220,7 +220,7 @@ pub struct TestOperation { /// JSON-Pointer value [RFC6901](https://tools.ietf.org/html/rfc6901) that references a location /// within the target document where the operation is performed. #[cfg_attr(feature = "utoipa", schema(value_type = String))] - pub path: Pointer, + pub path: PointerBuf, /// Value to test against. pub value: Value, } @@ -295,7 +295,7 @@ pub struct PatchError { /// Index of the operation that has failed. pub operation: usize, /// `path` of the operation. - pub path: Pointer, + pub path: PointerBuf, /// Kind of the error. pub kind: PatchErrorKind, } @@ -503,22 +503,23 @@ fn undo_patches(doc: &mut Value, undo_patches: &[PatchOperation]) -> Result<(), for (operation, patch) in undo_patches.iter().enumerate().rev() { match patch { PatchOperation::Add(op) => { - add(doc, &op.path, op.value.clone()) + add(doc, op.path.as_str(), op.value.clone()) .map_err(|e| translate_error(e, operation, &op.path))?; } PatchOperation::Remove(op) => { - remove(doc, &op.path, true).map_err(|e| translate_error(e, operation, &op.path))?; + remove(doc, op.path.as_str(), true) + .map_err(|e| translate_error(e, operation, &op.path))?; } PatchOperation::Replace(op) => { - replace(doc, &op.path, op.value.clone()) + replace(doc, op.path.as_str(), op.value.clone()) .map_err(|e| translate_error(e, operation, &op.path))?; } PatchOperation::Move(op) => { - mov(doc, op.from.as_str(), &op.path, true) + mov(doc, op.from.as_str(), op.path.as_str(), true) .map_err(|e| translate_error(e, operation, &op.path))?; } PatchOperation::Copy(op) => { - copy(doc, op.from.as_str(), &op.path) + copy(doc, op.from.as_str(), op.path.as_str()) .map_err(|e| translate_error(e, operation, &op.path))?; } _ => unreachable!(), @@ -539,7 +540,7 @@ fn apply_patches( for (operation, patch) in patches.iter().enumerate() { match patch { PatchOperation::Add(ref op) => { - let prev = add(doc, &op.path, op.value.clone()) + let prev = add(doc, op.path.as_str(), op.value.clone()) .map_err(|e| translate_error(e, operation, &op.path))?; if let Some(&mut ref mut undo_stack) = undo_stack { undo_stack.push(match prev { @@ -554,7 +555,7 @@ fn apply_patches( } } PatchOperation::Remove(ref op) => { - let prev = remove(doc, &op.path, false) + let prev = remove(doc, op.path.as_str(), false) .map_err(|e| translate_error(e, operation, &op.path))?; if let Some(&mut ref mut undo_stack) = undo_stack { undo_stack.push(PatchOperation::Add(AddOperation { @@ -564,7 +565,7 @@ fn apply_patches( } } PatchOperation::Replace(ref op) => { - let prev = replace(doc, &op.path, op.value.clone()) + let prev = replace(doc, op.path.as_str(), op.value.clone()) .map_err(|e| translate_error(e, operation, &op.path))?; if let Some(&mut ref mut undo_stack) = undo_stack { undo_stack.push(PatchOperation::Replace(ReplaceOperation { @@ -574,7 +575,7 @@ fn apply_patches( } } PatchOperation::Move(ref op) => { - let prev = mov(doc, op.from.as_str(), &op.path, false) + let prev = mov(doc, op.from.as_str(), op.path.as_str(), false) .map_err(|e| translate_error(e, operation, &op.path))?; if let Some(&mut ref mut undo_stack) = undo_stack { if let Some(prev) = prev { @@ -590,7 +591,7 @@ fn apply_patches( } } PatchOperation::Copy(ref op) => { - let prev = copy(doc, op.from.as_str(), &op.path) + let prev = copy(doc, op.from.as_str(), op.path.as_str()) .map_err(|e| translate_error(e, operation, &op.path))?; if let Some(&mut ref mut undo_stack) = undo_stack { undo_stack.push(match prev { @@ -605,7 +606,7 @@ fn apply_patches( } } PatchOperation::Test(ref op) => { - test(doc, &op.path, &op.value) + test(doc, op.path.as_str(), &op.value) .map_err(|e| translate_error(e, operation, &op.path))?; } } diff --git a/tests/basic.rs b/tests/basic.rs index 374f3c7..d90339f 100644 --- a/tests/basic.rs +++ b/tests/basic.rs @@ -2,7 +2,6 @@ use json_patch::{ AddOperation, CopyOperation, MoveOperation, Patch, PatchOperation, RemoveOperation, ReplaceOperation, TestOperation, }; -use jsonptr::Pointer; use serde_json::{from_str, from_value, json, Value}; #[test] @@ -14,11 +13,11 @@ fn parse_from_value() { patch, Patch(vec![ PatchOperation::Add(AddOperation { - path: Pointer::new(["a", "b"]), + path: "/a/b".parse().unwrap(), value: Value::from(1), }), PatchOperation::Remove(RemoveOperation { - path: Pointer::new(["c"]), + path: "/c".parse().unwrap(), }), ]) ); @@ -38,11 +37,11 @@ fn parse_from_string() { patch, Patch(vec![ PatchOperation::Add(AddOperation { - path: Pointer::new(["a", "b"]), + path: "/a/b".parse().unwrap(), value: Value::from(1), }), PatchOperation::Remove(RemoveOperation { - path: Pointer::new(["c"]), + path: "/c".parse().unwrap(), }), ]) ); @@ -60,7 +59,7 @@ fn serialize_patch() { #[test] fn display_add_operation() { let op = PatchOperation::Add(AddOperation { - path: Pointer::new(["a", "b", "c"]), + path: "/a/b/c".parse().unwrap(), value: json!(["hello", "bye"]), }); assert_eq!( @@ -83,7 +82,7 @@ fn display_add_operation() { #[test] fn display_remove_operation() { let op = PatchOperation::Remove(RemoveOperation { - path: Pointer::new(["a", "b", "c"]), + path: "/a/b/c".parse().unwrap(), }); assert_eq!(op.to_string(), r#"{"op":"remove","path":"/a/b/c"}"#); assert_eq!( @@ -98,7 +97,7 @@ fn display_remove_operation() { #[test] fn display_replace_operation() { let op = PatchOperation::Replace(ReplaceOperation { - path: Pointer::new(["a", "b", "c"]), + path: "/a/b/c".parse().unwrap(), value: json!(42), }); assert_eq!( @@ -118,8 +117,8 @@ fn display_replace_operation() { #[test] fn display_move_operation() { let op = PatchOperation::Move(MoveOperation { - from: Pointer::new(["a", "b", "c"]), - path: Pointer::new(["a", "b", "d"]), + from: "/a/b/c".parse().unwrap(), + path: "/a/b/d".parse().unwrap(), }); assert_eq!( op.to_string(), @@ -138,8 +137,8 @@ fn display_move_operation() { #[test] fn display_copy_operation() { let op = PatchOperation::Copy(CopyOperation { - from: Pointer::new(["a", "b", "d"]), - path: Pointer::new(["a", "b", "e"]), + from: "/a/b/d".parse().unwrap(), + path: "/a/b/e".parse().unwrap(), }); assert_eq!( op.to_string(), @@ -158,7 +157,7 @@ fn display_copy_operation() { #[test] fn display_test_operation() { let op = PatchOperation::Test(TestOperation { - path: Pointer::new(["a", "b", "c"]), + path: "/a/b/c".parse().unwrap(), value: json!("hello"), }); assert_eq!( @@ -179,11 +178,11 @@ fn display_test_operation() { fn display_patch() { let patch = Patch(vec![ PatchOperation::Add(AddOperation { - path: Pointer::new(["a", "b", "c"]), + path: "/a/b/c".parse().unwrap(), value: json!(["hello", "bye"]), }), PatchOperation::Remove(RemoveOperation { - path: Pointer::new(["a", "b", "c"]), + path: "/a/b/c".parse().unwrap(), }), ]); diff --git a/tests/errors.yaml b/tests/errors.yaml index 662a903..ead9373 100644 --- a/tests/errors.yaml +++ b/tests/errors.yaml @@ -89,19 +89,19 @@ - op: add path: "first" value: true - error: "json pointer \"first\" is malformed due to missing starting slash" + error: "json pointer is malformed as it does not start with a backslash ('/')" - doc: *1 patch: - op: replace path: "first" value: true - error: "json pointer \"first\" is malformed due to missing starting slash" + error: "json pointer is malformed as it does not start with a backslash ('/')" - doc: *1 patch: - op: remove path: "first" value: true - error: "json pointer \"first\" is malformed due to missing starting slash" + error: "json pointer is malformed as it does not start with a backslash ('/')" - doc: *1 patch: - op: add