use super::Value;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(tag = "type")]
pub enum AttributePathSegment {
#[serde(rename = "property")]
Property { name: String },
#[serde(rename = "index")]
Index { index: i64 },
#[serde(rename = "dynamicKey")]
DynamicKey {
variable: String,
#[serde(rename = "varPath")]
var_path: Option<Vec<AttributePathSegment>>,
},
}
pub fn resolve_path(base: &Value, path: &[AttributePathSegment]) -> Value {
let mut current = base.clone();
for segment in path {
match segment {
AttributePathSegment::Property { name } => {
current = match ¤t {
Value::Object(obj) => obj.get(name).cloned().unwrap_or(Value::Null),
Value::Array(arr) if name == "length" => Value::Number(arr.len() as f64),
Value::String(s) if name == "length" => Value::Number(s.chars().count() as f64),
_ => return Value::Null,
};
}
AttributePathSegment::Index { index } => {
current = match ¤t {
Value::Array(arr) => {
let idx = *index;
if idx < 0 || idx as usize >= arr.len() {
return Value::Null;
}
arr[idx as usize].clone()
}
_ => return Value::Null,
};
}
AttributePathSegment::DynamicKey { .. } => {
return Value::Null;
}
}
}
current
}
pub fn set_at_path(base: &mut Value, path: &[AttributePathSegment], value: Value) {
if path.is_empty() {
return;
}
if path.len() == 1 {
match &path[0] {
AttributePathSegment::Property { name } => {
if let Value::Object(obj) = base {
obj.insert(name.clone(), value);
} else {
let mut obj = indexmap::IndexMap::new();
obj.insert(name.clone(), value);
*base = Value::Object(obj);
}
}
AttributePathSegment::Index { index } => {
if let Value::Array(arr) = base {
let idx = *index as usize;
if idx < arr.len() {
arr[idx] = value;
} else {
arr.resize(idx + 1, Value::Null);
arr[idx] = value;
}
}
}
AttributePathSegment::DynamicKey { .. } => {
}
}
return;
}
let (first, rest) = path.split_first().unwrap();
match first {
AttributePathSegment::Property { name } => {
if let Value::Object(obj) = base {
let entry = obj
.entry(name.clone())
.or_insert_with(|| Value::Object(indexmap::IndexMap::new()));
set_at_path(entry, rest, value);
} else {
let mut obj = indexmap::IndexMap::new();
let mut child = Value::Object(indexmap::IndexMap::new());
set_at_path(&mut child, rest, value);
obj.insert(name.clone(), child);
*base = Value::Object(obj);
}
}
AttributePathSegment::Index { index } => {
if let Value::Array(arr) = base {
let idx = *index as usize;
if idx < arr.len() {
set_at_path(&mut arr[idx], rest, value);
}
}
}
AttributePathSegment::DynamicKey { .. } => {
}
}
}