1use std::marker::PhantomData;
4
5use crate::column::{BigInt, Date, Integer, Real, SmallInt, Text, Time, Timestamp, Uuid, Varchar};
6use crate::expr::{Expr, JoinCondition, OrderBy};
7use crate::value::Value;
8
9pub trait ColumnRef {
12 fn name(&self) -> &'static str;
13 fn table(&self) -> &'static str;
14}
15
16#[derive(Debug, Clone, Copy)]
17pub struct Column<T> {
18 pub table: &'static str,
19 pub name: &'static str,
20 _marker: PhantomData<T>,
21}
22
23impl<T> ColumnRef for Column<T> {
24 fn name(&self) -> &'static str {
25 self.name
26 }
27
28 fn table(&self) -> &'static str {
29 self.table
30 }
31}
32
33impl<T> Column<T> {
34 pub const fn new(table: &'static str, name: &'static str) -> Self {
35 Self {
36 table,
37 name,
38 _marker: PhantomData,
39 }
40 }
41
42 pub fn qualified(&self) -> String {
43 format!(r#""{}"."{}""#, self.table, self.name)
44 }
45
46 pub fn asc(&self) -> OrderBy {
47 OrderBy {
48 column: self.qualified(),
49 direction: "ASC",
50 }
51 }
52
53 pub fn desc(&self) -> OrderBy {
54 OrderBy {
55 column: self.qualified(),
56 direction: "DESC",
57 }
58 }
59
60 pub fn equals<U>(&self, other: &Column<U>) -> JoinCondition {
61 JoinCondition {
62 left: self.qualified(),
63 right: other.qualified(),
64 }
65 }
66}
67
68pub trait CommonOps {
71 fn eq<V: Into<Value>>(&self, val: V) -> Expr;
72 fn ne<V: Into<Value>>(&self, val: V) -> Expr;
73 fn in_list(&self, values: &[Value]) -> Expr;
74 fn not_in(&self, values: &[Value]) -> Expr;
75 fn is_null(&self) -> Expr;
76 fn is_not_null(&self) -> Expr;
77}
78
79pub trait TextOps {
80 fn like<V: Into<Value>>(&self, val: V) -> Expr;
81}
82
83pub trait NumericOps {
84 fn gt<V: Into<Value>>(&self, val: V) -> Expr;
85 fn lt<V: Into<Value>>(&self, val: V) -> Expr;
86 fn gte<V: Into<Value>>(&self, val: V) -> Expr;
87 fn lte<V: Into<Value>>(&self, val: V) -> Expr;
88 fn between<L: Into<Value>, H: Into<Value>>(&self, low: L, high: H) -> Expr;
89}
90
91impl<T> CommonOps for Column<T> {
94 fn eq<V: Into<Value>>(&self, val: V) -> Expr {
95 Expr::comparison(self.qualified(), "=", val.into())
96 }
97
98 fn ne<V: Into<Value>>(&self, val: V) -> Expr {
99 Expr::comparison(self.qualified(), "!=", val.into())
100 }
101
102 fn in_list(&self, values: &[Value]) -> Expr {
103 Expr::in_list(self.qualified(), values.to_vec(), false)
104 }
105
106 fn not_in(&self, values: &[Value]) -> Expr {
107 Expr::in_list(self.qualified(), values.to_vec(), true)
108 }
109
110 fn is_null(&self) -> Expr {
111 Expr::is_null(self.qualified(), false)
112 }
113
114 fn is_not_null(&self) -> Expr {
115 Expr::is_null(self.qualified(), true)
116 }
117}
118
119macro_rules! impl_text_ops {
122 ($($ty:ty),+) => {
123 $(
124 impl TextOps for Column<$ty> {
125 fn like<V: Into<Value>>(&self, val: V) -> Expr {
126 Expr::comparison(self.qualified(), "LIKE", val.into())
127 }
128 }
129 )+
130 };
131}
132
133impl_text_ops!(Text, Uuid, Date, Time);
134
135impl<const N: u32> TextOps for Column<Varchar<N>> {
136 fn like<V: Into<Value>>(&self, val: V) -> Expr {
137 Expr::comparison(self.qualified(), "LIKE", val.into())
138 }
139}
140
141macro_rules! impl_numeric_ops {
144 ($($ty:ty),+) => {
145 $(
146 impl NumericOps for Column<$ty> {
147 fn gt<V: Into<Value>>(&self, val: V) -> Expr {
148 Expr::comparison(self.qualified(), ">", val.into())
149 }
150
151 fn lt<V: Into<Value>>(&self, val: V) -> Expr {
152 Expr::comparison(self.qualified(), "<", val.into())
153 }
154
155 fn gte<V: Into<Value>>(&self, val: V) -> Expr {
156 Expr::comparison(self.qualified(), ">=", val.into())
157 }
158
159 fn lte<V: Into<Value>>(&self, val: V) -> Expr {
160 Expr::comparison(self.qualified(), "<=", val.into())
161 }
162
163 fn between<L: Into<Value>, H: Into<Value>>(&self, low: L, high: H) -> Expr {
164 Expr::between(self.qualified(), low.into(), high.into())
165 }
166 }
167 )+
168 };
169}
170
171impl_numeric_ops!(Integer, Real, BigInt, SmallInt, Timestamp, Date, Time);