1use serde::{Deserialize, Serialize};
2
3use crate::value::{PropertyInput, PropertyValue};
4#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
6#[serde(rename_all = "snake_case")]
7pub enum Expr {
8 Property(String),
10 Id,
12 Timestamp,
14 DateTimeNow,
16 Constant(PropertyValue),
18 Param(String),
20 Add { left: Box<Expr>, right: Box<Expr> },
22 Sub { left: Box<Expr>, right: Box<Expr> },
24 Mul { left: Box<Expr>, right: Box<Expr> },
26 Div { left: Box<Expr>, right: Box<Expr> },
28 Mod { left: Box<Expr>, right: Box<Expr> },
30 Neg { expr: Box<Expr> },
32 Case {
34 when_then: Vec<WhenThen>,
36 #[serde(default, skip_serializing_if = "Option::is_none")]
38 else_expr: Option<Box<Expr>>,
39 },
40}
41
42#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
44pub struct WhenThen {
45 pub when: Predicate,
47 pub then: Expr,
49}
50
51impl Expr {
52 pub fn prop(name: impl Into<String>) -> Self {
54 Self::Property(name.into())
55 }
56
57 pub fn val(value: impl Into<PropertyValue>) -> Self {
59 Self::Constant(value.into())
60 }
61
62 pub fn id() -> Self {
64 Self::Id
65 }
66
67 pub fn timestamp() -> Self {
69 Self::Timestamp
70 }
71
72 pub fn datetime() -> Self {
74 Self::DateTimeNow
75 }
76
77 pub fn param(name: impl Into<String>) -> Self {
79 Self::Param(name.into())
80 }
81
82 pub fn add_expr(self, other: Expr) -> Self {
84 Self::Add {
85 left: Box::new(self),
86 right: Box::new(other),
87 }
88 }
89
90 pub fn sub_expr(self, other: Expr) -> Self {
92 Self::Sub {
93 left: Box::new(self),
94 right: Box::new(other),
95 }
96 }
97
98 pub fn mul_expr(self, other: Expr) -> Self {
100 Self::Mul {
101 left: Box::new(self),
102 right: Box::new(other),
103 }
104 }
105
106 pub fn div_expr(self, other: Expr) -> Self {
108 Self::Div {
109 left: Box::new(self),
110 right: Box::new(other),
111 }
112 }
113
114 pub fn modulo(self, other: Expr) -> Self {
116 Self::Mod {
117 left: Box::new(self),
118 right: Box::new(other),
119 }
120 }
121
122 pub fn neg_expr(self) -> Self {
124 Self::Neg {
125 expr: Box::new(self),
126 }
127 }
128
129 #[allow(clippy::should_implement_trait)]
131 pub fn add(self, other: Expr) -> Self {
132 self.add_expr(other)
133 }
134
135 #[allow(clippy::should_implement_trait)]
137 pub fn sub(self, other: Expr) -> Self {
138 self.sub_expr(other)
139 }
140
141 #[allow(clippy::should_implement_trait)]
143 pub fn mul(self, other: Expr) -> Self {
144 self.mul_expr(other)
145 }
146
147 #[allow(clippy::should_implement_trait)]
149 pub fn div(self, other: Expr) -> Self {
150 self.div_expr(other)
151 }
152
153 #[allow(clippy::should_implement_trait)]
155 pub fn neg(self) -> Self {
156 self.neg_expr()
157 }
158
159 pub fn case(when_then: Vec<(Predicate, Expr)>, else_expr: Option<Expr>) -> Self {
161 Self::Case {
162 when_then: when_then
163 .into_iter()
164 .map(|(when, then)| WhenThen { when, then })
165 .collect(),
166 else_expr: else_expr.map(Box::new),
167 }
168 }
169}
170#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
172#[serde(rename_all = "snake_case")]
173pub enum StreamBound {
174 Literal(usize),
176 Expr(Expr),
178}
179
180impl StreamBound {
181 pub fn literal(value: usize) -> Self {
183 Self::Literal(value)
184 }
185
186 pub fn expr(expr: Expr) -> Self {
188 Self::Expr(expr)
189 }
190}
191
192impl From<usize> for StreamBound {
193 fn from(value: usize) -> Self {
194 Self::Literal(value)
195 }
196}
197
198impl From<u32> for StreamBound {
199 fn from(value: u32) -> Self {
200 Self::Literal(value as usize)
201 }
202}
203
204impl From<u16> for StreamBound {
205 fn from(value: u16) -> Self {
206 Self::Literal(value as usize)
207 }
208}
209
210impl From<u8> for StreamBound {
211 fn from(value: u8) -> Self {
212 Self::Literal(value as usize)
213 }
214}
215
216impl From<i64> for StreamBound {
217 fn from(value: i64) -> Self {
218 if value >= 0 {
219 Self::Literal(value as usize)
220 } else {
221 Self::Expr(Expr::val(value))
222 }
223 }
224}
225
226impl From<i32> for StreamBound {
227 fn from(value: i32) -> Self {
228 if value >= 0 {
229 Self::Literal(value as usize)
230 } else {
231 Self::Expr(Expr::val(value))
232 }
233 }
234}
235
236impl From<Expr> for StreamBound {
237 fn from(value: Expr) -> Self {
238 Self::Expr(value)
239 }
240}
241
242#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
244#[serde(rename_all = "snake_case")]
245pub enum CompareOp {
246 Eq,
248 Neq,
250 Gt,
252 Gte,
254 Lt,
256 Lte,
258}
259
260#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
262#[serde(rename_all = "snake_case")]
263pub enum Predicate {
264 Eq { left: Expr, right: Expr },
266 Neq { left: Expr, right: Expr },
268 Gt { left: Expr, right: Expr },
270 Gte { left: Expr, right: Expr },
272 Lt { left: Expr, right: Expr },
274 Lte { left: Expr, right: Expr },
276 Between { value: Expr, min: Expr, max: Expr },
278 HasKey { property: String },
280 IsNull { property: String },
282 IsNotNull { property: String },
284 StartsWith { value: Expr, prefix: Expr },
286 EndsWith { value: Expr, suffix: Expr },
288 Contains { value: Expr, substring: Expr },
290 IsIn { value: Expr, values: Expr },
292 And { predicates: Vec<Predicate> },
294 Or { predicates: Vec<Predicate> },
296 Not { predicate: Box<Predicate> },
298 Compare {
300 left: Expr,
302 op: CompareOp,
304 right: Expr,
306 },
307}
308
309pub type SourcePredicate = Predicate;
311
312impl Predicate {
313 pub fn eq(property: impl Into<String>, value: impl Into<PropertyInput>) -> Self {
315 Self::Eq {
316 left: Expr::prop(property),
317 right: value.into().into_expr(),
318 }
319 }
320
321 pub fn neq(property: impl Into<String>, value: impl Into<PropertyInput>) -> Self {
323 Self::Neq {
324 left: Expr::prop(property),
325 right: value.into().into_expr(),
326 }
327 }
328
329 pub fn gt(property: impl Into<String>, value: impl Into<PropertyInput>) -> Self {
331 Self::Gt {
332 left: Expr::prop(property),
333 right: value.into().into_expr(),
334 }
335 }
336
337 pub fn gte(property: impl Into<String>, value: impl Into<PropertyInput>) -> Self {
339 Self::Gte {
340 left: Expr::prop(property),
341 right: value.into().into_expr(),
342 }
343 }
344
345 pub fn lt(property: impl Into<String>, value: impl Into<PropertyInput>) -> Self {
347 Self::Lt {
348 left: Expr::prop(property),
349 right: value.into().into_expr(),
350 }
351 }
352
353 pub fn lte(property: impl Into<String>, value: impl Into<PropertyInput>) -> Self {
355 Self::Lte {
356 left: Expr::prop(property),
357 right: value.into().into_expr(),
358 }
359 }
360
361 pub fn between(
363 property: impl Into<String>,
364 min: impl Into<PropertyInput>,
365 max: impl Into<PropertyInput>,
366 ) -> Self {
367 Self::Between {
368 value: Expr::prop(property),
369 min: min.into().into_expr(),
370 max: max.into().into_expr(),
371 }
372 }
373
374 pub fn has_key(property: impl Into<String>) -> Self {
376 Self::HasKey {
377 property: property.into(),
378 }
379 }
380
381 pub fn is_null(property: impl Into<String>) -> Self {
383 Self::IsNull {
384 property: property.into(),
385 }
386 }
387
388 pub fn is_not_null(property: impl Into<String>) -> Self {
390 Self::IsNotNull {
391 property: property.into(),
392 }
393 }
394
395 pub fn starts_with(property: impl Into<String>, prefix: impl Into<String>) -> Self {
397 Self::StartsWith {
398 value: Expr::prop(property),
399 prefix: Expr::val(prefix.into()),
400 }
401 }
402
403 pub fn ends_with(property: impl Into<String>, suffix: impl Into<String>) -> Self {
405 Self::EndsWith {
406 value: Expr::prop(property),
407 suffix: Expr::val(suffix.into()),
408 }
409 }
410
411 pub fn contains(property: impl Into<String>, substring: impl Into<String>) -> Self {
413 Self::Contains {
414 value: Expr::prop(property),
415 substring: Expr::val(substring.into()),
416 }
417 }
418
419 pub fn contains_param(property: impl Into<String>, param_name: impl Into<String>) -> Self {
421 Self::Contains {
422 value: Expr::prop(property),
423 substring: Expr::param(param_name),
424 }
425 }
426
427 pub fn is_in(property: impl Into<String>, values: impl Into<PropertyValue>) -> Self {
429 Self::IsIn {
430 value: Expr::prop(property),
431 values: Expr::val(values.into()),
432 }
433 }
434
435 pub fn is_in_expr(property: impl Into<String>, values: Expr) -> Self {
437 Self::IsIn {
438 value: Expr::prop(property),
439 values,
440 }
441 }
442
443 pub fn is_in_param(property: impl Into<String>, param_name: impl Into<String>) -> Self {
445 Self::is_in_expr(property, Expr::param(param_name))
446 }
447
448 pub fn and(predicates: Vec<Predicate>) -> Self {
450 Self::And { predicates }
451 }
452
453 pub fn or(predicates: Vec<Predicate>) -> Self {
455 Self::Or { predicates }
456 }
457
458 #[allow(clippy::should_implement_trait)]
460 pub fn not(predicate: Predicate) -> Self {
461 Self::Not {
462 predicate: Box::new(predicate),
463 }
464 }
465
466 pub fn compare(left: Expr, op: CompareOp, right: Expr) -> Self {
468 Self::Compare { left, op, right }
469 }
470
471 pub fn eq_param(property: impl Into<String>, param_name: impl Into<String>) -> Self {
473 Self::eq(property, Expr::param(param_name))
474 }
475
476 pub fn neq_param(property: impl Into<String>, param_name: impl Into<String>) -> Self {
478 Self::neq(property, Expr::param(param_name))
479 }
480
481 pub fn gt_param(property: impl Into<String>, param_name: impl Into<String>) -> Self {
483 Self::gt(property, Expr::param(param_name))
484 }
485
486 pub fn gte_param(property: impl Into<String>, param_name: impl Into<String>) -> Self {
488 Self::gte(property, Expr::param(param_name))
489 }
490
491 pub fn lt_param(property: impl Into<String>, param_name: impl Into<String>) -> Self {
493 Self::lt(property, Expr::param(param_name))
494 }
495
496 pub fn lte_param(property: impl Into<String>, param_name: impl Into<String>) -> Self {
498 Self::lte(property, Expr::param(param_name))
499 }
500}