use serde::{Deserialize, Serialize};
use crate::value::{PropertyInput, PropertyValue};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum Expr {
Property(String),
Id,
Timestamp,
DateTimeNow,
Constant(PropertyValue),
Param(String),
Add { left: Box<Expr>, right: Box<Expr> },
Sub { left: Box<Expr>, right: Box<Expr> },
Mul { left: Box<Expr>, right: Box<Expr> },
Div { left: Box<Expr>, right: Box<Expr> },
Mod { left: Box<Expr>, right: Box<Expr> },
Neg { expr: Box<Expr> },
Case {
when_then: Vec<WhenThen>,
#[serde(default, skip_serializing_if = "Option::is_none")]
else_expr: Option<Box<Expr>>,
},
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct WhenThen {
pub when: Predicate,
pub then: Expr,
}
impl Expr {
pub fn prop(name: impl Into<String>) -> Self {
Self::Property(name.into())
}
pub fn val(value: impl Into<PropertyValue>) -> Self {
Self::Constant(value.into())
}
pub fn id() -> Self {
Self::Id
}
pub fn timestamp() -> Self {
Self::Timestamp
}
pub fn datetime() -> Self {
Self::DateTimeNow
}
pub fn param(name: impl Into<String>) -> Self {
Self::Param(name.into())
}
pub fn add_expr(self, other: Expr) -> Self {
Self::Add {
left: Box::new(self),
right: Box::new(other),
}
}
pub fn sub_expr(self, other: Expr) -> Self {
Self::Sub {
left: Box::new(self),
right: Box::new(other),
}
}
pub fn mul_expr(self, other: Expr) -> Self {
Self::Mul {
left: Box::new(self),
right: Box::new(other),
}
}
pub fn div_expr(self, other: Expr) -> Self {
Self::Div {
left: Box::new(self),
right: Box::new(other),
}
}
pub fn modulo(self, other: Expr) -> Self {
Self::Mod {
left: Box::new(self),
right: Box::new(other),
}
}
pub fn neg_expr(self) -> Self {
Self::Neg {
expr: Box::new(self),
}
}
#[allow(clippy::should_implement_trait)]
pub fn add(self, other: Expr) -> Self {
self.add_expr(other)
}
#[allow(clippy::should_implement_trait)]
pub fn sub(self, other: Expr) -> Self {
self.sub_expr(other)
}
#[allow(clippy::should_implement_trait)]
pub fn mul(self, other: Expr) -> Self {
self.mul_expr(other)
}
#[allow(clippy::should_implement_trait)]
pub fn div(self, other: Expr) -> Self {
self.div_expr(other)
}
#[allow(clippy::should_implement_trait)]
pub fn neg(self) -> Self {
self.neg_expr()
}
pub fn case(when_then: Vec<(Predicate, Expr)>, else_expr: Option<Expr>) -> Self {
Self::Case {
when_then: when_then
.into_iter()
.map(|(when, then)| WhenThen { when, then })
.collect(),
else_expr: else_expr.map(Box::new),
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum StreamBound {
Literal(usize),
Expr(Expr),
}
impl StreamBound {
pub fn literal(value: usize) -> Self {
Self::Literal(value)
}
pub fn expr(expr: Expr) -> Self {
Self::Expr(expr)
}
}
impl From<usize> for StreamBound {
fn from(value: usize) -> Self {
Self::Literal(value)
}
}
impl From<u32> for StreamBound {
fn from(value: u32) -> Self {
Self::Literal(value as usize)
}
}
impl From<u16> for StreamBound {
fn from(value: u16) -> Self {
Self::Literal(value as usize)
}
}
impl From<u8> for StreamBound {
fn from(value: u8) -> Self {
Self::Literal(value as usize)
}
}
impl From<i64> for StreamBound {
fn from(value: i64) -> Self {
if value >= 0 {
Self::Literal(value as usize)
} else {
Self::Expr(Expr::val(value))
}
}
}
impl From<i32> for StreamBound {
fn from(value: i32) -> Self {
if value >= 0 {
Self::Literal(value as usize)
} else {
Self::Expr(Expr::val(value))
}
}
}
impl From<Expr> for StreamBound {
fn from(value: Expr) -> Self {
Self::Expr(value)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum CompareOp {
Eq,
Neq,
Gt,
Gte,
Lt,
Lte,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum Predicate {
Eq { left: Expr, right: Expr },
Neq { left: Expr, right: Expr },
Gt { left: Expr, right: Expr },
Gte { left: Expr, right: Expr },
Lt { left: Expr, right: Expr },
Lte { left: Expr, right: Expr },
Between { value: Expr, min: Expr, max: Expr },
HasKey { property: String },
IsNull { property: String },
IsNotNull { property: String },
StartsWith { value: Expr, prefix: Expr },
EndsWith { value: Expr, suffix: Expr },
Contains { value: Expr, substring: Expr },
IsIn { value: Expr, values: Expr },
And { predicates: Vec<Predicate> },
Or { predicates: Vec<Predicate> },
Not { predicate: Box<Predicate> },
Compare {
left: Expr,
op: CompareOp,
right: Expr,
},
}
pub type SourcePredicate = Predicate;
impl Predicate {
pub fn eq(property: impl Into<String>, value: impl Into<PropertyInput>) -> Self {
Self::Eq {
left: Expr::prop(property),
right: value.into().into_expr(),
}
}
pub fn neq(property: impl Into<String>, value: impl Into<PropertyInput>) -> Self {
Self::Neq {
left: Expr::prop(property),
right: value.into().into_expr(),
}
}
pub fn gt(property: impl Into<String>, value: impl Into<PropertyInput>) -> Self {
Self::Gt {
left: Expr::prop(property),
right: value.into().into_expr(),
}
}
pub fn gte(property: impl Into<String>, value: impl Into<PropertyInput>) -> Self {
Self::Gte {
left: Expr::prop(property),
right: value.into().into_expr(),
}
}
pub fn lt(property: impl Into<String>, value: impl Into<PropertyInput>) -> Self {
Self::Lt {
left: Expr::prop(property),
right: value.into().into_expr(),
}
}
pub fn lte(property: impl Into<String>, value: impl Into<PropertyInput>) -> Self {
Self::Lte {
left: Expr::prop(property),
right: value.into().into_expr(),
}
}
pub fn between(
property: impl Into<String>,
min: impl Into<PropertyInput>,
max: impl Into<PropertyInput>,
) -> Self {
Self::Between {
value: Expr::prop(property),
min: min.into().into_expr(),
max: max.into().into_expr(),
}
}
pub fn has_key(property: impl Into<String>) -> Self {
Self::HasKey {
property: property.into(),
}
}
pub fn is_null(property: impl Into<String>) -> Self {
Self::IsNull {
property: property.into(),
}
}
pub fn is_not_null(property: impl Into<String>) -> Self {
Self::IsNotNull {
property: property.into(),
}
}
pub fn starts_with(property: impl Into<String>, prefix: impl Into<String>) -> Self {
Self::StartsWith {
value: Expr::prop(property),
prefix: Expr::val(prefix.into()),
}
}
pub fn ends_with(property: impl Into<String>, suffix: impl Into<String>) -> Self {
Self::EndsWith {
value: Expr::prop(property),
suffix: Expr::val(suffix.into()),
}
}
pub fn contains(property: impl Into<String>, substring: impl Into<String>) -> Self {
Self::Contains {
value: Expr::prop(property),
substring: Expr::val(substring.into()),
}
}
pub fn contains_param(property: impl Into<String>, param_name: impl Into<String>) -> Self {
Self::Contains {
value: Expr::prop(property),
substring: Expr::param(param_name),
}
}
pub fn is_in(property: impl Into<String>, values: impl Into<PropertyValue>) -> Self {
Self::IsIn {
value: Expr::prop(property),
values: Expr::val(values.into()),
}
}
pub fn is_in_expr(property: impl Into<String>, values: Expr) -> Self {
Self::IsIn {
value: Expr::prop(property),
values,
}
}
pub fn is_in_param(property: impl Into<String>, param_name: impl Into<String>) -> Self {
Self::is_in_expr(property, Expr::param(param_name))
}
pub fn and(predicates: Vec<Predicate>) -> Self {
Self::And { predicates }
}
pub fn or(predicates: Vec<Predicate>) -> Self {
Self::Or { predicates }
}
#[allow(clippy::should_implement_trait)]
pub fn not(predicate: Predicate) -> Self {
Self::Not {
predicate: Box::new(predicate),
}
}
pub fn compare(left: Expr, op: CompareOp, right: Expr) -> Self {
Self::Compare { left, op, right }
}
pub fn eq_param(property: impl Into<String>, param_name: impl Into<String>) -> Self {
Self::eq(property, Expr::param(param_name))
}
pub fn neq_param(property: impl Into<String>, param_name: impl Into<String>) -> Self {
Self::neq(property, Expr::param(param_name))
}
pub fn gt_param(property: impl Into<String>, param_name: impl Into<String>) -> Self {
Self::gt(property, Expr::param(param_name))
}
pub fn gte_param(property: impl Into<String>, param_name: impl Into<String>) -> Self {
Self::gte(property, Expr::param(param_name))
}
pub fn lt_param(property: impl Into<String>, param_name: impl Into<String>) -> Self {
Self::lt(property, Expr::param(param_name))
}
pub fn lte_param(property: impl Into<String>, param_name: impl Into<String>) -> Self {
Self::lte(property, Expr::param(param_name))
}
}