use simd_json::OwnedValue as Value;
use simd_json::StaticNode;
use super::eval;
use crate::error::EvalError;
use crate::filter::ast::{BoolOp, CompareOp, Filter};
pub fn eval_compare(
left: &Filter,
op: CompareOp,
right: &Filter,
value: &Value,
) -> Result<Vec<Value>, EvalError> {
let left_results = eval(left, value)?;
let right_results = eval(right, value)?;
let left_val = left_results
.into_iter()
.next()
.unwrap_or(Value::Static(StaticNode::Null));
let right_val = right_results
.into_iter()
.next()
.unwrap_or(Value::Static(StaticNode::Null));
let result = match op {
CompareOp::Eq => compare_values(&left_val, &right_val) == Some(std::cmp::Ordering::Equal),
CompareOp::Ne => compare_values(&left_val, &right_val) != Some(std::cmp::Ordering::Equal),
CompareOp::Lt => compare_values(&left_val, &right_val) == Some(std::cmp::Ordering::Less),
CompareOp::Le => matches!(
compare_values(&left_val, &right_val),
Some(std::cmp::Ordering::Less | std::cmp::Ordering::Equal)
),
CompareOp::Gt => compare_values(&left_val, &right_val) == Some(std::cmp::Ordering::Greater),
CompareOp::Ge => matches!(
compare_values(&left_val, &right_val),
Some(std::cmp::Ordering::Greater | std::cmp::Ordering::Equal)
),
};
Ok(vec![Value::Static(StaticNode::Bool(result))])
}
pub fn eval_bool_op(
left: &Filter,
op: BoolOp,
right: &Filter,
value: &Value,
) -> Result<Vec<Value>, EvalError> {
let left_results = eval(left, value)?;
let left_truthy = left_results.iter().any(|v| {
!matches!(
v,
Value::Static(StaticNode::Null) | Value::Static(StaticNode::Bool(false))
)
});
match op {
BoolOp::And => {
if !left_truthy {
Ok(vec![Value::Static(StaticNode::Bool(false))])
} else {
let right_results = eval(right, value)?;
let right_truthy = right_results.iter().any(|v| {
!matches!(
v,
Value::Static(StaticNode::Null) | Value::Static(StaticNode::Bool(false))
)
});
Ok(vec![Value::Static(StaticNode::Bool(right_truthy))])
}
}
BoolOp::Or => {
if left_truthy {
Ok(vec![Value::Static(StaticNode::Bool(true))])
} else {
let right_results = eval(right, value)?;
let right_truthy = right_results.iter().any(|v| {
!matches!(
v,
Value::Static(StaticNode::Null) | Value::Static(StaticNode::Bool(false))
)
});
Ok(vec![Value::Static(StaticNode::Bool(right_truthy))])
}
}
}
}
pub fn compare_values(a: &Value, b: &Value) -> Option<std::cmp::Ordering> {
use std::cmp::Ordering;
match (a, b) {
(Value::Static(StaticNode::Null), Value::Static(StaticNode::Null)) => Some(Ordering::Equal),
(Value::Static(StaticNode::Bool(a)), Value::Static(StaticNode::Bool(b))) => Some(a.cmp(b)),
(Value::Static(StaticNode::I64(a)), Value::Static(StaticNode::I64(b))) => Some(a.cmp(b)),
(Value::Static(StaticNode::U64(a)), Value::Static(StaticNode::U64(b))) => Some(a.cmp(b)),
(Value::Static(StaticNode::F64(a)), Value::Static(StaticNode::F64(b))) => a.partial_cmp(b),
(Value::Static(StaticNode::I64(a)), Value::Static(StaticNode::U64(b))) => {
Some((*a as i128).cmp(&(*b as i128)))
}
(Value::Static(StaticNode::U64(a)), Value::Static(StaticNode::I64(b))) => {
Some((*a as i128).cmp(&(*b as i128)))
}
(Value::Static(StaticNode::I64(a)), Value::Static(StaticNode::F64(b))) => {
(*a as f64).partial_cmp(b)
}
(Value::Static(StaticNode::F64(a)), Value::Static(StaticNode::I64(b))) => {
a.partial_cmp(&(*b as f64))
}
(Value::Static(StaticNode::U64(a)), Value::Static(StaticNode::F64(b))) => {
(*a as f64).partial_cmp(b)
}
(Value::Static(StaticNode::F64(a)), Value::Static(StaticNode::U64(b))) => {
a.partial_cmp(&(*b as f64))
}
(Value::String(a), Value::String(b)) => Some(a.cmp(b)),
(Value::Array(a), Value::Array(b)) => {
for (av, bv) in a.iter().zip(b.iter()) {
match compare_values(av, bv) {
Some(Ordering::Equal) => continue,
other => return other,
}
}
Some(a.len().cmp(&b.len()))
}
(Value::Object(a), Value::Object(b)) => {
let mut a_keys: Vec<_> = a.keys().collect();
let mut b_keys: Vec<_> = b.keys().collect();
a_keys.sort();
b_keys.sort();
for (ak, bk) in a_keys.iter().zip(b_keys.iter()) {
match ak.cmp(bk) {
Ordering::Equal => continue,
other => return Some(other),
}
}
if a_keys.len() != b_keys.len() {
return Some(a_keys.len().cmp(&b_keys.len()));
}
for k in a_keys {
match compare_values(&a[k], &b[k]) {
Some(Ordering::Equal) => continue,
other => return other,
}
}
Some(Ordering::Equal)
}
_ => None,
}
}