///|
/// Apply RFC 7396-style JSON Merge Patch semantics. Object members are merged
/// recursively, null removes a member, and every other value replaces target.
pub fn apply_merge_patch(target : JsonValue, patch : JsonValue) -> JsonValue {
match patch {
Object(patch_entries) => {
let target_entries = match target {
Object(entries) => entries
_ => []
}
let output : Array[(String, JsonValue)] = []
for entry in target_entries {
output.push(entry)
}
for entry in patch_entries {
if entry.1 is Null {
remove_object_key(output, entry.0)
} else {
let existing = find_object_key(output, entry.0)
let merged = match existing {
Some(value) => apply_merge_patch(value, entry.1)
None => apply_merge_patch(Null, entry.1)
}
replace_object_key(output, entry.0, merged)
}
}
Object(output)
}
_ => patch
}
}
///|
pub fn merge_patch_documents(
target : String,
patch : String,
) -> Result[String, String] {
let target_value = match parse(target) {
Ok(value) => value
Err(error) => return Err("target: " + error.message())
}
let patch_value = match parse(patch) {
Ok(value) => value
Err(error) => return Err("patch: " + error.message())
}
match canonicalize(apply_merge_patch(target_value, patch_value)) {
Ok(output) => Ok(output)
Err(error) => Err(error.message())
}
}
///|
fn find_object_key(
entries : Array[(String, JsonValue)],
key : String,
) -> JsonValue? {
for entry in entries {
if entry.0 == key {
return Some(entry.1)
}
}
None
}
///|
fn remove_object_key(
entries : Array[(String, JsonValue)],
key : String,
) -> Unit {
let filtered : Array[(String, JsonValue)] = []
for entry in entries {
if entry.0 != key {
filtered.push(entry)
}
}
entries.clear()
for entry in filtered {
entries.push(entry)
}
}
///|
fn replace_object_key(
entries : Array[(String, JsonValue)],
key : String,
value : JsonValue,
) -> Unit {
let mut found = false
let mut index = 0
while index < entries.length() {
if entries[index].0 == key {
entries[index] = (key, value)
found = true
}
index = index + 1
}
if !found {
entries.push((key, value))
}
}