1use std::marker::PhantomData;
4
5use quex::Encode;
6
7use crate::{
8 BigInt, Blob, Bool, Comparable, Double, Float, Int, Likeable, LowerCompatible, Nullable,
9 Qrafting, Required, Text, UBigInt, UInt, Untyped,
10 alias::Aliased,
11 expression::{
12 As, Between, Binary, EqExt, Expression, IsNull, IsPredicate, Like, LikeExt, Postfix,
13 op::{Eq, Gt, Gte, Lt, Lte, Neq},
14 },
15 lower::LowerCtx,
16 param::encode_param,
17 query::LowerProject,
18 ty::{Orderable, TypeMeta},
19};
20
21#[derive(Debug)]
23pub struct Column<M, T: TypeMeta> {
24 pub name: &'static str,
26 marker: PhantomData<(M, T)>,
27}
28
29impl<M, Ty: TypeMeta> Copy for Column<M, Ty> {}
30
31impl<M, Ty: TypeMeta> Clone for Column<M, Ty> {
32 fn clone(&self) -> Self {
33 *self
34 }
35}
36
37impl<M, T: TypeMeta> Column<M, T> {
38 pub const fn new(value: &'static str) -> Self {
40 Column {
41 name: value,
42 marker: PhantomData,
43 }
44 }
45
46 pub const fn name(self) -> &'static str {
48 self.name
49 }
50}
51
52pub trait Col {
54 fn typed<T: TypeMeta>(&self) -> Column<(), T>;
56
57 fn untyped(&self) -> Column<(), Untyped> {
59 self.typed::<Untyped>()
60 }
61
62 fn text(&self) -> Column<(), Text> {
64 self.typed::<Text>()
65 }
66
67 fn blob(&self) -> Column<(), Blob> {
69 self.typed::<Blob>()
70 }
71
72 fn bool(&self) -> Column<(), Bool> {
74 self.typed::<Bool>()
75 }
76
77 fn bigint(&self) -> Column<(), BigInt> {
79 self.typed::<BigInt>()
80 }
81
82 fn int(&self) -> Column<(), Int> {
84 self.typed::<Int>()
85 }
86
87 fn float(&self) -> Column<(), Float> {
89 self.typed::<Float>()
90 }
91
92 fn double(&self) -> Column<(), Double> {
94 self.typed::<Double>()
95 }
96
97 fn uint(&self) -> Column<(), UInt> {
99 self.typed::<UInt>()
100 }
101
102 fn ubigint(&self) -> Column<(), UBigInt> {
104 self.typed::<UBigInt>()
105 }
106}
107
108impl Col for &'static str {
109 fn typed<T: TypeMeta>(&self) -> Column<(), T> {
110 Column {
111 name: self,
112 marker: PhantomData,
113 }
114 }
115}
116
117impl<M, T: TypeMeta + Required> Column<M, T> {
118 pub fn nullable(self) -> Column<M, Nullable<T>> {
120 Column {
121 name: self.name,
122 marker: PhantomData,
123 }
124 }
125}
126
127impl<M: Qrafting, T: Comparable> Column<M, T> {
128 pub fn eq<E>(self, e: E) -> Binary<Eq, Self, E>
129 where
130 E: LowerCompatible<T>,
131 {
132 EqExt::eq(self, e)
133 }
134
135 pub fn neq<E>(self, e: E) -> Binary<Neq, Self, E>
136 where
137 E: LowerCompatible<T>,
138 {
139 EqExt::neq(self, e)
140 }
141}
142
143impl<T: Comparable> Column<(), T> {
144 pub fn eq<E>(self, e: E) -> Binary<Eq, Self, E>
145 where
146 E: LowerCompatible<T>,
147 {
148 EqExt::eq(self, e)
149 }
150
151 pub fn neq<E>(self, e: E) -> Binary<Neq, Self, E>
152 where
153 E: LowerCompatible<T>,
154 {
155 EqExt::neq(self, e)
156 }
157}
158
159pub trait CompareUntyped {
161 fn equal<E>(self, other: E) -> Binary<Eq, Column<(), Untyped>, UntypedValue<E>>
162 where
163 E: Encode;
164
165 fn not_equal<E>(self, other: E) -> Binary<Neq, Column<(), Untyped>, UntypedValue<E>>
166 where
167 E: Encode;
168
169 fn less_than<E>(self, other: E) -> Binary<Lt, Column<(), Untyped>, UntypedValue<E>>
170 where
171 E: Encode;
172
173 fn less_or_equal<E>(self, other: E) -> Binary<Lte, Column<(), Untyped>, UntypedValue<E>>
174 where
175 E: Encode;
176
177 fn greater_than<E>(self, other: E) -> Binary<Gt, Column<(), Untyped>, UntypedValue<E>>
178 where
179 E: Encode;
180
181 fn greater_or_equal<E>(self, other: E) -> Binary<Gte, Column<(), Untyped>, UntypedValue<E>>
182 where
183 E: Encode;
184}
185
186impl CompareUntyped for &'static str {
187 fn equal<E>(self, other: E) -> Binary<Eq, Column<(), Untyped>, UntypedValue<E>>
188 where
189 E: Encode,
190 {
191 Binary {
192 left: self.untyped(),
193 right: UntypedValue(other),
194 op: PhantomData,
195 }
196 }
197
198 fn not_equal<E>(self, other: E) -> Binary<Neq, Column<(), Untyped>, UntypedValue<E>>
199 where
200 E: Encode,
201 {
202 Binary {
203 left: self.untyped(),
204 right: UntypedValue(other),
205 op: PhantomData,
206 }
207 }
208
209 fn less_than<E>(self, other: E) -> Binary<Lt, Column<(), Untyped>, UntypedValue<E>>
210 where
211 E: Encode,
212 {
213 Binary {
214 left: self.untyped(),
215 right: UntypedValue(other),
216 op: PhantomData,
217 }
218 }
219
220 fn less_or_equal<E>(self, other: E) -> Binary<Lte, Column<(), Untyped>, UntypedValue<E>>
221 where
222 E: Encode,
223 {
224 Binary {
225 left: self.untyped(),
226 right: UntypedValue(other),
227 op: PhantomData,
228 }
229 }
230
231 fn greater_than<E>(self, other: E) -> Binary<Gt, Column<(), Untyped>, UntypedValue<E>>
232 where
233 E: Encode,
234 {
235 Binary {
236 left: self.untyped(),
237 right: UntypedValue(other),
238 op: PhantomData,
239 }
240 }
241
242 fn greater_or_equal<E>(self, other: E) -> Binary<Gte, Column<(), Untyped>, UntypedValue<E>>
243 where
244 E: Encode,
245 {
246 Binary {
247 left: self.untyped(),
248 right: UntypedValue(other),
249 op: PhantomData,
250 }
251 }
252}
253
254pub struct UntypedValue<T>(T);
255
256#[qraft_expression_macro::as_expression]
257impl<T: Encode> Expression for UntypedValue<T> {
258 type Type = Untyped;
259
260 fn lower(&self, ctx: &mut LowerCtx) -> usize {
261 let param = encode_param(&self.0, ctx.data);
262 ctx.lower_param(param)
263 }
264}
265
266impl<M: Qrafting, T: Comparable + Required> Column<M, Nullable<T>> {
267 #[allow(clippy::wrong_self_convention)]
268 pub fn is_null(self) -> Postfix<Self> {
269 IsNull::is_null(self)
270 }
271
272 #[allow(clippy::wrong_self_convention)]
273 pub fn is_not_null(self) -> Postfix<Self> {
274 IsNull::is_not_null(self)
275 }
276}
277
278impl<M: Qrafting, T: crate::Boolean> Column<M, T> {
279 #[allow(clippy::wrong_self_convention)]
280 pub fn is_true(self) -> Postfix<Self> {
281 IsPredicate::is_true(self)
282 }
283
284 #[allow(clippy::wrong_self_convention)]
285 pub fn is_false(self) -> Postfix<Self> {
286 IsPredicate::is_false(self)
287 }
288}
289
290impl<M: Qrafting, T: Likeable> Column<M, T> {
291 pub fn like<E>(self, other: E) -> Like<Self, E>
292 where
293 E: LowerCompatible<T>,
294 {
295 LikeExt::like(self, other)
296 }
297
298 pub fn not_like<E>(self, other: E) -> Like<Self, E>
299 where
300 E: LowerCompatible<T>,
301 {
302 LikeExt::not_like(self, other)
303 }
304}
305
306#[qraft_expression_macro::as_expression]
307impl<M: Qrafting, T: TypeMeta> Expression for Column<M, T> {
308 type Type = T;
309
310 fn lower(&self, ctx: &mut LowerCtx) -> usize {
311 ctx.lower_column(Some(M::TABLE), self.name)
312 }
313}
314
315#[qraft_expression_macro::as_expression]
316impl<T: TypeMeta> Expression for Column<(), T> {
317 type Type = T;
318
319 fn lower(&self, ctx: &mut LowerCtx) -> usize {
320 ctx.lower_column(None, self.name)
321 }
322}
323
324pub struct ColumnStar<M> {
326 pub marker: PhantomData<M>,
327}
328
329impl<M> Copy for ColumnStar<M> {}
330
331impl<M> Clone for ColumnStar<M> {
332 fn clone(&self) -> Self {
333 *self
334 }
335}
336
337impl<M: Qrafting> LowerProject for ColumnStar<M> {
338 fn lower_project(self, ctx: &mut LowerCtx) {
339 let table = M::TABLE;
340 ctx.lower_column(Some(table), "*");
341 }
342}
343
344impl<M: Qrafting> LowerProject for As<ColumnStar<M>> {
345 fn lower_project(self, ctx: &mut LowerCtx) {
346 ctx.lower_column(Some(self.table), "*");
347 }
348}
349
350impl<M: Qrafting> LowerProject for Aliased<As<ColumnStar<M>>> {
351 fn lower_project(self, ctx: &mut LowerCtx) {
352 let inner = ctx.lower_column(Some(self.inner.table), "*");
353 let _ = ctx.lower_alias(self.alias, inner);
354 }
355}
356
357impl<M> ColumnStar<M> {
358 pub const fn new() -> Self {
360 ColumnStar {
361 marker: PhantomData,
362 }
363 }
364}
365
366impl<M> Default for ColumnStar<M> {
367 fn default() -> Self {
368 Self::new()
369 }
370}
371
372pub const fn col<T: TypeMeta>(name: &'static str) -> Column<(), T> {
394 Column {
395 name,
396 marker: PhantomData,
397 }
398}
399
400impl<M: Qrafting, T: Orderable> Column<M, T> {
401 pub fn between<L, H>(self, low: L, high: H) -> Between<Self, L, H>
402 where
403 L: LowerCompatible<T>,
404 H: LowerCompatible<T>,
405 {
406 BetweenExt::between(self, low, high)
407 }
408
409 pub fn not_between<L, H>(self, low: L, high: H) -> Between<Self, L, H>
410 where
411 L: LowerCompatible<T>,
412 H: LowerCompatible<T>,
413 {
414 BetweenExt::not_between(self, low, high)
415 }
416}
417
418pub trait BetweenExt<T: Orderable>: Sized + Expression {
420 fn between<L, H>(self, low: L, high: H) -> Between<Self, L, H>
421 where
422 L: LowerCompatible<T>,
423 H: LowerCompatible<T>,
424 {
425 Between {
426 negated: false,
427 lhs: self,
428 low,
429 high,
430 }
431 }
432
433 fn not_between<L, H>(self, low: L, high: H) -> Between<Self, L, H>
434 where
435 L: LowerCompatible<T>,
436 H: LowerCompatible<T>,
437 {
438 Between {
439 negated: true,
440 lhs: self,
441 low,
442 high,
443 }
444 }
445}
446
447impl<T: Orderable, V> BetweenExt<T> for V where V: Expression<Type = T> {}
448
449#[cfg(test)]
450mod tests {
451 use super::{CompareUntyped, col};
452 use crate::{Sqlite, query::select};
453
454 #[test]
455 fn untyped_column_helpers_emit_expected_sql() {
456 let sql = select(col::<crate::Untyped>("priority"))
457 .from("tasks")
458 .filter("priority".greater_than(10))
459 .to_debug_sql::<Sqlite>();
460
461 assert_eq!(
462 sql,
463 r#"select "priority" from "tasks" where "priority" > ?; params=[10]"#
464 );
465 }
466
467 #[test]
468 fn untyped_column_equal_uses_untyped_builder() {
469 let sql = select("status")
470 .from("tasks")
471 .filter("status".equal("queued"))
472 .to_debug_sql::<Sqlite>();
473
474 assert_eq!(
475 sql,
476 r#"select "status" from "tasks" where "status" = ?; params=["queued"]"#
477 );
478 }
479}