1#[derive(Debug, Clone, PartialEq)]
3pub enum Expr {
4 Column(String),
6 Literal(Scalar),
8 BinaryOp {
10 left: Box<Expr>,
11 op: Operator,
12 right: Box<Expr>,
13 },
14 UnaryOp { op: UnaryOperator, expr: Box<Expr> },
16 Agg { func: AggFunc, expr: Box<Expr> },
18 Function {
20 input: Box<Expr>,
21 function: ExprFunction,
22 },
23 Alias { expr: Box<Expr>, name: String },
25 Wildcard,
27}
28
29#[derive(Debug, Clone, PartialEq, Eq)]
31pub enum ExprFunction {
32 String(StringFunction),
34 Datetime(DatetimeFunction),
36 List(ListFunction),
38}
39
40#[derive(Debug, Clone, PartialEq, Eq)]
42pub enum StringFunction {
43 ToLowercase,
45 ToUppercase,
47 Contains { pattern: String },
49 Replace {
51 pattern: String,
52 replacement: String,
53 },
54 StripChars { chars: Option<String> },
56 Split { separator: String },
58 LenChars,
60 Extract {
62 pattern: String,
63 capture_group: usize,
64 },
65}
66
67#[derive(Debug, Clone, PartialEq, Eq)]
69pub enum DatetimeFunction {
70 Year,
72 Month,
74 Day,
76 Weekday,
78 ToString,
80 ConvertTimeZone {
82 from_offset: String,
83 to_offset: String,
84 },
85}
86
87#[derive(Debug, Clone, PartialEq, Eq)]
89pub enum ListFunction {
90 Join {
92 separator: String,
93 null_value: Option<String>,
94 },
95 Len,
97 Contains { value: String },
99}
100
101#[derive(Debug, Copy, Clone, PartialEq, Eq)]
103pub enum Operator {
104 Add,
106 Sub,
108 Mul,
110 Div,
112 Eq,
114 Neq,
116 Gt,
118 Lt,
120 Ge,
122 Le,
124 And,
126 Or,
128}
129
130#[derive(Debug, Copy, Clone, PartialEq, Eq)]
132pub enum UnaryOperator {
133 Not,
135}
136
137#[derive(Debug, Copy, Clone, PartialEq, Eq)]
139pub enum AggFunc {
140 Sum,
142 Mean,
144 Count,
146 Min,
148 Max,
150}
151
152#[derive(Debug, Clone, PartialEq)]
154pub enum Scalar {
155 Null,
157 Boolean(bool),
159 Int64(i64),
161 Float64(f64),
163 Utf8(String),
165}
166
167impl From<()> for Scalar {
168 fn from(_: ()) -> Self {
169 Scalar::Null
170 }
171}
172
173impl From<bool> for Scalar {
174 fn from(v: bool) -> Self {
175 Scalar::Boolean(v)
176 }
177}
178
179impl From<i64> for Scalar {
180 fn from(v: i64) -> Self {
181 Scalar::Int64(v)
182 }
183}
184
185impl From<f64> for Scalar {
186 fn from(v: f64) -> Self {
187 Scalar::Float64(v)
188 }
189}
190
191impl From<String> for Scalar {
192 fn from(v: String) -> Self {
193 Scalar::Utf8(v)
194 }
195}
196
197impl From<&str> for Scalar {
198 fn from(v: &str) -> Self {
199 Scalar::Utf8(v.to_string())
200 }
201}
202
203impl Expr {
204 pub fn alias(self, name: impl Into<String>) -> Expr {
206 Expr::Alias {
207 expr: Box::new(self),
208 name: name.into(),
209 }
210 }
211
212 #[allow(clippy::should_implement_trait)]
214 pub fn add(self, rhs: Expr) -> Expr {
215 Expr::BinaryOp {
216 left: Box::new(self),
217 op: Operator::Add,
218 right: Box::new(rhs),
219 }
220 }
221
222 #[allow(clippy::should_implement_trait)]
224 pub fn sub(self, rhs: Expr) -> Expr {
225 Expr::BinaryOp {
226 left: Box::new(self),
227 op: Operator::Sub,
228 right: Box::new(rhs),
229 }
230 }
231
232 #[allow(clippy::should_implement_trait)]
234 pub fn mul(self, rhs: Expr) -> Expr {
235 Expr::BinaryOp {
236 left: Box::new(self),
237 op: Operator::Mul,
238 right: Box::new(rhs),
239 }
240 }
241
242 #[allow(clippy::should_implement_trait)]
244 pub fn div(self, rhs: Expr) -> Expr {
245 Expr::BinaryOp {
246 left: Box::new(self),
247 op: Operator::Div,
248 right: Box::new(rhs),
249 }
250 }
251
252 pub fn eq(self, rhs: Expr) -> Expr {
254 Expr::BinaryOp {
255 left: Box::new(self),
256 op: Operator::Eq,
257 right: Box::new(rhs),
258 }
259 }
260
261 pub fn neq(self, rhs: Expr) -> Expr {
263 Expr::BinaryOp {
264 left: Box::new(self),
265 op: Operator::Neq,
266 right: Box::new(rhs),
267 }
268 }
269
270 pub fn gt(self, rhs: Expr) -> Expr {
272 Expr::BinaryOp {
273 left: Box::new(self),
274 op: Operator::Gt,
275 right: Box::new(rhs),
276 }
277 }
278
279 pub fn lt(self, rhs: Expr) -> Expr {
281 Expr::BinaryOp {
282 left: Box::new(self),
283 op: Operator::Lt,
284 right: Box::new(rhs),
285 }
286 }
287
288 pub fn ge(self, rhs: Expr) -> Expr {
290 Expr::BinaryOp {
291 left: Box::new(self),
292 op: Operator::Ge,
293 right: Box::new(rhs),
294 }
295 }
296
297 pub fn le(self, rhs: Expr) -> Expr {
299 Expr::BinaryOp {
300 left: Box::new(self),
301 op: Operator::Le,
302 right: Box::new(rhs),
303 }
304 }
305
306 pub fn and_(self, rhs: Expr) -> Expr {
308 Expr::BinaryOp {
309 left: Box::new(self),
310 op: Operator::And,
311 right: Box::new(rhs),
312 }
313 }
314
315 pub fn or_(self, rhs: Expr) -> Expr {
317 Expr::BinaryOp {
318 left: Box::new(self),
319 op: Operator::Or,
320 right: Box::new(rhs),
321 }
322 }
323
324 pub fn not_(self) -> Expr {
326 Expr::UnaryOp {
327 op: UnaryOperator::Not,
328 expr: Box::new(self),
329 }
330 }
331
332 pub fn sum(self) -> Expr {
334 Expr::Agg {
335 func: AggFunc::Sum,
336 expr: Box::new(self),
337 }
338 }
339
340 pub fn mean(self) -> Expr {
342 Expr::Agg {
343 func: AggFunc::Mean,
344 expr: Box::new(self),
345 }
346 }
347
348 pub fn count(self) -> Expr {
350 Expr::Agg {
351 func: AggFunc::Count,
352 expr: Box::new(self),
353 }
354 }
355
356 pub fn min(self) -> Expr {
358 Expr::Agg {
359 func: AggFunc::Min,
360 expr: Box::new(self),
361 }
362 }
363
364 pub fn max(self) -> Expr {
366 Expr::Agg {
367 func: AggFunc::Max,
368 expr: Box::new(self),
369 }
370 }
371
372 pub fn str(self) -> StringExpr {
374 StringExpr { input: self }
375 }
376
377 pub fn dt(self) -> DatetimeExpr {
379 DatetimeExpr { input: self }
380 }
381
382 pub fn list(self) -> ListExpr {
384 ListExpr { input: self }
385 }
386}
387
388#[derive(Debug, Clone, PartialEq)]
390pub struct StringExpr {
391 input: Expr,
392}
393
394impl StringExpr {
395 fn function(self, function: StringFunction) -> Expr {
396 Expr::Function {
397 input: Box::new(self.input),
398 function: ExprFunction::String(function),
399 }
400 }
401
402 pub fn to_lowercase(self) -> Expr {
404 self.function(StringFunction::ToLowercase)
405 }
406
407 pub fn to_uppercase(self) -> Expr {
409 self.function(StringFunction::ToUppercase)
410 }
411
412 pub fn contains(self, pattern: impl Into<String>) -> Expr {
414 self.function(StringFunction::Contains {
415 pattern: pattern.into(),
416 })
417 }
418
419 pub fn replace(self, pattern: impl Into<String>, replacement: impl Into<String>) -> Expr {
421 self.function(StringFunction::Replace {
422 pattern: pattern.into(),
423 replacement: replacement.into(),
424 })
425 }
426
427 pub fn strip_chars(self, chars: Option<impl Into<String>>) -> Expr {
429 self.function(StringFunction::StripChars {
430 chars: chars.map(Into::into),
431 })
432 }
433
434 pub fn split(self, separator: impl Into<String>) -> Expr {
436 self.function(StringFunction::Split {
437 separator: separator.into(),
438 })
439 }
440
441 pub fn len_chars(self) -> Expr {
443 self.function(StringFunction::LenChars)
444 }
445
446 pub fn extract(self, pattern: impl Into<String>, capture_group: usize) -> Expr {
448 self.function(StringFunction::Extract {
449 pattern: pattern.into(),
450 capture_group,
451 })
452 }
453}
454
455#[derive(Debug, Clone, PartialEq)]
457pub struct DatetimeExpr {
458 input: Expr,
459}
460
461impl DatetimeExpr {
462 fn function(self, function: DatetimeFunction) -> Expr {
463 Expr::Function {
464 input: Box::new(self.input),
465 function: ExprFunction::Datetime(function),
466 }
467 }
468
469 pub fn year(self) -> Expr {
471 self.function(DatetimeFunction::Year)
472 }
473
474 pub fn month(self) -> Expr {
476 self.function(DatetimeFunction::Month)
477 }
478
479 pub fn day(self) -> Expr {
481 self.function(DatetimeFunction::Day)
482 }
483
484 pub fn weekday(self) -> Expr {
486 self.function(DatetimeFunction::Weekday)
487 }
488
489 #[allow(clippy::inherent_to_string)]
491 pub fn to_string(self) -> Expr {
492 self.function(DatetimeFunction::ToString)
493 }
494
495 pub fn convert_time_zone(
497 self,
498 from_offset: impl Into<String>,
499 to_offset: impl Into<String>,
500 ) -> Expr {
501 self.function(DatetimeFunction::ConvertTimeZone {
502 from_offset: from_offset.into(),
503 to_offset: to_offset.into(),
504 })
505 }
506}
507
508#[derive(Debug, Clone, PartialEq)]
510pub struct ListExpr {
511 input: Expr,
512}
513
514impl ListExpr {
515 fn function(self, function: ListFunction) -> Expr {
516 Expr::Function {
517 input: Box::new(self.input),
518 function: ExprFunction::List(function),
519 }
520 }
521
522 pub fn join(self, separator: impl Into<String>, null_value: Option<impl Into<String>>) -> Expr {
524 self.function(ListFunction::Join {
525 separator: separator.into(),
526 null_value: null_value.map(Into::into),
527 })
528 }
529
530 pub fn len(self) -> Expr {
532 self.function(ListFunction::Len)
533 }
534
535 pub fn contains(self, value: impl Into<String>) -> Expr {
537 self.function(ListFunction::Contains {
538 value: value.into(),
539 })
540 }
541}
542
543#[cfg(test)]
544mod tests {
545 use super::{AggFunc, Expr, ExprFunction, Operator, Scalar, StringFunction, UnaryOperator};
546 use crate::expr::{col, lit};
547
548 #[test]
549 fn builder_and_chaining_works() {
550 let expr = col("a").add(lit(1_i64)).alias("b");
551 assert_eq!(
552 expr,
553 Expr::Alias {
554 expr: Box::new(Expr::BinaryOp {
555 left: Box::new(Expr::Column("a".to_string())),
556 op: Operator::Add,
557 right: Box::new(Expr::Literal(Scalar::Int64(1))),
558 }),
559 name: "b".to_string(),
560 }
561 );
562 }
563
564 #[test]
565 fn logical_and_agg_works() {
566 let expr = col("x")
567 .gt(lit(1_i64))
568 .and_(col("y").lt(lit(10_i64)).not_())
569 .alias("p");
570
571 assert!(matches!(
572 expr,
573 Expr::Alias {
574 expr: _,
575 name
576 } if name == "p"
577 ));
578
579 let agg = col("v").sum();
580 assert_eq!(
581 agg,
582 Expr::Agg {
583 func: AggFunc::Sum,
584 expr: Box::new(Expr::Column("v".to_string()))
585 }
586 );
587
588 let u = Expr::Column("a".to_string()).not_();
589 assert_eq!(
590 u,
591 Expr::UnaryOp {
592 op: UnaryOperator::Not,
593 expr: Box::new(Expr::Column("a".to_string()))
594 }
595 );
596 }
597
598 #[test]
599 fn namespace_builders_create_function_exprs() {
600 let expr = col("name").str().to_lowercase().alias("name_lower");
601 assert_eq!(
602 expr,
603 Expr::Alias {
604 expr: Box::new(Expr::Function {
605 input: Box::new(Expr::Column("name".to_string())),
606 function: ExprFunction::String(StringFunction::ToLowercase),
607 }),
608 name: "name_lower".to_string(),
609 }
610 );
611 }
612}