1use super::related::Joined;
5use super::{Column, Expr, Ident};
6use crate::model::Model;
7use crate::plan::{OrderExpr, QueryPlan, SelectExpr};
8use crate::relations::ForeignKey;
9use crate::types::{DbType, SqlType};
10use crate::value::Value;
11use chrono::{DateTime, NaiveDate, NaiveTime, Utc};
12use rust_decimal::Decimal;
13use std::marker::PhantomData;
14use uuid::Uuid;
15
16pub struct Field<M, T> {
21 name: &'static str,
22 _marker: PhantomData<fn() -> (M, T)>,
23}
24
25impl<M, T> Clone for Field<M, T> {
26 fn clone(&self) -> Self {
27 *self
28 }
29}
30impl<M, T> Copy for Field<M, T> {}
31
32impl<M, T> std::fmt::Debug for Field<M, T> {
33 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
34 f.debug_tuple("Field").field(&self.name).finish()
35 }
36}
37
38impl<M, T> Field<M, T> {
39 pub const fn new(name: &'static str) -> Self {
41 Self {
42 name,
43 _marker: PhantomData,
44 }
45 }
46
47 pub const fn name(&self) -> &'static str {
49 self.name
50 }
51
52 pub fn outer_ref(self) -> Expr {
55 Expr::OuterRef(Column::new(self.name))
56 }
57
58 pub fn set(self, value: impl Operand<M, T>) -> (Ident, Expr) {
60 (self.name.into(), value.into_expr())
61 }
62
63 pub fn set_expr(self, value: impl Into<Expr>) -> (Ident, Expr) {
65 (self.name.into(), value.into())
66 }
67
68 pub fn select(self) -> SelectExpr {
70 SelectExpr::new(Expr::col(self.name), Some(self.name.into()))
71 }
72}
73
74impl<M, T> From<Field<M, T>> for Expr {
75 fn from(f: Field<M, T>) -> Self {
76 Expr::col(f.name)
77 }
78}
79
80impl<M, T> From<Field<M, T>> for SelectExpr {
81 fn from(f: Field<M, T>) -> Self {
82 f.select()
83 }
84}
85
86pub struct Calc<M, T> {
89 expr: Expr,
90 _marker: PhantomData<fn() -> (M, T)>,
91}
92
93impl<M, T> Calc<M, T> {
94 fn new(expr: Expr) -> Self {
95 Self {
96 expr,
97 _marker: PhantomData,
98 }
99 }
100
101 pub fn alias(self, name: impl Into<Ident>) -> SelectExpr {
103 SelectExpr::new(self.expr, Some(name.into()))
104 }
105}
106
107impl<M, T> Clone for Calc<M, T> {
108 fn clone(&self) -> Self {
109 Self::new(self.expr.clone())
110 }
111}
112
113impl<M, T> std::fmt::Debug for Calc<M, T> {
114 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
115 f.debug_tuple("Calc").field(&self.expr).finish()
116 }
117}
118
119impl<M, T> From<Calc<M, T>> for Expr {
120 fn from(c: Calc<M, T>) -> Self {
121 c.expr
122 }
123}
124
125pub trait Operand<M, T> {
129 fn into_expr(self) -> Expr;
131}
132
133pub trait Literal<T> {
136 fn into_value(self) -> Value;
138}
139
140macro_rules! literal {
142 ([$($generics:tt)*] $from:ty => $to:ty, |$v:ident| $convert:expr) => {
143 impl<$($generics)*> Literal<$to> for $from {
144 fn into_value(self) -> Value {
145 let $v = self;
146 $convert
147 }
148 }
149 impl<$($generics)*, M> Operand<M, $to> for $from {
150 fn into_expr(self) -> Expr {
151 Expr::Value(Literal::<$to>::into_value(self))
152 }
153 }
154 };
155}
156
157literal!([T: DbType] T => T, |v| v.to_value());
158literal!([T: DbType] T => Option<T>, |v| v.to_value());
159literal!(['a] &'a str => String, |v| Value::Text(v.to_owned()));
160literal!(['a] &'a str => Option<String>, |v| Value::Text(v.to_owned()));
161literal!(['a, T: Model] &'a T => ForeignKey<T>, |v| v.pk().to_value());
162literal!(['a, T: Model] &'a T => Option<ForeignKey<T>>, |v| v.pk().to_value());
163
164macro_rules! key_literal {
166 ($($key:ty),*) => {$(
167 literal!([T: Model<Pk = $key>] $key => ForeignKey<T>, |v| v.to_value());
168 literal!([T: Model<Pk = $key>] $key => Option<ForeignKey<T>>, |v| v.to_value());
169 )*};
170}
171key_literal!(i16, i32, i64, Uuid, String);
172literal!(['a, T: Model<Pk = String>] &'a str => ForeignKey<T>, |v| Value::Text(v.to_owned()));
173literal!(['a, T: Model<Pk = String>] &'a str => Option<ForeignKey<T>>, |v| Value::Text(v.to_owned()));
174
175pub trait Numeric {}
177pub trait DateLike {}
179pub trait TimeLike {}
181pub trait TextLike {}
183
184macro_rules! marker {
185 ($trait:ident: $($t:ty),*) => {$(
186 impl $trait for $t {}
187 impl $trait for Option<$t> {}
188 )*};
189}
190marker!(Numeric: i16, i32, i64, f32, f64, Decimal);
191marker!(DateLike: NaiveDate, DateTime<Utc>);
192marker!(TimeLike: NaiveTime, DateTime<Utc>);
193marker!(TextLike: String);
194
195macro_rules! typed_lookups {
197 ($handle:ident) => {
198 impl<M, T> Operand<M, T> for $handle<M, T> {
199 fn into_expr(self) -> Expr {
200 self.into()
201 }
202 }
203
204 impl<M, T> $handle<M, T> {
205 pub fn expr(self) -> Expr {
207 self.into()
208 }
209
210 pub fn asc(self) -> OrderExpr {
212 self.expr().asc()
213 }
214
215 pub fn desc(self) -> OrderExpr {
217 self.expr().desc()
218 }
219
220 pub fn is_null(self) -> Expr {
222 self.expr().is_null(true)
223 }
224
225 pub fn is_not_null(self) -> Expr {
227 self.expr().is_null(false)
228 }
229
230 pub fn is_in<L: Literal<T>>(self, values: impl IntoIterator<Item = L>) -> Expr {
232 self.expr().is_in(values.into_iter().map(Literal::into_value))
233 }
234
235 pub fn in_subquery(self, plan: QueryPlan) -> Expr {
237 self.expr().in_subquery(plan)
238 }
239
240 pub fn range(self, lo: impl Literal<T>, hi: impl Literal<T>) -> Expr {
242 self.expr().range(lo.into_value(), hi.into_value())
243 }
244
245 pub fn cast(self, ty: SqlType) -> Expr {
247 self.expr().cast(ty)
248 }
249 }
250
251 impl<M, T: TextLike> $handle<M, T> {
252 pub fn iexact(self, v: impl Into<String>) -> Expr {
254 self.expr().iexact(v.into())
255 }
256
257 pub fn regex(self, pattern: impl Into<String>) -> Expr {
259 self.expr().regex(pattern)
260 }
261 }
262
263 impl<M, T: DateLike> $handle<M, T> {
264 pub fn year(self) -> Expr {
266 self.expr().year()
267 }
268 pub fn month(self) -> Expr {
270 self.expr().month()
271 }
272 pub fn day(self) -> Expr {
274 self.expr().day()
275 }
276 pub fn week(self) -> Expr {
278 self.expr().week()
279 }
280 pub fn quarter(self) -> Expr {
282 self.expr().quarter()
283 }
284 pub fn date(self) -> Expr {
286 self.expr().date()
287 }
288 }
289
290 impl<M, T: TimeLike> $handle<M, T> {
291 pub fn hour(self) -> Expr {
293 self.expr().hour()
294 }
295 pub fn minute(self) -> Expr {
297 self.expr().minute()
298 }
299 pub fn second(self) -> Expr {
301 self.expr().second()
302 }
303 }
304
305 impl<M, T: Numeric> std::ops::Neg for $handle<M, T> {
306 type Output = Calc<M, T>;
307 fn neg(self) -> Calc<M, T> {
308 Calc::new(-self.expr())
309 }
310 }
311
312 typed_lookups!(@arith $handle: Add add, Sub sub, Mul mul, Div div, Rem rem);
313 typed_lookups!(@compare $handle: eq, ne, lt, le, gt, ge);
314 typed_lookups!(@text $handle:
315 contains => Contains, false;
316 icontains => Contains, true;
317 starts_with => StartsWith, false;
318 istarts_with => StartsWith, true;
319 ends_with => EndsWith, false;
320 iends_with => EndsWith, true);
321 };
322 (@arith $handle:ident: $($trait:ident $method:ident),*) => {$(
323 impl<M, T: Numeric, R: Operand<M, T>> std::ops::$trait<R> for $handle<M, T> {
324 type Output = Calc<M, T>;
325 fn $method(self, rhs: R) -> Calc<M, T> {
326 Calc::new(std::ops::$trait::$method(self.expr(), rhs.into_expr()))
327 }
328 }
329 )*};
330 (@compare $handle:ident: $($method:ident),*) => {
331 impl<M, T> $handle<M, T> {$(
332 #[doc = concat!("Typed `", stringify!($method), "` comparison.")]
333 pub fn $method(self, rhs: impl Operand<M, T>) -> Expr {
334 self.expr().$method(rhs.into_expr())
335 }
336 )*}
337 };
338 (@text $handle:ident: $($method:ident => $variant:ident, $ci:expr);*) => {
339 impl<M, T: TextLike> $handle<M, T> {$(
340 #[doc = concat!("Text lookup `", stringify!($method), "`.")]
341 pub fn $method(self, needle: impl Into<String>) -> Expr {
342 Expr::Lookup {
343 expr: Box::new(self.expr()),
344 lookup: super::Lookup::$variant {
345 needle: needle.into(),
346 case_insensitive: $ci,
347 },
348 }
349 }
350 )*}
351 };
352}
353
354typed_lookups!(Field);
355typed_lookups!(Calc);
356typed_lookups!(Joined);