use crate::{Error, Result};
use serde_json::Value;
use super::function_parser::{FunctionArg, PathExpr, FunctionCall};
use super::functions::execute_function;
pub fn evaluate_arg(
arg: &FunctionArg,
current: &Value,
root: &Value,
context: Option<&Value>,
path_stack: &[String],
) -> Result<Value> {
match arg {
FunctionArg::Literal(s) => Ok(Value::String(s.clone())),
FunctionArg::Number(n) => Ok(Value::Number((*n).into())),
FunctionArg::PathExpr(path_expr) => {
evaluate_path_expr(path_expr, current, root, path_stack)
}
FunctionArg::ContextRef(key) => {
if let Some(ctx) = context {
ctx.get(key).cloned().ok_or_else(|| {
Error::PathEvaluationError(format!("Context key not found: {}", key))
})
} else {
Err(Error::PathEvaluationError(
"No context provided".to_string(),
))
}
}
FunctionArg::Identifier(ident) => {
Ok(current
.get(ident)
.cloned()
.unwrap_or(Value::String(ident.clone())))
}
FunctionArg::Function(nested_call) => {
evaluate_function_call(nested_call, current, root, context, path_stack)
}
}
}
fn evaluate_function_call(
call: &FunctionCall,
current: &Value,
root: &Value,
context: Option<&Value>,
path_stack: &[String],
) -> Result<Value> {
let mut evaluated_args = Vec::new();
for arg in &call.args {
let evaluated = evaluate_arg(arg, current, root, context, path_stack)?;
evaluated_args.push(evaluated);
}
execute_function(&call.name, &evaluated_args)
}
fn evaluate_path_expr(
path_expr: &PathExpr,
current: &Value,
root: &Value,
path_stack: &[String],
) -> Result<Value> {
let base = if path_expr.level == 0 {
root
} else if path_expr.level == 1 {
current
} else if path_expr.level > path_stack.len() + 1 {
root
} else {
let levels_up = path_expr.level - 1;
let target_depth = path_stack.len().saturating_sub(levels_up);
let target_path = &path_stack[..target_depth];
if target_path.is_empty() {
root
} else {
let path_str = target_path.join(".");
match navigate_path(root, &path_str) {
Ok(v) => {
return if path_expr.path.is_empty() {
Ok(v)
} else {
navigate_path(&v, &path_expr.path)
}
}
Err(_) => root, }
}
};
if path_expr.path.is_empty() {
return Ok(base.clone());
}
navigate_path(base, &path_expr.path)
}
fn navigate_path(base: &Value, path: &str) -> Result<Value> {
let mut current = base;
let mut remaining = path;
while !remaining.is_empty() {
if let Some(open_bracket) = remaining.find('[') {
let key = &remaining[..open_bracket];
if !key.is_empty() {
current = current
.get(key)
.ok_or_else(|| Error::PathEvaluationError(format!("Key not found: {}", key)))?;
}
let close_bracket = remaining.find(']').ok_or_else(|| {
Error::PathEvaluationError(format!("Unclosed bracket in path: {}", path))
})?;
let index_str = &remaining[open_bracket + 1..close_bracket];
let index: usize = index_str.parse().map_err(|_| {
Error::PathEvaluationError(format!("Invalid array index: {}", index_str))
})?;
current = current.get(index).ok_or_else(|| {
Error::PathEvaluationError(format!("Array index out of bounds: {}", index))
})?;
remaining = &remaining[close_bracket + 1..];
if remaining.starts_with('.') {
remaining = &remaining[1..];
}
} else {
if let Some(dot_pos) = remaining.find('.') {
let key = &remaining[..dot_pos];
current = current
.get(key)
.ok_or_else(|| Error::PathEvaluationError(format!("Key not found: {}", key)))?;
remaining = &remaining[dot_pos + 1..];
} else {
current = current.get(remaining).ok_or_else(|| {
Error::PathEvaluationError(format!("Key not found: {}", remaining))
})?;
break;
}
}
}
Ok(current.clone())
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn test_navigate_simple_key() {
let data = json!({
"name": "John",
"age": 30
});
let result = navigate_path(&data, "name").unwrap();
assert_eq!(result, json!("John"));
}
#[test]
fn test_navigate_nested() {
let data = json!({
"person": {
"name": "John"
}
});
let result = navigate_path(&data, "person.name").unwrap();
assert_eq!(result, json!("John"));
}
#[test]
fn test_navigate_array() {
let data = json!({
"name": ["John", "Doe"]
});
let result = navigate_path(&data, "name[0]").unwrap();
assert_eq!(result, json!("John"));
let result = navigate_path(&data, "name[1]").unwrap();
assert_eq!(result, json!("Doe"));
}
#[test]
fn test_navigate_complex() {
let data = json!({
"lower": {
"leading": "hello"
}
});
let result = navigate_path(&data, "lower.leading").unwrap();
assert_eq!(result, json!("hello"));
}
}