use anyhow::anyhow;
use anyhow::Result;
use regex::Regex;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use crate::reduce::values_are_equal;
#[derive(Deserialize, Serialize, Eq, PartialEq, Debug, Clone)]
pub enum ComparisonOperator {
#[serde(rename = "AND")]
And,
#[serde(rename = "OR")]
Or,
#[serde(rename = "in")]
In,
#[serde(rename = "notIn")]
NotIn,
#[serde(rename = "==")]
Equal,
#[serde(rename = "!=")]
NotEqual,
#[serde(rename = "<")]
LessThan,
#[serde(rename = "<=")]
LessThanOrEqual,
#[serde(rename = ">")]
GreaterThan,
#[serde(rename = ">=")]
GreaterThanOrEqual,
#[serde(rename = "startsWith")]
StartsWith,
#[serde(rename = "notStartsWith")]
NotStartsWith,
#[serde(rename = "endsWith")]
EndsWith,
#[serde(rename = "notEndsWith")]
NotEndsWith,
#[serde(rename = "contains")]
Contains,
#[serde(rename = "notContains")]
NotContains,
#[serde(rename = "matches")]
Matches,
#[serde(rename = "notMatches")]
NotMatches,
}
impl ComparisonOperator {
pub fn evaluate(&self, a: Value, b: Value) -> Result<bool> {
match self {
ComparisonOperator::Equal => Ok(values_are_equal(Some(&a), Some(&b))),
ComparisonOperator::NotEqual => Ok(!values_are_equal(Some(&a), Some(&b))),
ComparisonOperator::And => match (a, b) {
(Value::Bool(a), Value::Bool(b)) => Ok(a && b),
_ => Err(anyhow!("Evaluated operands of comparison expression with 'AND' operator are not both booleans")),
},
ComparisonOperator::Or => match (a, b) {
(Value::Bool(a), Value::Bool(b)) => Ok(a || b),
_ => Err(anyhow!("Evaluated operands of comparison expression with 'OR' operator are not both booleans")),
},
ComparisonOperator::In => match b {
Value::Array(values) => Ok(values.into_iter().any(|value| values_are_equal(Some(&value), Some(&a)))),
_ => Err(anyhow!(
"Second operand of comparison operator is not an array"
)),
},
ComparisonOperator::NotIn => match b {
Value::Array(values) => Ok(!values.into_iter().any(|value| values_are_equal(Some(&value), Some(&a)))),
_ => Err(anyhow!(
"Second operand of comparison operator is not an array"
)),
},
ComparisonOperator::LessThan => match (a, b) {
(Value::Number(a), Value::Number(b)) => match (a.as_f64(), b.as_f64()) {
(Some(a), Some(b)) => Ok(a < b),
_ => Err(anyhow!("Cannot represent operands as an f64"))
}
_ => Err(anyhow!("Evaluated operands of comparison expression with '<' operator are not both numbers")),
},
ComparisonOperator::LessThanOrEqual => match (a, b) {
(Value::Number(a), Value::Number(b)) => match (a.as_f64(), b.as_f64()) {
(Some(a), Some(b)) => Ok(a <= b),
_ => Err(anyhow!("Cannot represent operands as an f64"))
}
_ => Err(anyhow!("Evaluated operands of comparison expression with '<=' operator are not both numbers")),
},
ComparisonOperator::GreaterThan => match (a, b) {
(Value::Number(a), Value::Number(b)) => match (a.as_f64(), b.as_f64()) {
(Some(a), Some(b)) => Ok(a > b),
_ => Err(anyhow!("Cannot represent operands as an f64"))
}
_ => Err(anyhow!("Evaluated operands of comparison expression with '>' operator are not both numbers")),
},
ComparisonOperator::GreaterThanOrEqual => match (a, b) {
(Value::Number(a), Value::Number(b)) => match (a.as_f64(), b.as_f64()) {
(Some(a), Some(b)) => Ok(a >= b),
_ => Err(anyhow!("Cannot represent operands as an f64"))
}
_ => Err(anyhow!("Evaluated operands of comparison expression with '>=' operator are not both numbers")),
},
ComparisonOperator::StartsWith => match (a, b) {
(Value::String(a), Value::String(b)) => Ok(a.starts_with(&b)),
(Value::Array(a), b) => Ok(values_are_equal(a.first(), Some(&b))),
_ => Err(anyhow!("Evaluated operands of comparison expression with 'startsWith' operator are not both strings or an array and an element")),
}
ComparisonOperator::NotStartsWith => match (a, b) {
(Value::String(a), Value::String(b)) => Ok(!a.starts_with(&b)),
(Value::Array(a), b) => Ok(!values_are_equal(a.first(), Some(&b))),
_ => Err(anyhow!("Evaluated operands of comparison expression with 'notStartsWith' operator are not both strings or an array and an element")),
}
ComparisonOperator::EndsWith => match (a, b) {
(Value::String(a), Value::String(b)) => Ok(a.ends_with(&b)),
(Value::Array(a), b) => Ok(values_are_equal(a.last(), Some(&b))),
_ => Err(anyhow!("Evaluated operands of comparison expression with 'endsWith' operator are not both strings or an array and an element")),
}
ComparisonOperator::NotEndsWith => match (a, b) {
(Value::String(a), Value::String(b)) => Ok(!a.ends_with(&b)),
(Value::Array(a), b) => Ok(!values_are_equal(a.last(), Some(&b))),
_ => Err(anyhow!("Evaluated operands of comparison expression with 'notEndsWith' operator are not both strings or an array and an element")),
}
ComparisonOperator::Contains => match (a, b) {
(Value::String(a), Value::String(b)) => Ok(a.contains(&b)),
(Value::Array(a), b) => Ok(a.into_iter().any(|value| values_are_equal(Some(&value), Some(&b)))),
_ => Err(anyhow!("Evaluated operands of comparison expression with 'contains' operator are not both strings or an array and an element")),
}
ComparisonOperator::NotContains => match (a, b) {
(Value::String(a), Value::String(b)) => Ok(!a.contains(&b)),
(Value::Array(a), b) => Ok(!a.into_iter().any(|value| values_are_equal(Some(&value), Some(&b)))),
_ => Err(anyhow!("Evaluated operands of comparison expression with 'notContains' operator are not both strings or an array and an element")),
}
ComparisonOperator::Matches => match (a, b) {
(Value::String(a), Value::String(b)) => {
let re = Regex::new(&b)?;
Ok(re.is_match(&a))
}
_ => Err(anyhow!("Evaluated operands of comparison expression with 'matches' operator are noth both strings")),
},
ComparisonOperator::NotMatches => match (a, b) {
(Value::String(a), Value::String(b)) => {
let re = Regex::new(&b)?;
Ok(!re.is_match(&a))
}
_ => Err(anyhow!("Evaluated operands of comparison expression with 'notMatches' operator are noth both strings")),
},
}
}
}