mirror of
https://github.com/aljazceru/turso.git
synced 2026-02-23 00:45:37 +01:00
add json_patch implementation
This commit is contained in:
103
core/json/json_operations.rs
Normal file
103
core/json/json_operations.rs
Normal file
@@ -0,0 +1,103 @@
|
||||
use std::collections::VecDeque;
|
||||
|
||||
use crate::types::OwnedValue;
|
||||
|
||||
use super::{convert_json_to_db_type, get_json_value, Val};
|
||||
|
||||
/// Represents a single patch operation in the merge queue.
|
||||
///
|
||||
/// Used internally by the `merge_patch` function to track the path and value
|
||||
/// for each pending merge operation.
|
||||
#[derive(Debug, Clone)]
|
||||
struct PatchOperation {
|
||||
path: Vec<usize>,
|
||||
patch: Val,
|
||||
}
|
||||
|
||||
/// The function follows RFC 7386 JSON Merge Patch semantics:
|
||||
/// * If the patch is null, the target is replaced with null
|
||||
/// * If the patch contains a scalar value, the target is replaced with that value
|
||||
/// * If both target and patch are objects, the patch is recursively applied
|
||||
/// * null values in the patch result in property removal from the target
|
||||
pub fn json_patch(target: &OwnedValue, patch: &OwnedValue) -> crate::Result<OwnedValue> {
|
||||
match (target, patch) {
|
||||
(OwnedValue::Blob(_), _) | (_, OwnedValue::Blob(_)) => {
|
||||
crate::bail_constraint_error!("blob is not supported!");
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
let mut parsed_target = get_json_value(target)?;
|
||||
let parsed_patch = get_json_value(patch)?;
|
||||
|
||||
merge_patch(&mut parsed_target, parsed_patch);
|
||||
|
||||
convert_json_to_db_type(&parsed_target, false)
|
||||
}
|
||||
|
||||
/// # Implementation Notes
|
||||
///
|
||||
/// * Processes patches in breadth-first order
|
||||
/// * Maintains path information for nested updates
|
||||
/// * Handles object merging with proper null value semantics
|
||||
/// * Preserves existing values not mentioned in the patch
|
||||
///
|
||||
/// Following RFC 7386 rules:
|
||||
/// * null values remove properties
|
||||
/// * Objects are merged recursively
|
||||
/// * Non-object values replace the target completely
|
||||
fn merge_patch(target: &mut Val, patch: Val) {
|
||||
let mut queue = VecDeque::with_capacity(8);
|
||||
|
||||
queue.push_back(PatchOperation {
|
||||
path: Vec::new(),
|
||||
patch,
|
||||
});
|
||||
while let Some(PatchOperation { path, patch }) = queue.pop_front() {
|
||||
let mut current: &mut Val = target;
|
||||
|
||||
for (depth, key) in path.iter().enumerate() {
|
||||
if let Val::Object(ref mut obj) = current {
|
||||
current = &mut obj
|
||||
.get_mut(*key)
|
||||
.unwrap_or_else(|| {
|
||||
panic!("Invalid path at depth {}: key '{}' not found", depth, key)
|
||||
})
|
||||
.1;
|
||||
}
|
||||
}
|
||||
|
||||
match (current, patch) {
|
||||
(curr, Val::Null) => {
|
||||
*curr = Val::Null;
|
||||
}
|
||||
(Val::Object(target_map), Val::Object(patch_map)) => {
|
||||
for (key, patch_val) in patch_map {
|
||||
if let Some(pos) = target_map
|
||||
.iter()
|
||||
.position(|(target_key, _)| target_key == &key)
|
||||
{
|
||||
match patch_val {
|
||||
Val::Null => {
|
||||
target_map.remove(pos);
|
||||
}
|
||||
val => {
|
||||
let mut new_path = path.clone();
|
||||
new_path.push(pos);
|
||||
queue.push_back(PatchOperation {
|
||||
path: new_path,
|
||||
patch: val,
|
||||
});
|
||||
}
|
||||
};
|
||||
} else {
|
||||
target_map.push((key, patch_val));
|
||||
};
|
||||
}
|
||||
}
|
||||
(curr, patch_val) => {
|
||||
*curr = patch_val;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
mod de;
|
||||
mod error;
|
||||
mod json_operations;
|
||||
mod json_path;
|
||||
mod ser;
|
||||
|
||||
@@ -8,6 +9,7 @@ use std::rc::Rc;
|
||||
pub use crate::json::de::from_str;
|
||||
use crate::json::de::ordered_object;
|
||||
use crate::json::error::Error as JsonError;
|
||||
pub use crate::json::json_operations::json_patch;
|
||||
use crate::json::json_path::{json_path, JsonPath, PathElement};
|
||||
pub use crate::json::ser::to_string;
|
||||
use crate::types::{LimboText, OwnedValue, TextSubtype};
|
||||
|
||||
Reference in New Issue
Block a user