datasources 0.1.5

Async datasource interfaces, middleware, registry, and test doubles
Documentation
use super::array_expression::ArrayExpression;
use super::expression::Expression;
use super::identifier::Identifier;
use super::literal::Literal;
use crate::value::Value;

#[derive(Clone)]
pub enum Operator {
    Equals {
        left: Box<Expression>,
        right: Box<Expression>,
    },
    NotEquals {
        left: Box<Expression>,
        right: Box<Expression>,
    },
    GreaterThan {
        left: Box<Expression>,
        right: Box<Expression>,
    },
    LessThan {
        left: Box<Expression>,
        right: Box<Expression>,
    },
    GreaterThanOrEqual {
        left: Box<Expression>,
        right: Box<Expression>,
    },
    LessThanOrEqual {
        left: Box<Expression>,
        right: Box<Expression>,
    },
    And {
        left: Box<Expression>,
        right: Box<Expression>,
    },
    Or {
        left: Box<Expression>,
        right: Box<Expression>,
    },
    In {
        left: Box<Expression>,
        right: Box<Expression>,
    },
    Not {
        argument: Box<Expression>,
    },
    IsNull {
        argument: Box<Expression>,
    },
    IsNotNull {
        argument: Box<Expression>,
    },
    Like {
        left: Box<Expression>,
        right: Box<Expression>,
    },
    NotLike {
        left: Box<Expression>,
        right: Box<Expression>,
    },
    Between {
        value: Box<Expression>,
        low: Box<Expression>,
        high: Box<Expression>,
    },
    NotBetween {
        value: Box<Expression>,
        low: Box<Expression>,
        high: Box<Expression>,
    },
    NotIn {
        left: Box<Expression>,
        right: Box<Expression>,
    },
    Exists {
        argument: Box<Expression>,
    },
}

impl Operator {
    pub fn equals(field: impl Into<String>, value: impl Into<Value>) -> Self {
        Self::Equals {
            left: Box::new(Expression::Identifier(Identifier::new(field))),
            right: Box::new(Expression::Literal(Literal::new(value))),
        }
    }

    pub fn not_equals(field: impl Into<String>, value: impl Into<Value>) -> Self {
        Self::NotEquals {
            left: Box::new(Expression::Identifier(Identifier::new(field))),
            right: Box::new(Expression::Literal(Literal::new(value))),
        }
    }

    pub fn greater_than(field: impl Into<String>, value: impl Into<Value>) -> Self {
        Self::GreaterThan {
            left: Box::new(Expression::Identifier(Identifier::new(field))),
            right: Box::new(Expression::Literal(Literal::new(value))),
        }
    }

    pub fn less_than(field: impl Into<String>, value: impl Into<Value>) -> Self {
        Self::LessThan {
            left: Box::new(Expression::Identifier(Identifier::new(field))),
            right: Box::new(Expression::Literal(Literal::new(value))),
        }
    }

    pub fn greater_than_or_equal(field: impl Into<String>, value: impl Into<Value>) -> Self {
        Self::GreaterThanOrEqual {
            left: Box::new(Expression::Identifier(Identifier::new(field))),
            right: Box::new(Expression::Literal(Literal::new(value))),
        }
    }

    pub fn less_than_or_equal(field: impl Into<String>, value: impl Into<Value>) -> Self {
        Self::LessThanOrEqual {
            left: Box::new(Expression::Identifier(Identifier::new(field))),
            right: Box::new(Expression::Literal(Literal::new(value))),
        }
    }

    pub fn and(left: impl Into<Expression>, right: impl Into<Expression>) -> Self {
        Self::And {
            left: Box::new(left.into()),
            right: Box::new(right.into()),
        }
    }

    pub fn or(left: impl Into<Expression>, right: impl Into<Expression>) -> Self {
        Self::Or {
            left: Box::new(left.into()),
            right: Box::new(right.into()),
        }
    }

    pub fn not(argument: impl Into<Expression>) -> Self {
        Self::Not {
            argument: Box::new(argument.into()),
        }
    }

    pub fn like(field: impl Into<String>, value: impl Into<Value>) -> Self {
        Self::binary_value(field, value, |left, right| Self::Like { left, right })
    }

    pub fn not_like(field: impl Into<String>, value: impl Into<Value>) -> Self {
        Self::binary_value(field, value, |left, right| Self::NotLike { left, right })
    }

    pub fn is_null(argument: impl Into<Expression>) -> Self {
        Self::IsNull {
            argument: Box::new(argument.into()),
        }
    }

    pub fn is_not_null(argument: impl Into<Expression>) -> Self {
        Self::IsNotNull {
            argument: Box::new(argument.into()),
        }
    }

    pub fn between(
        field: impl Into<String>,
        low: impl Into<Value>,
        high: impl Into<Value>,
    ) -> Self {
        Self::bounds(field, low, high, |value, low, high| Self::Between {
            value,
            low,
            high,
        })
    }

    pub fn not_between(
        field: impl Into<String>,
        low: impl Into<Value>,
        high: impl Into<Value>,
    ) -> Self {
        Self::bounds(field, low, high, |value, low, high| Self::NotBetween {
            value,
            low,
            high,
        })
    }

    pub fn not_in(field: impl Into<String>, values: Vec<Value>) -> Self {
        Self::NotIn {
            left: Box::new(Expression::Identifier(Identifier::new(field))),
            right: Box::new(Expression::Array(ArrayExpression::new(
                values
                    .into_iter()
                    .map(|value| Expression::Literal(Literal::new(value)))
                    .collect(),
            ))),
        }
    }

    pub fn exists(argument: impl Into<Expression>) -> Self {
        Self::Exists {
            argument: Box::new(argument.into()),
        }
    }

    fn binary_value(
        field: impl Into<String>,
        value: impl Into<Value>,
        build: impl FnOnce(Box<Expression>, Box<Expression>) -> Self,
    ) -> Self {
        build(
            Box::new(Expression::Identifier(Identifier::new(field))),
            Box::new(Expression::Literal(Literal::new(value))),
        )
    }

    fn bounds(
        field: impl Into<String>,
        low: impl Into<Value>,
        high: impl Into<Value>,
        build: impl FnOnce(Box<Expression>, Box<Expression>, Box<Expression>) -> Self,
    ) -> Self {
        build(
            Box::new(Expression::Identifier(Identifier::new(field))),
            Box::new(Expression::Literal(Literal::new(low))),
            Box::new(Expression::Literal(Literal::new(high))),
        )
    }

    pub fn in_list(field: impl Into<String>, values: Vec<Value>) -> Self {
        Self::In {
            left: Box::new(Expression::Identifier(Identifier::new(field))),
            right: Box::new(Expression::Array(ArrayExpression::new(
                values
                    .into_iter()
                    .map(|value| Expression::Literal(Literal::new(value)))
                    .collect(),
            ))),
        }
    }
}

impl From<Operator> for Expression {
    fn from(op: Operator) -> Self {
        Expression::Operator(op)
    }
}