use serde_json::Value;
use crate::error::EvalError;
use super::{json_cmp, type_error, type_name};
pub fn eval_length(value: &Value) -> Result<Value, EvalError> {
let len = match value {
Value::Null => 0,
Value::String(s) => s.chars().count(),
Value::Array(arr) => arr.len(),
Value::Object(obj) => obj.len(),
Value::Bool(_) | Value::Number(_) => {
return Err(type_error(format!("{} has no length", type_name(value))));
}
};
Ok(Value::Number(len.into()))
}
pub fn eval_min(value: &Value) -> Result<Value, EvalError> {
match value {
Value::Array(arr) if arr.is_empty() => Ok(Value::Null),
Value::Array(arr) => Ok(arr
.iter()
.min_by(|a, b| json_cmp(a, b))
.cloned()
.unwrap_or(Value::Null)),
_ => Err(type_error(format!(
"{} cannot have its minimum taken, as it is not an array",
type_name(value)
))),
}
}
pub fn eval_max(value: &Value) -> Result<Value, EvalError> {
match value {
Value::Array(arr) if arr.is_empty() => Ok(Value::Null),
Value::Array(arr) => Ok(arr
.iter()
.max_by(|a, b| json_cmp(a, b))
.cloned()
.unwrap_or(Value::Null)),
_ => Err(type_error(format!(
"{} cannot have its maximum taken, as it is not an array",
type_name(value)
))),
}
}
pub fn eval_add(value: &Value) -> Result<Value, EvalError> {
match value {
Value::Array(arr) if arr.is_empty() => Ok(Value::Null),
Value::Array(arr) => {
let first_type = arr.iter().find(|v| !v.is_null());
Ok(match first_type {
Some(Value::Number(_)) => {
let sum: f64 = arr.iter().filter_map(|v| v.as_f64()).sum();
serde_json::json!(sum)
}
Some(Value::String(_)) => {
let concat: String = arr.iter().filter_map(|v| v.as_str()).collect();
Value::String(concat)
}
Some(Value::Array(_)) => {
let concat: Vec<Value> = arr
.iter()
.filter_map(|v| v.as_array())
.flatten()
.cloned()
.collect();
Value::Array(concat)
}
_ => Value::Null,
})
}
_ => Err(type_error(format!(
"{} cannot be added, as it is not an array",
type_name(value)
))),
}
}
#[cfg(test)]
mod tests {
use crate::filter::builtins::{eval, Builtin};
use serde_json::json;
#[test]
fn test_length_array() {
assert_eq!(eval(&Builtin::Length, &json!([1, 2, 3])).unwrap(), vec![json!(3)]);
}
#[test]
fn test_length_string() {
assert_eq!(eval(&Builtin::Length, &json!("hello")).unwrap(), vec![json!(5)]);
}
#[test]
fn test_length_string_unicode() {
assert_eq!(eval(&Builtin::Length, &json!("日本語")).unwrap(), vec![json!(3)]);
}
#[test]
fn test_length_object() {
assert_eq!(eval(&Builtin::Length, &json!({"a": 1, "b": 2})).unwrap(), vec![json!(2)]);
}
#[test]
fn test_length_null() {
assert_eq!(eval(&Builtin::Length, &json!(null)).unwrap(), vec![json!(0)]);
}
#[test]
fn test_length_empty() {
assert_eq!(eval(&Builtin::Length, &json!([])).unwrap(), vec![json!(0)]);
assert_eq!(eval(&Builtin::Length, &json!("")).unwrap(), vec![json!(0)]);
assert_eq!(eval(&Builtin::Length, &json!({})).unwrap(), vec![json!(0)]);
}
#[test]
fn test_length_number_errors() {
let result = eval(&Builtin::Length, &json!(42));
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("has no length"));
}
#[test]
fn test_length_bool_errors() {
let result = eval(&Builtin::Length, &json!(true));
assert!(result.is_err());
}
#[test]
fn test_min() {
assert_eq!(eval(&Builtin::Min, &json!([3, 1, 2])).unwrap(), vec![json!(1)]);
}
#[test]
fn test_max() {
assert_eq!(eval(&Builtin::Max, &json!([3, 1, 2])).unwrap(), vec![json!(3)]);
}
#[test]
fn test_min_empty() {
assert_eq!(eval(&Builtin::Min, &json!([])).unwrap(), vec![json!(null)]);
}
#[test]
fn test_max_empty() {
assert_eq!(eval(&Builtin::Max, &json!([])).unwrap(), vec![json!(null)]);
}
#[test]
fn test_min_strings() {
assert_eq!(
eval(&Builtin::Min, &json!(["banana", "apple", "cherry"])).unwrap(),
vec![json!("apple")]
);
}
#[test]
fn test_add_numbers() {
assert_eq!(eval(&Builtin::Add, &json!([1, 2, 3])).unwrap(), vec![json!(6.0)]);
}
#[test]
fn test_add_strings() {
assert_eq!(eval(&Builtin::Add, &json!(["a", "b", "c"])).unwrap(), vec![json!("abc")]);
}
#[test]
fn test_add_arrays() {
assert_eq!(
eval(&Builtin::Add, &json!([[1, 2], [3, 4]])).unwrap(),
vec![json!([1, 2, 3, 4])]
);
}
#[test]
fn test_add_empty() {
assert_eq!(eval(&Builtin::Add, &json!([])).unwrap(), vec![json!(null)]);
}
#[test]
fn test_add_non_array_errors() {
assert!(eval(&Builtin::Add, &json!("hello")).is_err());
}
#[test]
fn test_min_max_non_array_errors() {
assert!(eval(&Builtin::Min, &json!("hello")).is_err());
assert!(eval(&Builtin::Max, &json!(42)).is_err());
}
}