use rayon::prelude::*;
use serde_json::Value;
use crate::error::EvalError;
use super::ast::{Filter, ObjectKey};
use super::builtins;
const PARALLEL_THRESHOLD: usize = 10_000;
pub fn eval(filter: &Filter, value: &Value) -> Result<Vec<Value>, EvalError> {
match filter {
Filter::Identity => Ok(vec![value.clone()]),
Filter::Field(name) => match value {
Value::Object(map) => Ok(vec![map.get(name).cloned().unwrap_or(Value::Null)]),
_ => Ok(vec![Value::Null]),
},
Filter::Index(n) => match value {
Value::Array(arr) => {
let idx = if *n >= 0 {
*n as usize
} else {
arr.len()
.checked_sub((-*n) as usize)
.unwrap_or(usize::MAX)
};
Ok(vec![arr.get(idx).cloned().unwrap_or(Value::Null)])
}
_ => Ok(vec![Value::Null]),
},
Filter::Iterate => match value {
Value::Array(arr) if arr.len() >= PARALLEL_THRESHOLD => {
Ok(arr.par_iter().cloned().collect())
}
Value::Array(arr) => Ok(arr.clone()),
Value::Object(map) => Ok(map.values().cloned().collect()),
_ => Err(EvalError::CannotIterate {
value: value.clone(),
position: 0, }),
},
Filter::Pipe(left, right) => {
let left_results = eval(left, value)?;
let mut results = Vec::new();
for v in left_results {
results.extend(eval(right, &v)?);
}
Ok(results)
}
Filter::Builtin(b) => builtins::eval(b, value),
Filter::Array(element_filters) => {
let mut array_elements = Vec::new();
for elem_filter in element_filters {
let results = eval(elem_filter, value)?;
array_elements.extend(results);
}
Ok(vec![Value::Array(array_elements)])
}
Filter::Object(pairs) => {
let mut obj = serde_json::Map::new();
for (key, value_filter) in pairs {
let key_str = match key {
ObjectKey::Static(s) => s.clone(),
ObjectKey::Dynamic(key_filter) => {
let key_results = eval(key_filter, value)?;
match key_results.into_iter().next() {
Some(Value::String(s)) => s,
Some(other) => {
return Err(EvalError::TypeError {
message: format!(
"object key must be string, got {}",
type_name(&other)
),
position: 0,
});
}
None => {
return Err(EvalError::TypeError {
message: "object key expression produced no value".to_string(),
position: 0,
});
}
}
}
};
let results = eval(value_filter, value)?;
let val = results.into_iter().next().unwrap_or(Value::Null);
obj.insert(key_str, val);
}
Ok(vec![Value::Object(obj)])
}
}
}
fn type_name(v: &Value) -> &'static str {
match v {
Value::Null => "null",
Value::Bool(_) => "boolean",
Value::Number(_) => "number",
Value::String(_) => "string",
Value::Array(_) => "array",
Value::Object(_) => "object",
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn test_identity() {
let value = json!({"name": "alice"});
let result = eval(&Filter::Identity, &value).unwrap();
assert_eq!(result, vec![value]);
}
#[test]
fn test_field_access() {
let value = json!({"name": "alice", "age": 30});
let result = eval(&Filter::Field("name".to_string()), &value).unwrap();
assert_eq!(result, vec![json!("alice")]);
}
#[test]
fn test_field_missing() {
let value = json!({"name": "alice"});
let result = eval(&Filter::Field("missing".to_string()), &value).unwrap();
assert_eq!(result, vec![Value::Null]);
}
#[test]
fn test_field_on_non_object() {
let result = eval(&Filter::Field("foo".to_string()), &json!("string")).unwrap();
assert_eq!(result, vec![Value::Null]);
let result = eval(&Filter::Field("foo".to_string()), &json!(123)).unwrap();
assert_eq!(result, vec![Value::Null]);
let result = eval(&Filter::Field("foo".to_string()), &json!(null)).unwrap();
assert_eq!(result, vec![Value::Null]);
let result = eval(&Filter::Field("foo".to_string()), &json!([1, 2, 3])).unwrap();
assert_eq!(result, vec![Value::Null]);
}
#[test]
fn test_nested_field() {
let value = json!({"user": {"name": "alice", "address": {"city": "NYC"}}});
let filter = Filter::Pipe(
Box::new(Filter::Field("user".to_string())),
Box::new(Filter::Field("name".to_string())),
);
let result = eval(&filter, &value).unwrap();
assert_eq!(result, vec![json!("alice")]);
}
#[test]
fn test_deeply_nested_field() {
let value = json!({"user": {"address": {"city": "NYC"}}});
let filter = Filter::Pipe(
Box::new(Filter::Pipe(
Box::new(Filter::Field("user".to_string())),
Box::new(Filter::Field("address".to_string())),
)),
Box::new(Filter::Field("city".to_string())),
);
let result = eval(&filter, &value).unwrap();
assert_eq!(result, vec![json!("NYC")]);
}
#[test]
fn test_nested_field_missing() {
let value = json!({"user": {}});
let filter = Filter::Pipe(
Box::new(Filter::Field("user".to_string())),
Box::new(Filter::Field("name".to_string())),
);
let result = eval(&filter, &value).unwrap();
assert_eq!(result, vec![Value::Null]);
}
#[test]
fn test_field_on_nested_null() {
let value = json!({"user": null});
let filter = Filter::Pipe(
Box::new(Filter::Field("user".to_string())),
Box::new(Filter::Field("name".to_string())),
);
let result = eval(&filter, &value).unwrap();
assert_eq!(result, vec![Value::Null]);
}
#[test]
fn test_index_positive() {
let value = json!([10, 20, 30]);
assert_eq!(eval(&Filter::Index(0), &value).unwrap(), vec![json!(10)]);
assert_eq!(eval(&Filter::Index(1), &value).unwrap(), vec![json!(20)]);
assert_eq!(eval(&Filter::Index(2), &value).unwrap(), vec![json!(30)]);
}
#[test]
fn test_index_negative() {
let value = json!([10, 20, 30]);
assert_eq!(eval(&Filter::Index(-1), &value).unwrap(), vec![json!(30)]);
assert_eq!(eval(&Filter::Index(-2), &value).unwrap(), vec![json!(20)]);
assert_eq!(eval(&Filter::Index(-3), &value).unwrap(), vec![json!(10)]);
}
#[test]
fn test_index_out_of_bounds() {
let value = json!([10, 20, 30]);
assert_eq!(eval(&Filter::Index(10), &value).unwrap(), vec![Value::Null]);
assert_eq!(
eval(&Filter::Index(-10), &value).unwrap(),
vec![Value::Null]
);
}
#[test]
fn test_index_on_non_array() {
assert_eq!(
eval(&Filter::Index(0), &json!({"a": 1})).unwrap(),
vec![Value::Null]
);
assert_eq!(
eval(&Filter::Index(0), &json!("string")).unwrap(),
vec![Value::Null]
);
assert_eq!(
eval(&Filter::Index(0), &json!(123)).unwrap(),
vec![Value::Null]
);
assert_eq!(
eval(&Filter::Index(0), &json!(null)).unwrap(),
vec![Value::Null]
);
}
#[test]
fn test_iterate_array() {
let value = json!([1, 2, 3]);
let result = eval(&Filter::Iterate, &value).unwrap();
assert_eq!(result, vec![json!(1), json!(2), json!(3)]);
}
#[test]
fn test_iterate_object() {
let value = json!({"a": 1, "b": 2});
let result = eval(&Filter::Iterate, &value).unwrap();
assert_eq!(result.len(), 2);
assert!(result.contains(&json!(1)));
assert!(result.contains(&json!(2)));
}
#[test]
fn test_iterate_empty_array() {
let value = json!([]);
let result = eval(&Filter::Iterate, &value).unwrap();
assert_eq!(result, Vec::<Value>::new());
}
#[test]
fn test_iterate_empty_object() {
let value = json!({});
let result = eval(&Filter::Iterate, &value).unwrap();
assert_eq!(result, Vec::<Value>::new());
}
#[test]
fn test_iterate_non_iterable_errors() {
assert!(matches!(
eval(&Filter::Iterate, &json!("string")),
Err(EvalError::CannotIterate { .. })
));
assert!(matches!(
eval(&Filter::Iterate, &json!(123)),
Err(EvalError::CannotIterate { .. })
));
assert!(matches!(
eval(&Filter::Iterate, &json!(null)),
Err(EvalError::CannotIterate { .. })
));
assert!(matches!(
eval(&Filter::Iterate, &json!(true)),
Err(EvalError::CannotIterate { .. })
));
}
#[test]
fn test_chain_field_index() {
let value = json!({"items": [1, 2, 3]});
let filter = Filter::Pipe(
Box::new(Filter::Field("items".to_string())),
Box::new(Filter::Index(0)),
);
assert_eq!(eval(&filter, &value).unwrap(), vec![json!(1)]);
}
#[test]
fn test_chain_index_field() {
let value = json!([{"id": 1}, {"id": 2}]);
let filter = Filter::Pipe(
Box::new(Filter::Index(0)),
Box::new(Filter::Field("id".to_string())),
);
assert_eq!(eval(&filter, &value).unwrap(), vec![json!(1)]);
}
#[test]
fn test_chain_iterate_field() {
let value = json!({"items": [{"id": 1}, {"id": 2}]});
let filter = Filter::Pipe(
Box::new(Filter::Pipe(
Box::new(Filter::Field("items".to_string())),
Box::new(Filter::Iterate),
)),
Box::new(Filter::Field("id".to_string())),
);
assert_eq!(eval(&filter, &value).unwrap(), vec![json!(1), json!(2)]);
}
#[test]
fn test_quoted_field_special_chars() {
let value = json!({"foo-bar": 1, "with space": 2});
assert_eq!(
eval(&Filter::Field("foo-bar".to_string()), &value).unwrap(),
vec![json!(1)]
);
assert_eq!(
eval(&Filter::Field("with space".to_string()), &value).unwrap(),
vec![json!(2)]
);
}
#[test]
fn test_quoted_field_empty() {
let value = json!({"": 42});
assert_eq!(
eval(&Filter::Field("".to_string()), &value).unwrap(),
vec![json!(42)]
);
}
#[test]
fn test_quoted_field_unicode() {
let value = json!({"日本語": "Japanese"});
assert_eq!(
eval(&Filter::Field("日本語".to_string()), &value).unwrap(),
vec![json!("Japanese")]
);
}
#[test]
fn test_pipe_explicit() {
let value = json!({"users": [{"name": "alice"}, {"name": "bob"}]});
let filter = Filter::Pipe(
Box::new(Filter::Pipe(
Box::new(Filter::Field("users".to_string())),
Box::new(Filter::Iterate),
)),
Box::new(Filter::Field("name".to_string())),
);
assert_eq!(
eval(&filter, &value).unwrap(),
vec![json!("alice"), json!("bob")]
);
}
}