hypertune 0.6.2

Hypertune SDK for type safe configuration
Documentation
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")),
            },
        }
    }
}