///|
fn edit_tree(
value : Value,
pointer : String,
replacement : Value?,
insert : Bool,
limits : Limits,
) -> Value raise PlistError {
validate(value, limits~)
if pointer.length() > limits.max_bytes {
raise Limit("edit pointer budget")
}
let tokens = parse_pointer(pointer)
if tokens.length() > limits.max_depth {
raise Limit("edit path depth")
}
match replacement {
Some(v) => validate(v, limits~)
None => ()
}
fn edit(v : Value, depth : Int32) -> Value raise PlistError {
if depth == tokens.length() {
return match replacement {
Some(v) => v
None => raise PathError("cannot remove document root")
}
}
let token = tokens[depth]
let last = depth + 1 == tokens.length()
match v {
Dict(entries) => {
let result : Array[(String, Value)] = []
let mut found = false
for (key, item) in entries {
if key == token {
found = true
if last {
match replacement {
Some(v) => result.push((key, v))
None => ()
}
} else {
result.push((key, edit(item, depth + 1)))
}
} else {
result.push((key, item))
}
}
if !found {
match replacement {
Some(v) if last && insert => result.push((token, v))
_ => raise PathError("edit key missing")
}
}
Dict(result)
}
List(items) => {
let adding = last && insert && replacement is Some(_)
let index = array_index(token, items.length(), adding)
let result : Array[Value] = []
for i = 0; i < items.length(); i = i + 1 {
if i == index {
if last {
match replacement {
Some(v) => result.push(v)
None => ()
}
if adding {
result.push(items[i])
}
} else {
result.push(edit(items[i], depth + 1))
}
} else {
result.push(items[i])
}
}
if adding && index == items.length() {
match replacement {
Some(v) => result.push(v)
None => ()
}
}
List(result)
}
_ => raise PathError("edit traverses scalar")
}
}
let result = edit(value, 0)
validate(result, limits~)
result
}
///|
/// Functional replacement: missing paths are errors. Input is not mutated.
pub fn replace(
value : Value,
pointer : String,
replacement : Value,
limits~ : Limits = Limits::default(),
) -> Value raise PlistError {
edit_tree(value, pointer, Some(replacement), false, limits)
}
///|
/// Dictionary upsert or array insertion; '-' appends to arrays.
pub fn insert(
value : Value,
pointer : String,
replacement : Value,
limits~ : Limits = Limits::default(),
) -> Value raise PlistError {
edit_tree(value, pointer, Some(replacement), true, limits)
}
///|
pub fn remove(
value : Value,
pointer : String,
limits~ : Limits = Limits::default(),
) -> Value raise PlistError {
edit_tree(value, pointer, None, false, limits)
}