use crate::jsonpath::ast::comparison::{Comparable, ComparisonExpr, ComparisonOp};
impl ComparisonExpr {
pub fn eval(&self, current_value: &serde_json::Value, root_value: &serde_json::Value) -> bool {
let left = self.left().eval(current_value, root_value);
let right = self.right().eval(current_value, root_value);
match self.operator() {
ComparisonOp::Equal => is_equal(&left, &right),
ComparisonOp::Less => is_less(&left, &right),
ComparisonOp::NotEqual => !is_equal(&left, &right),
ComparisonOp::LessOrEqual => is_less(&left, &right) || is_equal(&left, &right),
ComparisonOp::Greater => is_less(&right, &left),
ComparisonOp::GreaterOrEqual => is_less(&right, &left) || is_equal(&left, &right),
}
}
}
fn is_equal(left: &Option<serde_json::Value>, right: &Option<serde_json::Value>) -> bool {
if left.is_none() && right.is_none() {
true
} else if let (Some(left), Some(right)) = (left, right) {
match (left, right) {
(serde_json::Value::Number(left_num), serde_json::Value::Number(right_num)) => {
f64_equals(left_num.as_f64().unwrap(), right_num.as_f64().unwrap())
}
_ => left == right,
}
} else {
false
}
}
fn f64_equals(a: f64, b: f64) -> bool {
(a - b).abs() < 1e-12
}
fn is_less(left: &Option<serde_json::Value>, right: &Option<serde_json::Value>) -> bool {
match (left, right) {
(Some(serde_json::Value::String(left)), Some(serde_json::Value::String(right))) => {
left < right
}
(Some(serde_json::Value::Number(left)), Some(serde_json::Value::Number(right))) => {
if let (Some(left), Some(right)) = (left.as_f64(), right.as_f64()) {
f64_less(left, right)
} else {
false
}
}
_ => false,
}
}
fn f64_less(a: f64, b: f64) -> bool {
(b - a) > 1e-12
}
impl Comparable {
pub fn eval(
&self,
current_value: &serde_json::Value,
root_value: &serde_json::Value,
) -> Option<serde_json::Value> {
match self {
Comparable::Literal(literal) => Some(literal.eval()),
Comparable::SingularQuery(singular_query) => {
singular_query.eval(current_value, root_value)
}
Comparable::Function(value_type_function) => {
value_type_function.eval(current_value, root_value)
}
}
}
}
#[cfg(test)]
mod tests {
use serde_json::json;
use super::*;
use crate::jsonpath::ast::literal::{Literal, Number};
use crate::jsonpath::ast::singular_query::SingularQuerySegment;
use crate::jsonpath::ast::{
selector::NameSelector,
singular_query::{AbsoluteSingularQuery, RelativeSingularQuery, SingularQuery},
};
fn name_query(name: &str) -> SingularQuery {
SingularQuery::Absolute(AbsoluteSingularQuery::new(vec![
SingularQuerySegment::Name(NameSelector::new(name.to_string())),
]))
}
#[test]
pub fn test_is_equal() {
assert!(is_equal(&Some(json!(110)), &Some(json!(110.0))));
assert!(is_equal(
&Some(json!(110.0)),
&Some(json!(110.00000000000001))
));
}
#[test]
pub fn test_is_less() {
assert!(!is_less(&Some(json!(110)), &Some(json!(110.0))));
assert!(!is_less(
&Some(json!(110.0)),
&Some(json!(110.00000000000001))
));
}
#[test]
pub fn test_comparison() {
let current_value = &serde_json::json!({});
let root_value = json!({
"obj": {"x": "y"},
"arr": [2, 3]
});
assert!(
ComparisonExpr::new(
Comparable::SingularQuery(name_query("absent1")),
Comparable::SingularQuery(name_query("absent2")),
ComparisonOp::Equal
)
.eval(current_value, &root_value)
);
assert!(
ComparisonExpr::new(
Comparable::SingularQuery(name_query("absent1")),
Comparable::SingularQuery(name_query("absent2")),
ComparisonOp::LessOrEqual
)
.eval(current_value, &root_value)
);
assert!(
!ComparisonExpr::new(
Comparable::SingularQuery(name_query("absent")),
Comparable::Literal(Literal::String("g".to_string())),
ComparisonOp::Equal
)
.eval(current_value, &root_value)
);
assert!(
!ComparisonExpr::new(
Comparable::SingularQuery(name_query("absent1")),
Comparable::SingularQuery(name_query("absent2")),
ComparisonOp::NotEqual
)
.eval(current_value, &root_value)
);
assert!(
ComparisonExpr::new(
Comparable::SingularQuery(name_query("absent")),
Comparable::Literal(Literal::String("g".to_string())),
ComparisonOp::NotEqual
)
.eval(current_value, &root_value)
);
assert!(
ComparisonExpr::new(
Comparable::Literal(Literal::Number(Number::Integer(1))),
Comparable::Literal(Literal::Number(Number::Integer(2))),
ComparisonOp::LessOrEqual
)
.eval(current_value, &root_value)
);
assert!(
!ComparisonExpr::new(
Comparable::Literal(Literal::Number(Number::Integer(1))),
Comparable::Literal(Literal::Number(Number::Integer(2))),
ComparisonOp::Greater
)
.eval(current_value, &root_value)
);
assert!(
!ComparisonExpr::new(
Comparable::Literal(Literal::Number(Number::Integer(13))),
Comparable::Literal(Literal::String("13".to_string())),
ComparisonOp::Equal
)
.eval(current_value, &root_value)
);
assert!(
ComparisonExpr::new(
Comparable::Literal(Literal::String("a".to_string())),
Comparable::Literal(Literal::String("b".to_string())),
ComparisonOp::LessOrEqual
)
.eval(current_value, &root_value)
);
assert!(
!ComparisonExpr::new(
Comparable::Literal(Literal::String("a".to_string())),
Comparable::Literal(Literal::String("b".to_string())),
ComparisonOp::Greater
)
.eval(current_value, &root_value)
);
assert!(
!ComparisonExpr::new(
Comparable::SingularQuery(name_query("obj")),
Comparable::SingularQuery(name_query("arr")),
ComparisonOp::Equal
)
.eval(current_value, &root_value)
);
assert!(
ComparisonExpr::new(
Comparable::SingularQuery(name_query("obj")),
Comparable::SingularQuery(name_query("arr")),
ComparisonOp::NotEqual
)
.eval(current_value, &root_value)
);
assert!(
ComparisonExpr::new(
Comparable::SingularQuery(name_query("obj")),
Comparable::SingularQuery(name_query("obj")),
ComparisonOp::Equal
)
.eval(current_value, &root_value)
);
assert!(
!ComparisonExpr::new(
Comparable::SingularQuery(name_query("obj")),
Comparable::SingularQuery(name_query("obj")),
ComparisonOp::NotEqual
)
.eval(current_value, &root_value)
);
assert!(
ComparisonExpr::new(
Comparable::SingularQuery(name_query("arr")),
Comparable::SingularQuery(name_query("arr")),
ComparisonOp::Equal
)
.eval(current_value, &root_value)
);
assert!(
!ComparisonExpr::new(
Comparable::SingularQuery(name_query("arr")),
Comparable::SingularQuery(name_query("arr")),
ComparisonOp::NotEqual
)
.eval(current_value, &root_value)
);
assert!(
!ComparisonExpr::new(
Comparable::SingularQuery(name_query("obj")),
Comparable::Literal(Literal::Number(Number::Integer(17))),
ComparisonOp::Equal
)
.eval(current_value, &root_value)
);
assert!(
ComparisonExpr::new(
Comparable::SingularQuery(name_query("obj")),
Comparable::Literal(Literal::Number(Number::Integer(17))),
ComparisonOp::NotEqual
)
.eval(current_value, &root_value)
);
assert!(
!ComparisonExpr::new(
Comparable::SingularQuery(name_query("obj")),
Comparable::SingularQuery(name_query("arr")),
ComparisonOp::LessOrEqual
)
.eval(current_value, &root_value)
);
assert!(
!ComparisonExpr::new(
Comparable::SingularQuery(name_query("obj")),
Comparable::SingularQuery(name_query("arr")),
ComparisonOp::Less
)
.eval(current_value, &root_value)
);
assert!(
ComparisonExpr::new(
Comparable::SingularQuery(name_query("obj")),
Comparable::SingularQuery(name_query("obj")),
ComparisonOp::LessOrEqual
)
.eval(current_value, &root_value)
);
assert!(
ComparisonExpr::new(
Comparable::SingularQuery(name_query("arr")),
Comparable::SingularQuery(name_query("arr")),
ComparisonOp::LessOrEqual
)
.eval(current_value, &root_value)
);
assert!(
!ComparisonExpr::new(
Comparable::Literal(Literal::Number(Number::Integer(1))),
Comparable::SingularQuery(name_query("arr")),
ComparisonOp::LessOrEqual
)
.eval(current_value, &root_value)
);
assert!(
!ComparisonExpr::new(
Comparable::Literal(Literal::Number(Number::Integer(1))),
Comparable::SingularQuery(name_query("arr")),
ComparisonOp::GreaterOrEqual
)
.eval(current_value, &root_value)
);
assert!(
!ComparisonExpr::new(
Comparable::Literal(Literal::Number(Number::Integer(1))),
Comparable::SingularQuery(name_query("arr")),
ComparisonOp::Greater
)
.eval(current_value, &root_value)
);
assert!(
!ComparisonExpr::new(
Comparable::Literal(Literal::Number(Number::Integer(1))),
Comparable::SingularQuery(name_query("arr")),
ComparisonOp::Less
)
.eval(current_value, &root_value)
);
assert!(
ComparisonExpr::new(
Comparable::Literal(Literal::Bool(true)),
Comparable::Literal(Literal::Bool(true)),
ComparisonOp::LessOrEqual
)
.eval(current_value, &root_value)
);
assert!(
!ComparisonExpr::new(
Comparable::Literal(Literal::Bool(true)),
Comparable::Literal(Literal::Bool(true)),
ComparisonOp::Greater
)
.eval(current_value, &root_value)
);
}
#[test]
pub fn test_number() {
let comparison = ComparisonExpr::new(
Comparable::SingularQuery(SingularQuery::Relative(RelativeSingularQuery::new(vec![
SingularQuerySegment::Name(NameSelector::new("a".to_string())),
]))),
Comparable::Literal(Literal::Number(Number::Integer(110))),
ComparisonOp::Equal,
);
assert!(comparison.eval(&serde_json::json!({"a":110}), &serde_json::json!({})));
assert!(comparison.eval(&serde_json::json!({"a":110.0}), &serde_json::json!({})));
assert!(comparison.eval(&serde_json::json!({"a":1.1e2}), &serde_json::json!({})));
let comparison = ComparisonExpr::new(
Comparable::SingularQuery(SingularQuery::Relative(RelativeSingularQuery::new(vec![
SingularQuerySegment::Name(NameSelector::new("a".to_string())),
]))),
Comparable::Literal(Literal::Number(Number::Float(110.0))),
ComparisonOp::Equal,
);
assert!(comparison.eval(&serde_json::json!({"a":110}), &serde_json::json!({})));
assert!(comparison.eval(&serde_json::json!({"a":110.0}), &serde_json::json!({})));
assert!(comparison.eval(&serde_json::json!({"a":1.1e2}), &serde_json::json!({})));
}
}