use sonic_rs::{JsonValueMutTrait, JsonValueTrait, Value};
use crate::{
api::json::{
r#const::{
ERR_INPUT_SHOULD_BE_NUMBER, ERR_PARENT_PATH_DOES_NOT_EXIST, ERR_RESULT_IS_INFINITE,
ERR_TARGET_PARENT_NOT_OBJECT, JSON_ROOT_PATH,
},
opt::JsonNumberOp,
path::{
ast::PathSegment,
eval::{extract_simple_field, get_path_values, mutate_path_values},
parser::parse_json_path,
},
},
error::{Error, Result},
};
pub fn json_set_path(root: &mut Value, path_str: &str, new_val: Value) -> Result<bool> {
let path_str = path_str.trim();
if path_str == JSON_ROOT_PATH || path_str == "." || path_str.is_empty() {
*root = new_val;
return Ok(true);
}
let mutated = mutate_path_values(root, path_str, |node| {
*node = new_val.clone();
})?;
if mutated > 0 {
return Ok(true);
}
if let Some(field) = extract_simple_field(path_str) {
if let Some(obj) = root.as_object_mut() {
obj.insert(field, new_val);
return Ok(true);
} else {
return Err(Error::invalid_data(ERR_TARGET_PARENT_NOT_OBJECT));
}
}
let segments = parse_json_path(path_str)?;
let meaningful_segs: Vec<&PathSegment<'_>> = segments
.iter()
.filter(|s| !matches!(s, PathSegment::Root))
.collect();
if meaningful_segs.is_empty() {
*root = new_val;
return Ok(true);
}
let (parent_segs, last_seg) = meaningful_segs.split_at(meaningful_segs.len() - 1);
let mut cur = root;
for seg in parent_segs {
match seg {
PathSegment::Field(name) => {
if let Some(obj) = cur.as_object_mut() {
if let Some(next) = obj.get_mut(name) {
cur = next;
} else {
return Err(Error::invalid_data(ERR_PARENT_PATH_DOES_NOT_EXIST));
}
} else {
return Err(Error::invalid_data(ERR_PARENT_PATH_DOES_NOT_EXIST));
}
}
PathSegment::Index(idx) => {
if let Some(arr) = cur.as_array_mut() {
let len = arr.len() as isize;
let actual = if *idx < 0 { len + *idx } else { *idx };
if actual >= 0 && (actual as usize) < arr.len() {
cur = &mut arr[actual as usize];
} else {
return Err(Error::invalid_data(ERR_PARENT_PATH_DOES_NOT_EXIST));
}
} else {
return Err(Error::invalid_data(ERR_PARENT_PATH_DOES_NOT_EXIST));
}
}
_ => return Err(Error::invalid_data(ERR_PARENT_PATH_DOES_NOT_EXIST)),
}
}
match last_seg[0] {
PathSegment::Field(name) => {
if let Some(obj) = cur.as_object_mut() {
obj.insert(name.as_ref(), new_val);
Ok(true)
} else {
Err(Error::invalid_data(ERR_TARGET_PARENT_NOT_OBJECT))
}
}
PathSegment::Index(idx) => {
if let Some(arr) = cur.as_array_mut() {
let len = arr.len() as isize;
if *idx == len {
arr.push(new_val);
Ok(true)
} else if *idx >= 0 && (*idx as usize) < arr.len() {
arr[*idx as usize] = new_val;
Ok(true)
} else if *idx < 0 && (len + *idx) >= 0 && ((len + *idx) as usize) < arr.len() {
arr[(len + *idx) as usize] = new_val;
Ok(true)
} else {
Err(Error::invalid_data(ERR_PARENT_PATH_DOES_NOT_EXIST))
}
} else {
Err(Error::invalid_data(ERR_PARENT_PATH_DOES_NOT_EXIST))
}
}
_ => Err(Error::invalid_data(ERR_PARENT_PATH_DOES_NOT_EXIST)),
}
}
pub fn execute_numop(
root: &mut Value,
path: &str,
num_str: &str,
op: JsonNumberOp,
) -> Result<Vec<Value>> {
let delta = if let Ok(val) = sonic_rs::from_str::<Value>(num_str) {
if val.is_number() && !val.is_str() {
val.as_f64().unwrap_or(f64::NAN)
} else {
return Err(Error::invalid_data(ERR_INPUT_SHOULD_BE_NUMBER));
}
} else if let Ok(f) = num_str.parse::<f64>() {
f
} else {
return Err(Error::invalid_data(ERR_INPUT_SHOULD_BE_NUMBER));
};
if delta.is_nan() || delta.is_infinite() {
return Err(Error::invalid_data(ERR_RESULT_IS_INFINITE));
}
let mut res_values = Vec::new();
let mut is_infinite_err = false;
mutate_path_values(root, path, |node| {
if is_infinite_err {
return;
}
if !node.is_number() || node.is_str() {
res_values.push(sonic_rs::json!(null));
return;
}
let origin_f64 = node.as_f64().unwrap_or(0.0);
let result_f64 = match op {
JsonNumberOp::Incr => origin_f64 + delta,
JsonNumberOp::Mul => origin_f64 * delta,
};
if result_f64.is_infinite() || result_f64.is_nan() {
is_infinite_err = true;
return;
}
if result_f64.fract() == 0.0 && result_f64 > (i64::MIN as f64) && result_f64 < (i64::MAX as f64)
{
let int_val = result_f64 as i64;
*node = sonic_rs::json!(int_val);
res_values.push(sonic_rs::json!(int_val));
} else {
*node = sonic_rs::json!(result_f64);
res_values.push(sonic_rs::json!(result_f64));
}
})?;
if is_infinite_err {
return Err(Error::invalid_data(ERR_RESULT_IS_INFINITE));
}
Ok(res_values)
}
pub fn json_get_path<'a>(root: &'a Value, path_str: &str) -> Option<&'a Value> {
let values = get_path_values(root, path_str).ok()?;
values.into_iter().next()
}
pub fn json_path_query<'a>(root: &'a Value, path: &str) -> Vec<&'a Value> {
get_path_values(root, path).unwrap_or_default()
}