pub mod predicates;
pub mod string_methods;
pub mod array_methods;
pub mod lambda_methods;
pub mod conversion_methods;
use crate::ast::Expr;
use crate::custom::FunctionRegistry;
use crate::error::Error;
use crate::types::Value;
use std::collections::HashMap;
use std::sync::{Arc, RwLock};
pub use predicates::exec_predicate;
pub use string_methods::exec_string_method;
pub use array_methods::exec_array_method;
pub use lambda_methods::{exec_filter, exec_map, exec_find, exec_reduce};
pub use conversion_methods::exec_conversion_method;
pub fn exec_method(
name: &str,
predicate: bool,
recv: &Value,
args_expr: &[Expr],
base_vars: Option<&HashMap<String, Value>>,
) -> Result<Value, Error> {
if predicate {
return exec_predicate(name, recv);
}
let lname = name.to_lowercase();
match lname.as_str() {
"to_s" | "to_string" | "to_i" | "to_int" | "to_f" | "to_float" |
"to_a" | "to_array" | "to_json" | "to_bool" | "to_boolean" => {
return exec_conversion_method(name, recv);
}
_ => {}
}
match recv {
Value::String(_) => exec_string_method(name, recv, args_expr, base_vars),
Value::Array(_) => {
match lname.as_str() {
"filter" => exec_filter(recv, args_expr, base_vars),
"map" => exec_map(recv, args_expr, base_vars),
"find" => exec_find(recv, args_expr, base_vars),
"reduce" => exec_reduce(recv, args_expr, base_vars),
_ => exec_array_method(name, recv, args_expr, base_vars),
}
}
Value::Number(_) => exec_number_method(name, recv, args_expr, base_vars),
Value::Json(_) => exec_json_method(name, recv, args_expr, base_vars),
_ => Err(Error::new(
format!("No methods available for {:?} type", recv),
None,
)),
}
}
pub fn exec_method_with_custom(
name: &str,
predicate: bool,
recv: &Value,
args_expr: &[Expr],
base_vars: Option<&HashMap<String, Value>>,
custom_registry: &Arc<RwLock<FunctionRegistry>>,
) -> Result<Value, Error> {
if predicate {
return exec_predicate(name, recv);
}
let lname = name.to_lowercase();
match lname.as_str() {
"to_s" | "to_string" | "to_i" | "to_int" | "to_f" | "to_float" |
"to_a" | "to_array" | "to_json" | "to_bool" | "to_boolean" => {
return exec_conversion_method(name, recv);
}
_ => {}
}
match recv {
Value::String(_) => exec_string_method(name, recv, args_expr, base_vars),
Value::Array(_) => {
match lname.as_str() {
"filter" => lambda_methods::exec_filter_with_custom(recv, args_expr, base_vars, custom_registry),
"map" => lambda_methods::exec_map_with_custom(recv, args_expr, base_vars, custom_registry),
"find" => lambda_methods::exec_find_with_custom(recv, args_expr, base_vars, custom_registry),
"reduce" => lambda_methods::exec_reduce_with_custom(recv, args_expr, base_vars, custom_registry),
_ => exec_array_method(name, recv, args_expr, base_vars),
}
}
Value::Number(_) => exec_number_method(name, recv, args_expr, base_vars),
Value::Json(_) => exec_json_method(name, recv, args_expr, base_vars),
_ => Err(Error::new(
format!("No methods available for {:?} type", recv),
None,
)),
}
}
fn exec_number_method(
name: &str,
recv: &Value,
args_expr: &[Expr],
base_vars: Option<&HashMap<String, Value>>,
) -> Result<Value, Error> {
let num = match recv {
Value::Number(n) => *n,
Value::Currency(c) => *c,
_ => return Err(Error::new("Method called on non-number", None)),
};
let lname = name.to_lowercase();
match lname.as_str() {
"abs" => Ok(Value::Number(num.abs())),
"ceil" | "ceiling" => Ok(Value::Number(num.ceil())),
"floor" => Ok(Value::Number(num.floor())),
"round" => {
if args_expr.is_empty() {
Ok(Value::Number(num.round()))
} else {
use crate::runtime::evaluation::{eval, eval_with_vars};
let precision_val = if let Some(vars) = base_vars {
eval_with_vars(&args_expr[0], vars)?
} else {
eval(&args_expr[0])?
};
let precision = match precision_val {
Value::Number(p) => p as i32,
_ => return Err(Error::new("round precision must be number", None)),
};
if precision == 0 {
Ok(Value::Number(num.round()))
} else {
let multiplier = 10f64.powi(precision);
Ok(Value::Number((num * multiplier).round() / multiplier))
}
}
}
"sqrt" => {
if num < 0.0 {
Err(Error::new("Cannot take square root of negative number", None))
} else {
Ok(Value::Number(num.sqrt()))
}
}
"sin" => Ok(Value::Number(num.sin())),
"cos" => Ok(Value::Number(num.cos())),
"tan" => Ok(Value::Number(num.tan())),
"int" => Ok(Value::Number(num.trunc())),
"between" => {
if args_expr.len() != 2 {
return Err(Error::new("between expects 2 arguments: min, max", None));
}
use crate::runtime::evaluation::{eval, eval_with_vars};
let min_val = if let Some(vars) = base_vars {
eval_with_vars(&args_expr[0], vars)?
} else {
eval(&args_expr[0])?
};
let max_val = if let Some(vars) = base_vars {
eval_with_vars(&args_expr[1], vars)?
} else {
eval(&args_expr[1])?
};
let min = match min_val {
Value::Number(n) => n,
Value::Currency(c) => c,
_ => return Err(Error::new("between min must be a number", None)),
};
let max = match max_val {
Value::Number(n) => n,
Value::Currency(c) => c,
_ => return Err(Error::new("between max must be a number", None)),
};
Ok(Value::Boolean(num >= min && num <= max))
}
_ => Err(Error::new(
format!("Unknown number method: {}", name),
None,
)),
}
}
fn exec_json_method(
name: &str,
recv: &Value,
args_expr: &[Expr],
base_vars: Option<&HashMap<String, Value>>,
) -> Result<Value, Error> {
let json_str = match recv {
Value::Json(s) => s,
_ => return Err(Error::new("Method called on non-JSON", None)),
};
let lname = name.to_lowercase();
match lname.as_str() {
"keys" => {
let parsed: serde_json::Value = serde_json::from_str(json_str)
.map_err(|e| Error::new(format!("Invalid JSON: {}", e), None))?;
if let serde_json::Value::Object(obj) = parsed {
let keys: Vec<Value> = obj.keys()
.map(|k| Value::String(k.clone()))
.collect();
Ok(Value::Array(keys))
} else {
Err(Error::new("keys() method requires JSON object", None))
}
}
"values" => {
let parsed: serde_json::Value = serde_json::from_str(json_str)
.map_err(|e| Error::new(format!("Invalid JSON: {}", e), None))?;
if let serde_json::Value::Object(obj) = parsed {
let values: Result<Vec<Value>, Error> = obj.values()
.map(|v| crate::json_to_value(v.clone()))
.collect();
Ok(Value::Array(values?))
} else {
Err(Error::new("values() method requires JSON object", None))
}
}
"has_key" | "has" => {
if args_expr.is_empty() {
return Err(Error::new("has_key method expects 1 argument", None));
}
use crate::runtime::evaluation::{eval, eval_with_vars};
let key_val = if let Some(vars) = base_vars {
eval_with_vars(&args_expr[0], vars)?
} else {
eval(&args_expr[0])?
};
let key = match key_val {
Value::String(s) => s,
_ => return Err(Error::new("has_key method expects string argument", None)),
};
let parsed: serde_json::Value = serde_json::from_str(json_str)
.map_err(|e| Error::new(format!("Invalid JSON: {}", e), None))?;
if let serde_json::Value::Object(obj) = parsed {
Ok(Value::Boolean(obj.contains_key(&key)))
} else {
Ok(Value::Boolean(false))
}
}
"dig" => {
if args_expr.is_empty() {
return Err(Error::new("dig(path_array, [default]) expects at least 1 argument", None));
}
use crate::runtime::evaluation::{eval, eval_with_vars};
let path_val = if let Some(vars) = base_vars {
eval_with_vars(&args_expr[0], vars)?
} else {
eval(&args_expr[0])?
};
let default_val = if args_expr.len() > 1 {
Some(if let Some(vars) = base_vars { eval_with_vars(&args_expr[1], vars)? } else { eval(&args_expr[1])? })
} else { None };
let path_vals = match path_val {
Value::Array(a) => a,
_ => return Err(Error::new("dig expects first argument to be an array path", None)),
};
let parsed: serde_json::Value = serde_json::from_str(json_str)
.map_err(|e| Error::new(format!("Invalid JSON: {}", e), None))?;
let mut cur = &parsed;
let mut ok = true;
for seg in &path_vals {
match seg {
Value::String(key) => {
if let serde_json::Value::Object(map) = cur {
if let Some(next) = map.get(key) { cur = next; } else { ok = false; break; }
} else { ok = false; break; }
}
Value::Number(n) => {
if let serde_json::Value::Array(arr) = cur {
let idx = if n.is_finite() { n.floor() as isize } else { -1 };
if idx >= 0 && (idx as usize) < arr.len() { cur = &arr[idx as usize]; } else { ok = false; break; }
} else { ok = false; break; }
}
_ => { ok = false; break; }
}
}
if ok {
crate::json_to_value(cur.clone())
} else if let Some(defv) = default_val {
Ok(defv)
} else {
Ok(Value::Null)
}
}
_ => Err(Error::new(
format!("Unknown JSON method: {}", name),
None,
)),
}
}