use serde_json::Value; use crate::tx::types::PatchOp; pub fn json_pointer_parent<'a>( root: &'a mut Value, path: &str, ) -> Result<(&'a mut Value, String), String> { if !path.starts_with('/') && path != "" { return Err("path must be JSON pointer starting with '/'".into()); } let part: Vec = path .split('/') .skip(1) .map(|x| x.replace("~1", "/").replace("~0", "~")) .collect(); if path.is_empty() { return Ok((root, "".into())); } let last = part.last().unwrap().clone(); let mut cur = root; for key in &part[..part.len() - 1] { match cur { Value::Object(map) => { cur = map .get_mut(key) .ok_or_else(|| format!("missing object key {key}"))?; } Value::Array(arr) => { let idx: usize = key.parse().map_err(|_| format!("bad array idx {key}"))?; cur = arr .get_mut(idx) .ok_or_else(|| format!("array index out of bounds {idx}"))?; } _ => return Err("cannot traverse non-container".into()), } } Ok((cur, last)) } pub fn apply_ops(mut state: Value, ops: &[PatchOp]) -> Result { for op in ops { match op { PatchOp::Replace { path, value } => { if path == "" || path == "/" { state = value.clone(); continue; } let (parent, last) = json_pointer_parent(&mut state, path)?; match parent { Value::Object(map) => { if !map.contains_key(&last) { return Err(format!("replace target missing key {last}")); } map.insert(last, value.clone()); } Value::Array(arr) => { let idx: usize = last .parse() .map_err(|_| format!("bad array index {last}"))?; if idx >= arr.len() { return Err(format!("replace index out of bounds {idx}")); } arr[idx] = value.clone(); } _ => return Err("replace parent not container".into()), } } PatchOp::Add { path, value } => { let (parent, last) = json_pointer_parent(&mut state, path)?; match parent { Value::Object(map) => { map.insert(last, value.clone()); } Value::Array(arr) => { if last == "-" { arr.push(value.clone()); } else { let idx: usize = last .parse() .map_err(|_| format!("bad array index {last}"))?; if idx > arr.len() { return Err(format!("add index out of bounds {idx}")); } arr.insert(idx, value.clone()); } } _ => return Err("add parent not container".into()), } } PatchOp::Remove { path } => { let (parent, last) = json_pointer_parent(&mut state, path)?; match parent { Value::Object(map) => { map.remove(&last) .ok_or_else(|| format!("remove missing key {last}"))?; } Value::Array(arr) => { let idx: usize = last .parse() .map_err(|_| format!("bad array index {last}"))?; if idx > arr.len() { return Err(format!("remove index out of bounds {idx}")); } arr.remove(idx); } _ => return Err("remove parent not container".into()), } } } } Ok(state) }