///|
/// Navigation and mutation share these decoded segments. Provenance retains
/// the original JSON keys, including unnormalized slice bounds, for `path()`.
priv enum PathSegment {
Field(String)
Element(Int32)
Region(Int32?, Int32?)
}
///|
fn path_segment(key : Json) -> PathSegment? {
match key {
String(name) => Some(Field(name))
Number(n, ..) => Some(Element(n.to_int()))
Object(bounds) if bounds.contains("start") && bounds.contains("end") => {
let (start, end) = slice_segment(bounds)
Some(Region(start, end))
}
_ => None
}
}
///|
fn slice_key(start : Int32?, end : Int32?) -> Json {
{
"start": start.map(n => Json::number(n.to_double())).unwrap_or(null),
"end": end.map(n => Json::number(n.to_double())).unwrap_or(null),
}
}
///|
/// `None` means an incompatible container; missing entries read as null.
fn PathSegment::read(self : PathSegment, root : Json) -> Json? {
match (self, root) {
(_, Null) => Some(null)
(Field(key), Object(obj)) => Some(obj.get(key).unwrap_or(null))
(Element(i), Array(arr)) =>
Some(arr.get(normalize_index(i, arr.length())).unwrap_or(null))
(Region(start, end), Array(_) | String(_)) =>
Some(slice_value(root, start, end))
_ => None
}
}
///|
fn PathSegment::write(
self : PathSegment,
root : Json,
replacement : Json,
) -> Json raise PrimitiveError {
match (self, root) {
(Field(key), Object(_) | Null) => {
let obj = match root {
Object(obj) => obj.copy()
_ => Map([])
}
obj[key] = replacement
Json::object(obj)
}
(Element(raw), Array(_) | Null) => {
let arr = match root {
Array(arr) => arr.copy()
_ => []
}
let i = normalize_index(raw, arr.length())
if i < 0 {
raise InvalidOperation("Out of bounds negative array index")
}
if i > MAX_SETPATH_INDEX {
raise InvalidOperation("Array index too large")
}
while arr.length() <= i {
arr.push(null)
}
arr[i] = replacement
Json::array(arr)
}
(Region(start, end), Array(_) | Null) => {
let arr = match root {
Array(arr) => arr
_ => []
}
let (first, last) = slice_bounds(start, end, arr.length())
guard replacement is Array(values) else {
raise InvalidOperation("A slice can only be assigned an array")
}
Json::array(arr[:first].to_owned() + values + arr[last:].to_owned())
}
_ => root
}
}