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::Slice(start, end) => match value {
Value::Array(arr) => {
let len = arr.len() as i64;
let start_idx = match start {
Some(s) if *s >= 0 => (*s as usize).min(arr.len()),
Some(s) => (len + *s).max(0) as usize,
None => 0,
};
let end_idx = match end {
Some(e) if *e >= 0 => (*e as usize).min(arr.len()),
Some(e) => (len + *e).max(0) as usize,
None => arr.len(),
};
if start_idx >= end_idx {
Ok(vec![Value::Array(vec![])])
} else {
Ok(vec![Value::Array(arr[start_idx..end_idx].to_vec())])
}
}
Value::String(s) => {
let chars: Vec<char> = s.chars().collect();
let len = chars.len() as i64;
let start_idx = match start {
Some(st) if *st >= 0 => (*st as usize).min(chars.len()),
Some(st) => (len + *st).max(0) as usize,
None => 0,
};
let end_idx = match end {
Some(e) if *e >= 0 => (*e as usize).min(chars.len()),
Some(e) => (len + *e).max(0) as usize,
None => chars.len(),
};
if start_idx >= end_idx {
Ok(vec![Value::String(String::new())])
} else {
Ok(vec![Value::String(chars[start_idx..end_idx].iter().collect())])
}
}
_ => 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::Optional(inner) => {
match eval(inner, value) {
Ok(results) => Ok(results),
Err(_) => Ok(vec![]),
}
}
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")]
);
}
#[test]
fn test_optional_iterate_on_non_iterable() {
let filter = Filter::Optional(Box::new(Filter::Iterate));
let result = eval(&filter, &json!("not an array")).unwrap();
assert_eq!(result, Vec::<Value>::new());
}
#[test]
fn test_optional_iterate_on_array() {
let filter = Filter::Optional(Box::new(Filter::Iterate));
let result = eval(&filter, &json!([1, 2, 3])).unwrap();
assert_eq!(result, vec![json!(1), json!(2), json!(3)]);
}
#[test]
fn test_optional_field_succeeds() {
let filter = Filter::Optional(Box::new(Filter::Field("foo".to_string())));
let result = eval(&filter, &json!({"foo": "bar"})).unwrap();
assert_eq!(result, vec![json!("bar")]);
}
#[test]
fn test_slice_both_indices() {
let filter = Filter::Slice(Some(1), Some(3));
let result = eval(&filter, &json!([0, 1, 2, 3, 4])).unwrap();
assert_eq!(result, vec![json!([1, 2])]);
}
#[test]
fn test_slice_start_only() {
let filter = Filter::Slice(Some(2), None);
let result = eval(&filter, &json!([0, 1, 2, 3, 4])).unwrap();
assert_eq!(result, vec![json!([2, 3, 4])]);
}
#[test]
fn test_slice_end_only() {
let filter = Filter::Slice(None, Some(3));
let result = eval(&filter, &json!([0, 1, 2, 3, 4])).unwrap();
assert_eq!(result, vec![json!([0, 1, 2])]);
}
#[test]
fn test_slice_negative_end() {
let filter = Filter::Slice(None, Some(-1));
let result = eval(&filter, &json!([0, 1, 2, 3, 4])).unwrap();
assert_eq!(result, vec![json!([0, 1, 2, 3])]);
}
#[test]
fn test_slice_negative_start() {
let filter = Filter::Slice(Some(-2), None);
let result = eval(&filter, &json!([0, 1, 2, 3, 4])).unwrap();
assert_eq!(result, vec![json!([3, 4])]);
}
#[test]
fn test_slice_both_negative() {
let filter = Filter::Slice(Some(-3), Some(-1));
let result = eval(&filter, &json!([0, 1, 2, 3, 4])).unwrap();
assert_eq!(result, vec![json!([2, 3])]);
}
#[test]
fn test_slice_empty_result() {
let filter = Filter::Slice(Some(3), Some(1));
let result = eval(&filter, &json!([0, 1, 2, 3, 4])).unwrap();
assert_eq!(result, vec![json!([])]);
}
#[test]
fn test_slice_string() {
let filter = Filter::Slice(Some(1), Some(4));
let result = eval(&filter, &json!("hello")).unwrap();
assert_eq!(result, vec![json!("ell")]);
}
#[test]
fn test_slice_on_non_array() {
let filter = Filter::Slice(Some(0), Some(2));
let result = eval(&filter, &json!(123)).unwrap();
assert_eq!(result, vec![Value::Null]);
}
}