use serde_json::Value;
use super::helpers::{extract_datetime_value, extract_duration_value};
use crate::constants::INVALID_ARGS;
use crate::value_helpers::{coerce_to_number, loose_equals, strict_equals};
use crate::{CompiledNode, ContextStack, DataLogic, Result};
#[inline]
pub fn evaluate_equals(
args: &[CompiledNode],
context: &mut ContextStack,
engine: &DataLogic,
) -> Result<Value> {
if args.len() < 2 {
return Err(crate::Error::InvalidArguments(INVALID_ARGS.into()));
}
let first = engine.evaluate_node_cow(&args[0], context)?;
for item in args.iter().skip(1) {
let current = engine.evaluate_node_cow(item, context)?;
let result = compare_equals(&first, ¤t, false, engine)?;
if !result {
return Ok(Value::Bool(false));
}
}
Ok(Value::Bool(true))
}
#[inline]
pub fn evaluate_strict_equals(
args: &[CompiledNode],
context: &mut ContextStack,
engine: &DataLogic,
) -> Result<Value> {
if args.len() < 2 {
return Err(crate::Error::InvalidArguments(INVALID_ARGS.into()));
}
let first = engine.evaluate_node_cow(&args[0], context)?;
for item in args.iter().skip(1) {
let current = engine.evaluate_node_cow(item, context)?;
let result = compare_equals(&first, ¤t, true, engine)?;
if !result {
return Ok(Value::Bool(false));
}
}
Ok(Value::Bool(true))
}
#[inline]
fn could_be_datetime_or_duration(s: &str) -> bool {
let b = s.as_bytes();
if b.len() < 2 || !b[0].is_ascii_digit() {
return false;
}
if b.len() >= 10 && b[4] == b'-' {
return true;
}
b.iter().any(|&c| matches!(c, b'd' | b'h' | b'm' | b's'))
}
#[inline]
fn compare_equals(left: &Value, right: &Value, strict: bool, engine: &DataLogic) -> Result<bool> {
match (left, right) {
(Value::Number(_), Value::Number(_))
| (Value::Bool(_), Value::Bool(_))
| (Value::Null, Value::Null) => {
return if strict {
Ok(strict_equals(left, right))
} else {
loose_equals(left, right, engine)
};
}
(Value::String(l), Value::String(r))
if !could_be_datetime_or_duration(l) || !could_be_datetime_or_duration(r) =>
{
return if strict {
Ok(strict_equals(left, right))
} else {
loose_equals(left, right, engine)
};
}
(Value::Number(_), _)
| (_, Value::Number(_))
| (Value::Bool(_), _)
| (_, Value::Bool(_))
| (Value::Null, _)
| (_, Value::Null)
if !matches!(left, Value::Object(_)) && !matches!(right, Value::Object(_)) =>
{
return if strict {
Ok(strict_equals(left, right))
} else {
loose_equals(left, right, engine)
};
}
_ => {}
}
let left_dt = extract_datetime_value(left);
let right_dt = extract_datetime_value(right);
if let (Some(dt1), Some(dt2)) = (left_dt, right_dt) {
return Ok(dt1 == dt2);
}
let left_dur = extract_duration_value(left);
let right_dur = extract_duration_value(right);
if let (Some(dur1), Some(dur2)) = (left_dur, right_dur) {
return Ok(dur1 == dur2);
}
if strict {
Ok(strict_equals(left, right))
} else {
loose_equals(left, right, engine)
}
}
#[inline]
pub fn evaluate_not_equals(
args: &[CompiledNode],
context: &mut ContextStack,
engine: &DataLogic,
) -> Result<Value> {
if args.len() < 2 {
return Err(crate::Error::InvalidArguments(INVALID_ARGS.into()));
}
let first = engine.evaluate_node_cow(&args[0], context)?;
let second = engine.evaluate_node_cow(&args[1], context)?;
let equals = compare_equals(&first, &second, false, engine)?;
if !equals {
return Ok(Value::Bool(true));
}
if args.len() == 2 {
return Ok(Value::Bool(false));
}
Ok(Value::Bool(false))
}
#[inline]
pub fn evaluate_strict_not_equals(
args: &[CompiledNode],
context: &mut ContextStack,
engine: &DataLogic,
) -> Result<Value> {
if args.len() < 2 {
return Err(crate::Error::InvalidArguments(INVALID_ARGS.into()));
}
let first = engine.evaluate_node_cow(&args[0], context)?;
let second = engine.evaluate_node_cow(&args[1], context)?;
let equals = compare_equals(&first, &second, true, engine)?;
if !equals {
return Ok(Value::Bool(true));
}
if args.len() == 2 {
return Ok(Value::Bool(false));
}
Ok(Value::Bool(false))
}
#[derive(Clone, Copy)]
enum OrdOp {
Gt,
Gte,
Lt,
Lte,
}
impl OrdOp {
#[inline]
fn apply_f64(self, l: f64, r: f64) -> bool {
match self {
OrdOp::Gt => l > r,
OrdOp::Gte => l >= r,
OrdOp::Lt => l < r,
OrdOp::Lte => l <= r,
}
}
#[inline]
fn apply_str(self, l: &str, r: &str) -> bool {
match self {
OrdOp::Gt => l > r,
OrdOp::Gte => l >= r,
OrdOp::Lt => l < r,
OrdOp::Lte => l <= r,
}
}
#[inline]
fn apply_datetime(
self,
l: &crate::datetime::DataDateTime,
r: &crate::datetime::DataDateTime,
) -> bool {
match self {
OrdOp::Gt => l > r,
OrdOp::Gte => l >= r,
OrdOp::Lt => l < r,
OrdOp::Lte => l <= r,
}
}
#[inline]
fn apply_duration(
self,
l: &crate::datetime::DataDuration,
r: &crate::datetime::DataDuration,
) -> bool {
match self {
OrdOp::Gt => l > r,
OrdOp::Gte => l >= r,
OrdOp::Lt => l < r,
OrdOp::Lte => l <= r,
}
}
}
#[inline]
fn evaluate_chained_comparison(
args: &[CompiledNode],
context: &mut ContextStack,
engine: &DataLogic,
op: OrdOp,
) -> Result<Value> {
if args.len() < 2 {
return Err(crate::Error::InvalidArguments(INVALID_ARGS.into()));
}
let mut prev = engine.evaluate_node_cow(&args[0], context)?;
for item in args.iter().skip(1) {
let curr = engine.evaluate_node_cow(item, context)?;
if !compare_ordered(&prev, &curr, op, engine)? {
return Ok(Value::Bool(false));
}
prev = curr;
}
Ok(Value::Bool(true))
}
#[inline]
fn compare_ordered(left: &Value, right: &Value, op: OrdOp, engine: &DataLogic) -> Result<bool> {
if let (Value::Number(l), Value::Number(r)) = (left, right) {
return Ok(op.apply_f64(
l.as_f64().unwrap_or(f64::NAN),
r.as_f64().unwrap_or(f64::NAN),
));
}
if let (Value::String(l), Value::String(r)) = (left, right)
&& (!could_be_datetime_or_duration(l) || !could_be_datetime_or_duration(r))
{
return Ok(op.apply_str(l, r));
}
let left_dt = extract_datetime_value(left);
let right_dt = extract_datetime_value(right);
if let (Some(dt1), Some(dt2)) = (&left_dt, &right_dt) {
return Ok(op.apply_datetime(dt1, dt2));
}
let left_dur = if left_dt.is_none() {
extract_duration_value(left)
} else {
None
};
let right_dur = if right_dt.is_none() {
extract_duration_value(right)
} else {
None
};
if let (Some(dur1), Some(dur2)) = (&left_dur, &right_dur) {
return Ok(op.apply_duration(dur1, dur2));
}
if matches!(left, Value::Array(_) | Value::Object(_))
|| matches!(right, Value::Array(_) | Value::Object(_))
{
return Err(crate::constants::nan_error());
}
if let (Value::String(l), Value::String(r)) = (left, right) {
return Ok(op.apply_str(l, r));
}
let left_num = coerce_to_number(left, engine);
let right_num = coerce_to_number(right, engine);
if let (Some(l), Some(r)) = (left_num, right_num) {
return Ok(op.apply_f64(l, r));
}
if (matches!(left, Value::Number(_)) && matches!(right, Value::String(_)))
|| (matches!(right, Value::Number(_)) && matches!(left, Value::String(_)))
{
return Err(crate::constants::nan_error());
}
Ok(false)
}
#[inline]
pub fn evaluate_greater_than(
args: &[CompiledNode],
context: &mut ContextStack,
engine: &DataLogic,
) -> Result<Value> {
evaluate_chained_comparison(args, context, engine, OrdOp::Gt)
}
#[inline]
pub fn evaluate_greater_than_equal(
args: &[CompiledNode],
context: &mut ContextStack,
engine: &DataLogic,
) -> Result<Value> {
evaluate_chained_comparison(args, context, engine, OrdOp::Gte)
}
#[inline]
pub fn evaluate_less_than(
args: &[CompiledNode],
context: &mut ContextStack,
engine: &DataLogic,
) -> Result<Value> {
evaluate_chained_comparison(args, context, engine, OrdOp::Lt)
}
#[inline]
pub fn evaluate_less_than_equal(
args: &[CompiledNode],
context: &mut ContextStack,
engine: &DataLogic,
) -> Result<Value> {
evaluate_chained_comparison(args, context, engine, OrdOp::Lte)
}