Skip to main content

clicktype_query/
expr.rs

1//! Expression system for type-safe query building
2
3use std::marker::PhantomData;
4use clicktype_core::traits::{ClickHouseType, TypedColumn, Numeric};
5
6/// Base trait for all expressions
7pub trait Expression: Sized {
8    /// The output type of this expression
9    type Output: ClickHouseType;
10
11    /// Render the expression to SQL
12    fn render(&self) -> String;
13
14    /// Whether this is an aggregate expression
15    fn is_aggregate(&self) -> bool {
16        false
17    }
18}
19
20/// Column expression
21#[derive(Debug, Clone, Copy)]
22pub struct ColumnExpr<C: TypedColumn> {
23    _col: PhantomData<C>,
24}
25
26impl<C: TypedColumn> ColumnExpr<C> {
27    pub fn new() -> Self {
28        Self { _col: PhantomData }
29    }
30}
31
32impl<C: TypedColumn> Expression for ColumnExpr<C> {
33    type Output = C::Type;
34
35    fn render(&self) -> String {
36        C::name().to_string()
37    }
38}
39
40/// Literal value expression
41#[derive(Debug, Clone)]
42pub struct Literal<T: ClickHouseType> {
43    value: String,
44    _phantom: PhantomData<T>,
45}
46
47impl<T: ClickHouseType> Literal<T> {
48    pub fn new(value: String) -> Self {
49        Self {
50            value,
51            _phantom: PhantomData,
52        }
53    }
54}
55
56impl<T: ClickHouseType> Expression for Literal<T> {
57    type Output = T;
58
59    fn render(&self) -> String {
60        self.value.clone()
61    }
62}
63
64// === COMPARISON OPERATORS ===
65
66pub struct Eq;
67pub struct Ne;
68pub struct Lt;
69pub struct Le;
70pub struct Gt;
71pub struct Ge;
72
73pub trait ComparisonOp {
74    fn symbol() -> &'static str;
75}
76
77impl ComparisonOp for Eq {
78    fn symbol() -> &'static str {
79        "="
80    }
81}
82
83impl ComparisonOp for Ne {
84    fn symbol() -> &'static str {
85        "!="
86    }
87}
88
89impl ComparisonOp for Lt {
90    fn symbol() -> &'static str {
91        "<"
92    }
93}
94
95impl ComparisonOp for Le {
96    fn symbol() -> &'static str {
97        "<="
98    }
99}
100
101impl ComparisonOp for Gt {
102    fn symbol() -> &'static str {
103        ">"
104    }
105}
106
107impl ComparisonOp for Ge {
108    fn symbol() -> &'static str {
109        ">="
110    }
111}
112
113/// Binary comparison expression
114pub struct CompareExpr<L: Expression, R: Expression, Op: ComparisonOp> {
115    left: L,
116    right: R,
117    _op: PhantomData<Op>,
118}
119
120impl<L: Expression, R: Expression, Op: ComparisonOp> CompareExpr<L, R, Op> {
121    pub fn new(left: L, right: R) -> Self {
122        Self {
123            left,
124            right,
125            _op: PhantomData,
126        }
127    }
128}
129
130impl<L: Expression, R: Expression, Op: ComparisonOp> Expression for CompareExpr<L, R, Op> {
131    type Output = bool;
132
133    fn render(&self) -> String {
134        format!("{} {} {}", self.left.render(), Op::symbol(), self.right.render())
135    }
136}
137
138/// Trait for comparable expressions
139pub trait Comparable: Expression {
140    fn eq<R>(self, rhs: R) -> CompareExpr<Self, Literal<Self::Output>, Eq>
141    where
142        R: Into<Literal<Self::Output>>,
143    {
144        CompareExpr::new(self, rhs.into())
145    }
146
147    fn ne<R>(self, rhs: R) -> CompareExpr<Self, Literal<Self::Output>, Ne>
148    where
149        R: Into<Literal<Self::Output>>,
150    {
151        CompareExpr::new(self, rhs.into())
152    }
153
154    fn lt<R>(self, rhs: R) -> CompareExpr<Self, Literal<Self::Output>, Lt>
155    where
156        R: Into<Literal<Self::Output>>,
157    {
158        CompareExpr::new(self, rhs.into())
159    }
160
161    fn le<R>(self, rhs: R) -> CompareExpr<Self, Literal<Self::Output>, Le>
162    where
163        R: Into<Literal<Self::Output>>,
164    {
165        CompareExpr::new(self, rhs.into())
166    }
167
168    fn gt<R>(self, rhs: R) -> CompareExpr<Self, Literal<Self::Output>, Gt>
169    where
170        R: Into<Literal<Self::Output>>,
171    {
172        CompareExpr::new(self, rhs.into())
173    }
174
175    fn ge<R>(self, rhs: R) -> CompareExpr<Self, Literal<Self::Output>, Ge>
176    where
177        R: Into<Literal<Self::Output>>,
178    {
179        CompareExpr::new(self, rhs.into())
180    }
181}
182
183impl<E: Expression> Comparable for E {}
184
185// === LOGICAL OPERATORS ===
186
187pub struct And;
188pub struct Or;
189pub struct Not;
190
191/// AND expression
192pub struct AndExpr<L: Expression<Output = bool>, R: Expression<Output = bool>> {
193    left: L,
194    right: R,
195}
196
197impl<L: Expression<Output = bool>, R: Expression<Output = bool>> Expression for AndExpr<L, R> {
198    type Output = bool;
199
200    fn render(&self) -> String {
201        format!("({} AND {})", self.left.render(), self.right.render())
202    }
203}
204
205/// OR expression
206pub struct OrExpr<L: Expression<Output = bool>, R: Expression<Output = bool>> {
207    left: L,
208    right: R,
209}
210
211impl<L: Expression<Output = bool>, R: Expression<Output = bool>> Expression for OrExpr<L, R> {
212    type Output = bool;
213
214    fn render(&self) -> String {
215        format!("({} OR {})", self.left.render(), self.right.render())
216    }
217}
218
219/// NOT expression
220pub struct NotExpr<E: Expression<Output = bool>> {
221    inner: E,
222}
223
224impl<E: Expression<Output = bool>> Expression for NotExpr<E> {
225    type Output = bool;
226
227    fn render(&self) -> String {
228        format!("NOT ({})", self.inner.render())
229    }
230}
231
232/// Trait for logical operations
233pub trait Logical: Expression<Output = bool> {
234    fn and<R: Expression<Output = bool>>(self, rhs: R) -> AndExpr<Self, R> {
235        AndExpr { left: self, right: rhs }
236    }
237
238    fn or<R: Expression<Output = bool>>(self, rhs: R) -> OrExpr<Self, R> {
239        OrExpr { left: self, right: rhs }
240    }
241
242    fn not(self) -> NotExpr<Self> {
243        NotExpr { inner: self }
244    }
245}
246
247impl<E: Expression<Output = bool>> Logical for E {}
248
249// === ARITHMETIC OPERATORS ===
250
251pub struct Add;
252pub struct Sub;
253pub struct Mul;
254pub struct Div;
255pub struct Mod;
256
257pub trait ArithmeticOp {
258    fn symbol() -> &'static str;
259}
260
261impl ArithmeticOp for Add {
262    fn symbol() -> &'static str {
263        "+"
264    }
265}
266
267impl ArithmeticOp for Sub {
268    fn symbol() -> &'static str {
269        "-"
270    }
271}
272
273impl ArithmeticOp for Mul {
274    fn symbol() -> &'static str {
275        "*"
276    }
277}
278
279impl ArithmeticOp for Div {
280    fn symbol() -> &'static str {
281        "/"
282    }
283}
284
285impl ArithmeticOp for Mod {
286    fn symbol() -> &'static str {
287        "%"
288    }
289}
290
291/// Arithmetic expression
292pub struct ArithExpr<L: Expression, R: Expression, Op: ArithmeticOp> {
293    left: L,
294    right: R,
295    _op: PhantomData<Op>,
296}
297
298impl<L, R, Op> Expression for ArithExpr<L, R, Op>
299where
300    L: Expression,
301    R: Expression,
302    Op: ArithmeticOp,
303    L::Output: Numeric,
304    R::Output: Numeric,
305{
306    type Output = L::Output;
307
308    fn render(&self) -> String {
309        format!("({} {} {})", self.left.render(), Op::symbol(), self.right.render())
310    }
311}
312
313/// Trait for arithmetic operations
314pub trait Arithmetic: Expression
315where
316    Self::Output: Numeric,
317{
318    fn add<R>(self, rhs: R) -> ArithExpr<Self, R, Add>
319    where
320        R: Expression,
321        R::Output: Numeric,
322    {
323        ArithExpr {
324            left: self,
325            right: rhs,
326            _op: PhantomData,
327        }
328    }
329
330    fn sub<R>(self, rhs: R) -> ArithExpr<Self, R, Sub>
331    where
332        R: Expression,
333        R::Output: Numeric,
334    {
335        ArithExpr {
336            left: self,
337            right: rhs,
338            _op: PhantomData,
339        }
340    }
341
342    fn mul<R>(self, rhs: R) -> ArithExpr<Self, R, Mul>
343    where
344        R: Expression,
345        R::Output: Numeric,
346    {
347        ArithExpr {
348            left: self,
349            right: rhs,
350            _op: PhantomData,
351        }
352    }
353
354    fn div<R>(self, rhs: R) -> ArithExpr<Self, R, Div>
355    where
356        R: Expression,
357        R::Output: Numeric,
358    {
359        ArithExpr {
360            left: self,
361            right: rhs,
362            _op: PhantomData,
363        }
364    }
365
366    fn modulo<R>(self, rhs: R) -> ArithExpr<Self, R, Mod>
367    where
368        R: Expression,
369        R::Output: Numeric,
370    {
371        ArithExpr {
372            left: self,
373            right: rhs,
374            _op: PhantomData,
375        }
376    }
377}
378
379impl<E: Expression> Arithmetic for E where E::Output: Numeric {}
380
381// === STRING OPERATORS ===
382
383/// LIKE expression
384pub struct LikeExpr<E: Expression<Output = String>> {
385    inner: E,
386    pattern: String,
387}
388
389impl<E: Expression<Output = String>> Expression for LikeExpr<E> {
390    type Output = bool;
391
392    fn render(&self) -> String {
393        format!("{} LIKE '{}'", self.inner.render(), self.pattern)
394    }
395}
396
397/// ILIKE expression (case-insensitive)
398pub struct ILikeExpr<E: Expression<Output = String>> {
399    inner: E,
400    pattern: String,
401}
402
403impl<E: Expression<Output = String>> Expression for ILikeExpr<E> {
404    type Output = bool;
405
406    fn render(&self) -> String {
407        format!("{} ILIKE '{}'", self.inner.render(), self.pattern)
408    }
409}
410
411/// Trait for string operations
412pub trait StringOps: Expression<Output = String> {
413    fn like(self, pattern: &str) -> LikeExpr<Self> {
414        LikeExpr {
415            inner: self,
416            pattern: pattern.to_string(),
417        }
418    }
419
420    fn ilike(self, pattern: &str) -> ILikeExpr<Self> {
421        ILikeExpr {
422            inner: self,
423            pattern: pattern.to_string(),
424        }
425    }
426}
427
428impl<E: Expression<Output = String>> StringOps for E {}
429
430// === NULL OPERATORS ===
431
432/// IS NULL expression
433pub struct IsNullExpr<E: Expression> {
434    inner: E,
435}
436
437impl<E: Expression> Expression for IsNullExpr<E> {
438    type Output = bool;
439
440    fn render(&self) -> String {
441        format!("{} IS NULL", self.inner.render())
442    }
443}
444
445/// IS NOT NULL expression
446pub struct IsNotNullExpr<E: Expression> {
447    inner: E,
448}
449
450impl<E: Expression> Expression for IsNotNullExpr<E> {
451    type Output = bool;
452
453    fn render(&self) -> String {
454        format!("{} IS NOT NULL", self.inner.render())
455    }
456}
457
458/// Trait for NULL operations
459pub trait NullOps: Expression {
460    fn is_null(self) -> IsNullExpr<Self> {
461        IsNullExpr { inner: self }
462    }
463
464    fn is_not_null(self) -> IsNotNullExpr<Self> {
465        IsNotNullExpr { inner: self }
466    }
467}
468
469impl<E: Expression> NullOps for E {}
470
471// Implement Expression for TypedColumn
472impl<C: TypedColumn> Expression for C {
473    type Output = C::Type;
474
475    fn render(&self) -> String {
476        C::name().to_string()
477    }
478}
479
480// Helper for creating literals from primitives
481impl From<i8> for Literal<i8> {
482    fn from(value: i8) -> Self {
483        Literal::new(value.to_string())
484    }
485}
486
487impl From<i16> for Literal<i16> {
488    fn from(value: i16) -> Self {
489        Literal::new(value.to_string())
490    }
491}
492
493impl From<i32> for Literal<i32> {
494    fn from(value: i32) -> Self {
495        Literal::new(value.to_string())
496    }
497}
498
499impl From<i64> for Literal<i64> {
500    fn from(value: i64) -> Self {
501        Literal::new(value.to_string())
502    }
503}
504
505impl From<u8> for Literal<u8> {
506    fn from(value: u8) -> Self {
507        Literal::new(value.to_string())
508    }
509}
510
511impl From<u16> for Literal<u16> {
512    fn from(value: u16) -> Self {
513        Literal::new(value.to_string())
514    }
515}
516
517impl From<u32> for Literal<u32> {
518    fn from(value: u32) -> Self {
519        Literal::new(value.to_string())
520    }
521}
522
523impl From<u64> for Literal<u64> {
524    fn from(value: u64) -> Self {
525        Literal::new(value.to_string())
526    }
527}
528
529impl From<f32> for Literal<f32> {
530    fn from(value: f32) -> Self {
531        Literal::new(value.to_string())
532    }
533}
534
535impl From<f64> for Literal<f64> {
536    fn from(value: f64) -> Self {
537        Literal::new(value.to_string())
538    }
539}
540
541impl From<&str> for Literal<String> {
542    fn from(value: &str) -> Self {
543        Literal::new(format!("'{}'", value))
544    }
545}
546
547impl From<String> for Literal<String> {
548    fn from(value: String) -> Self {
549        Literal::new(format!("'{}'", value))
550    }
551}