use serde_json::Value as JsonValue;
use std::error::Error;
use std::fmt;
pub use json_patch::{
AddOperation, CopyOperation, MoveOperation, Patch, PatchOperation, RemoveOperation,
ReplaceOperation, TestOperation,
};
use jsonptr::PointerBuf;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PatchError {
message: String,
}
impl PatchError {
pub fn new(message: impl Into<String>) -> Self {
Self {
message: message.into(),
}
}
}
impl fmt::Display for PatchError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Patch error: {}", self.message)
}
}
impl Error for PatchError {}
impl From<json_patch::PatchError> for PatchError {
fn from(err: json_patch::PatchError) -> Self {
Self::new(format!("{}", err))
}
}
pub fn create_patch(from: &JsonValue, to: &JsonValue) -> Patch {
json_patch::diff(from, to)
}
pub fn apply_patch(target: &mut JsonValue, patch: &Patch) -> Result<(), PatchError> {
json_patch::patch(target, patch.0.as_slice()).map_err(PatchError::from)
}
pub fn apply_patch_from_value(target: &mut JsonValue, patch: &JsonValue) -> Result<(), PatchError> {
let patch: Patch = serde_json::from_value(patch.clone())
.map_err(|e| PatchError::new(format!("Invalid patch format: {}", e)))?;
apply_patch(target, &patch)
}
pub fn patch_to_value(patch: &Patch) -> JsonValue {
serde_json::to_value(patch).unwrap_or(JsonValue::Array(vec![]))
}
pub fn patch_to_vec(patch: &Patch) -> Vec<JsonValue> {
patch
.0
.iter()
.filter_map(|op| serde_json::to_value(op).ok())
.collect()
}
#[derive(Debug, Clone, Default)]
pub struct PatchBuilder {
operations: Vec<PatchOperation>,
}
impl PatchBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn add(mut self, path: impl AsRef<str>, value: JsonValue) -> Self {
self.operations.push(PatchOperation::Add(AddOperation {
path: PointerBuf::parse(path.as_ref()).unwrap_or_default(),
value,
}));
self
}
pub fn remove(mut self, path: impl AsRef<str>) -> Self {
self.operations
.push(PatchOperation::Remove(RemoveOperation {
path: PointerBuf::parse(path.as_ref()).unwrap_or_default(),
}));
self
}
pub fn replace(mut self, path: impl AsRef<str>, value: JsonValue) -> Self {
self.operations
.push(PatchOperation::Replace(ReplaceOperation {
path: PointerBuf::parse(path.as_ref()).unwrap_or_default(),
value,
}));
self
}
pub fn move_value(mut self, from: impl AsRef<str>, path: impl AsRef<str>) -> Self {
self.operations.push(PatchOperation::Move(MoveOperation {
from: PointerBuf::parse(from.as_ref()).unwrap_or_default(),
path: PointerBuf::parse(path.as_ref()).unwrap_or_default(),
}));
self
}
pub fn copy(mut self, from: impl AsRef<str>, path: impl AsRef<str>) -> Self {
self.operations.push(PatchOperation::Copy(CopyOperation {
from: PointerBuf::parse(from.as_ref()).unwrap_or_default(),
path: PointerBuf::parse(path.as_ref()).unwrap_or_default(),
}));
self
}
pub fn test(mut self, path: impl AsRef<str>, value: JsonValue) -> Self {
self.operations.push(PatchOperation::Test(TestOperation {
path: PointerBuf::parse(path.as_ref()).unwrap_or_default(),
value,
}));
self
}
pub fn build(self) -> Patch {
Patch(self.operations)
}
pub fn build_vec(self) -> Vec<JsonValue> {
patch_to_vec(&self.build())
}
}
pub fn can_apply_patch(target: &JsonValue, patch: &Patch) -> bool {
let mut test_target = target.clone();
apply_patch(&mut test_target, patch).is_ok()
}
pub fn merge_patches(first: &Patch, second: &Patch) -> Patch {
let mut operations = first.0.clone();
operations.extend(second.0.clone());
Patch(operations)
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn test_create_patch_simple() {
let from = json!({"count": 0});
let to = json!({"count": 5});
let patch = create_patch(&from, &to);
assert!(!patch.0.is_empty());
let mut result = from.clone();
apply_patch(&mut result, &patch).unwrap();
assert_eq!(result, to);
}
#[test]
fn test_create_patch_add_field() {
let from = json!({"name": "Alice"});
let to = json!({"name": "Alice", "age": 30});
let patch = create_patch(&from, &to);
let mut result = from.clone();
apply_patch(&mut result, &patch).unwrap();
assert_eq!(result, to);
}
#[test]
fn test_create_patch_remove_field() {
let from = json!({"name": "Alice", "temp": "value"});
let to = json!({"name": "Alice"});
let patch = create_patch(&from, &to);
let mut result = from.clone();
apply_patch(&mut result, &patch).unwrap();
assert_eq!(result, to);
}
#[test]
fn test_create_patch_array_operations() {
let from = json!({"items": ["a", "b"]});
let to = json!({"items": ["a", "b", "c"]});
let patch = create_patch(&from, &to);
let mut result = from.clone();
apply_patch(&mut result, &patch).unwrap();
assert_eq!(result, to);
}
#[test]
fn test_apply_patch_from_value() {
let mut state = json!({"count": 0});
let patch_json = json!([
{"op": "replace", "path": "/count", "value": 42}
]);
apply_patch_from_value(&mut state, &patch_json).unwrap();
assert_eq!(state["count"], 42);
}
#[test]
fn test_apply_patch_from_value_invalid() {
let mut state = json!({"count": 0});
let invalid_patch = json!("not an array");
let result = apply_patch_from_value(&mut state, &invalid_patch);
assert!(result.is_err());
}
#[test]
fn test_patch_to_value() {
let patch = create_patch(&json!({"x": 1}), &json!({"x": 2}));
let value = patch_to_value(&patch);
assert!(value.is_array());
}
#[test]
fn test_patch_to_vec() {
let patch = create_patch(&json!({"a": 1, "b": 2}), &json!({"a": 1, "b": 3, "c": 4}));
let ops = patch_to_vec(&patch);
assert!(!ops.is_empty());
for op in &ops {
assert!(op.is_object());
assert!(op.get("op").is_some());
}
}
#[test]
fn test_patch_builder_add() {
let patch = PatchBuilder::new()
.add("/name", json!("Alice"))
.build();
let mut state = json!({});
apply_patch(&mut state, &patch).unwrap();
assert_eq!(state["name"], "Alice");
}
#[test]
fn test_patch_builder_replace() {
let patch = PatchBuilder::new()
.replace("/count", json!(10))
.build();
let mut state = json!({"count": 0});
apply_patch(&mut state, &patch).unwrap();
assert_eq!(state["count"], 10);
}
#[test]
fn test_patch_builder_remove() {
let patch = PatchBuilder::new().remove("/temp").build();
let mut state = json!({"name": "Alice", "temp": "value"});
apply_patch(&mut state, &patch).unwrap();
assert!(state.get("temp").is_none());
assert_eq!(state["name"], "Alice");
}
#[test]
fn test_patch_builder_move() {
let patch = PatchBuilder::new()
.move_value("/old", "/new")
.build();
let mut state = json!({"old": "value"});
apply_patch(&mut state, &patch).unwrap();
assert!(state.get("old").is_none());
assert_eq!(state["new"], "value");
}
#[test]
fn test_patch_builder_copy() {
let patch = PatchBuilder::new()
.copy("/source", "/dest")
.build();
let mut state = json!({"source": "value"});
apply_patch(&mut state, &patch).unwrap();
assert_eq!(state["source"], "value");
assert_eq!(state["dest"], "value");
}
#[test]
fn test_patch_builder_test() {
let patch = PatchBuilder::new()
.test("/count", json!(0))
.replace("/count", json!(1))
.build();
let mut state = json!({"count": 0});
apply_patch(&mut state, &patch).unwrap();
assert_eq!(state["count"], 1);
}
#[test]
fn test_patch_builder_test_fails() {
let patch = PatchBuilder::new()
.test("/count", json!(999)) .replace("/count", json!(1))
.build();
let mut state = json!({"count": 0});
let result = apply_patch(&mut state, &patch);
assert!(result.is_err());
}
#[test]
fn test_patch_builder_build_vec() {
let ops = PatchBuilder::new()
.add("/a", json!(1))
.replace("/b", json!(2))
.build_vec();
assert_eq!(ops.len(), 2);
}
#[test]
fn test_can_apply_patch() {
let state = json!({"count": 0});
let valid_patch = PatchBuilder::new().replace("/count", json!(1)).build();
assert!(can_apply_patch(&state, &valid_patch));
let invalid_patch = PatchBuilder::new().remove("/nonexistent").build();
assert!(!can_apply_patch(&state, &invalid_patch));
}
#[test]
fn test_merge_patches() {
let patch1 = PatchBuilder::new().add("/a", json!(1)).build();
let patch2 = PatchBuilder::new().add("/b", json!(2)).build();
let merged = merge_patches(&patch1, &patch2);
assert_eq!(merged.0.len(), 2);
let mut state = json!({});
apply_patch(&mut state, &merged).unwrap();
assert_eq!(state["a"], 1);
assert_eq!(state["b"], 2);
}
#[test]
fn test_patch_error_display() {
let err = PatchError::new("test error");
assert!(err.to_string().contains("test error"));
}
#[test]
fn test_complex_nested_patch() {
let from = json!({
"user": {
"profile": {
"name": "Alice",
"settings": {
"theme": "light"
}
}
}
});
let to = json!({
"user": {
"profile": {
"name": "Alice",
"settings": {
"theme": "dark",
"notifications": true
}
}
}
});
let patch = create_patch(&from, &to);
let mut result = from.clone();
apply_patch(&mut result, &patch).unwrap();
assert_eq!(result, to);
}
#[test]
fn test_empty_patch() {
let state = json!({"count": 0});
let patch = create_patch(&state, &state);
assert!(patch.0.is_empty());
let mut result = state.clone();
apply_patch(&mut result, &patch).unwrap();
assert_eq!(result, state);
}
}