1pub mod operations;
2
3use std::{marker::PhantomData, ops::Deref, rc::Rc};
4
5use operations::{Add, And, AsFloat, Eq, Glob, IsNotNull, Like, Lt, Not, Or, UnwrapOr};
6use ref_cast::RefCast;
7use sea_query::{Alias, Expr, Nullable, SelectStatement, SimpleExpr};
8
9use crate::{
10 alias::{Field, MyAlias, RawAlias},
11 ast::{MySelect, Source},
12 db::TableRow,
13 hash,
14 migrate::NoTable,
15 Table,
16};
17
18#[derive(Clone, Copy)]
19pub struct ValueBuilder<'x> {
20 pub(crate) inner: &'x MySelect,
21}
22
23impl<'x> ValueBuilder<'x> {
24 pub(crate) fn get_aggr(
25 self,
26 aggr: SelectStatement,
27 conds: Vec<(Field, SimpleExpr)>,
28 ) -> MyAlias {
29 let source = Source {
30 kind: crate::ast::SourceKind::Aggregate(aggr),
31 conds,
32 };
33 let new_alias = || self.inner.scope.new_alias();
34 *self.inner.extra.get_or_init(source, new_alias)
35 }
36
37 pub(crate) fn get_join<T: Table>(self, expr: SimpleExpr) -> MyAlias {
38 let source = Source {
39 kind: crate::ast::SourceKind::Implicit(T::NAME.to_owned()),
40 conds: vec![(Field::Str(T::ID), expr)],
41 };
42 let new_alias = || self.inner.scope.new_alias();
43 *self.inner.extra.get_or_init(source, new_alias)
44 }
45
46 pub fn get_unique(
47 self,
48 table: &'static str,
49 conds: Vec<(&'static str, SimpleExpr)>,
50 ) -> SimpleExpr {
51 let source = Source {
52 kind: crate::ast::SourceKind::Implicit(table.to_owned()),
53 conds: conds.into_iter().map(|x| (Field::Str(x.0), x.1)).collect(),
54 };
55
56 let new_alias = || self.inner.scope.new_alias();
57 let table = self.inner.extra.get_or_init(source, new_alias);
58 Expr::col((*table, Alias::new("id"))).into()
59 }
60}
61
62pub trait NumTyp: MyTyp + Clone + Copy {
63 const ZERO: Self;
64 fn into_sea_value(self) -> sea_query::Value;
65}
66
67impl NumTyp for i64 {
68 const ZERO: Self = 0;
69 fn into_sea_value(self) -> sea_query::Value {
70 sea_query::Value::BigInt(Some(self))
71 }
72}
73impl NumTyp for f64 {
74 const ZERO: Self = 0.;
75 fn into_sea_value(self) -> sea_query::Value {
76 sea_query::Value::Double(Some(self))
77 }
78}
79
80pub trait EqTyp {}
81
82impl EqTyp for String {}
83impl EqTyp for i64 {}
84impl EqTyp for f64 {}
85impl EqTyp for bool {}
86impl<T: Table> EqTyp for T {}
87
88pub trait Typed {
90 type Typ;
92
93 #[doc(hidden)]
94 fn build_expr(&self, b: ValueBuilder) -> SimpleExpr;
95 #[doc(hidden)]
96 fn build_table(&self, b: crate::value::ValueBuilder) -> MyAlias
97 where
98 Self::Typ: Table,
99 {
100 b.get_join::<Self::Typ>(self.build_expr(b))
101 }
102}
103
104pub trait IntoColumn<'t, S>: Typed + Clone {
112 #[doc(hidden)]
113 type Owned: Typed<Typ = Self::Typ> + 't;
114
115 #[doc(hidden)]
116 fn into_owned(self) -> Self::Owned;
117
118 fn into_column(self) -> Column<'t, S, Self::Typ> {
120 Column(Rc::new(self.into_owned()), PhantomData)
121 }
122}
123
124impl<'t, S, T: NumTyp> Column<'t, S, T> {
125 pub fn add(&self, rhs: impl IntoColumn<'t, S, Typ = T>) -> Column<'t, S, T> {
127 Add(self, rhs).into_column()
128 }
129
130 pub fn lt(&self, rhs: impl IntoColumn<'t, S, Typ = T>) -> Column<'t, S, bool> {
132 Lt(self, rhs).into_column()
133 }
134}
135
136impl<'t, S, T: EqTyp + 't> Column<'t, S, T> {
137 pub fn eq(&self, rhs: impl IntoColumn<'t, S, Typ = T>) -> Column<'t, S, bool> {
139 Eq(self, rhs).into_column()
140 }
141}
142
143impl<'t, S> Column<'t, S, bool> {
144 pub fn not(&self) -> Column<'t, S, bool> {
146 Not(self).into_column()
147 }
148
149 pub fn and(&self, rhs: impl IntoColumn<'t, S, Typ = bool>) -> Column<'t, S, bool> {
151 And(self, rhs).into_column()
152 }
153
154 pub fn or(&self, rhs: impl IntoColumn<'t, S, Typ = bool>) -> Column<'t, S, bool> {
156 Or(self, rhs).into_column()
157 }
158}
159
160impl<'t, S, Typ: 't> Column<'t, S, Option<Typ>> {
161 pub fn unwrap_or(&self, rhs: impl IntoColumn<'t, S, Typ = Typ>) -> Column<'t, S, Typ>
163 where
164 Self: IntoColumn<'t, S, Typ = Option<Typ>>,
165 {
166 UnwrapOr(self, rhs).into_column()
167 }
168
169 pub fn is_some(&self) -> Column<'t, S, bool> {
171 IsNotNull(self).into_column()
172 }
173}
174
175impl<'t, S> Column<'t, S, i64> {
176 pub fn as_float(&self) -> Column<'t, S, f64> {
178 AsFloat(self).into_column()
179 }
180}
181
182impl<'t, S> Column<'t, S, String> {
183 pub fn starts_with(&self, pattern: impl AsRef<str>) -> Column<'t, S, bool> {
187 Glob(self, format!("{}*", escape_glob(pattern))).into_column()
188 }
189
190 pub fn ends_with(&self, pattern: impl AsRef<str>) -> Column<'t, S, bool> {
194 Glob(self, format!("*{}", escape_glob(pattern))).into_column()
195 }
196
197 pub fn contains(&self, pattern: impl AsRef<str>) -> Column<'t, S, bool> {
201 Glob(self, format!("*{}*", escape_glob(pattern))).into_column()
202 }
203
204 pub fn like(&self, pattern: impl Into<String> + Clone + 't) -> Column<'t, S, bool> {
210 Like(self, pattern.into()).into_column()
211 }
212
213 pub fn glob(&self, rhs: impl IntoColumn<'t, S, Typ = String>) -> Column<'t, S, bool> {
219 Glob(self, rhs).into_column()
220 }
221}
222
223impl<T: Typed<Typ = X>, X: MyTyp<Sql: Nullable>> Typed for Option<T> {
224 type Typ = Option<T::Typ>;
225
226 fn build_expr(&self, b: ValueBuilder) -> SimpleExpr {
227 self.as_ref()
228 .map(|x| T::build_expr(x, b))
229 .unwrap_or(X::Sql::null().into())
230 }
231}
232
233impl<'t, S, T: IntoColumn<'t, S, Typ = X>, X: MyTyp<Sql: Nullable>> IntoColumn<'t, S>
234 for Option<T>
235{
236 type Owned = Option<T::Owned>;
237 fn into_owned(self) -> Self::Owned {
238 self.map(IntoColumn::into_owned)
239 }
240}
241
242impl Typed for &str {
243 type Typ = String;
244 fn build_expr(&self, _: ValueBuilder) -> SimpleExpr {
245 SimpleExpr::from(*self)
246 }
247}
248
249impl<'t, S> IntoColumn<'t, S> for &str {
250 type Owned = String;
251 fn into_owned(self) -> Self::Owned {
252 self.to_owned()
253 }
254}
255
256impl Typed for String {
257 type Typ = String;
258 fn build_expr(&self, _: ValueBuilder) -> SimpleExpr {
259 SimpleExpr::from(self)
260 }
261}
262
263impl<'t, S> IntoColumn<'t, S> for String {
264 type Owned = String;
265 fn into_owned(self) -> Self::Owned {
266 self
267 }
268}
269
270impl Typed for bool {
271 type Typ = bool;
272 fn build_expr(&self, _: ValueBuilder) -> SimpleExpr {
273 SimpleExpr::from(*self)
274 }
275}
276
277impl<'t, S> IntoColumn<'t, S> for bool {
278 type Owned = Self;
279 fn into_owned(self) -> Self::Owned {
280 self
281 }
282}
283
284impl Typed for i64 {
285 type Typ = i64;
286 fn build_expr(&self, _: ValueBuilder) -> SimpleExpr {
287 SimpleExpr::from(*self)
288 }
289}
290
291impl<'t, S> IntoColumn<'t, S> for i64 {
292 type Owned = Self;
293 fn into_owned(self) -> Self::Owned {
294 self
295 }
296}
297
298impl Typed for f64 {
299 type Typ = f64;
300 fn build_expr(&self, _: ValueBuilder) -> SimpleExpr {
301 SimpleExpr::from(*self)
302 }
303}
304
305impl<'t, S> IntoColumn<'t, S> for f64 {
306 type Owned = Self;
307 fn into_owned(self) -> Self::Owned {
308 self
309 }
310}
311
312impl<T> Typed for &T
313where
314 T: Typed,
315{
316 type Typ = T::Typ;
317 fn build_expr(&self, b: ValueBuilder) -> SimpleExpr {
318 T::build_expr(self, b)
319 }
320 fn build_table(&self, b: crate::value::ValueBuilder) -> MyAlias
321 where
322 Self::Typ: Table,
323 {
324 T::build_table(self, b)
325 }
326}
327
328impl<'t, S, T> IntoColumn<'t, S> for &T
329where
330 T: IntoColumn<'t, S>,
331{
332 type Owned = T::Owned;
333 fn into_owned(self) -> Self::Owned {
334 T::into_owned(self.clone())
335 }
336}
337
338#[derive(Clone, Copy)]
340pub struct UnixEpoch;
341
342impl Typed for UnixEpoch {
343 type Typ = i64;
344 fn build_expr(&self, _: ValueBuilder) -> SimpleExpr {
345 Expr::col(RawAlias("unixepoch('now')".to_owned())).into()
346 }
347}
348
349impl<'t, S> IntoColumn<'t, S> for UnixEpoch {
350 type Owned = Self;
351 fn into_owned(self) -> Self::Owned {
352 self
353 }
354}
355
356pub trait MyTyp: 'static {
357 #[doc(hidden)]
358 const NULLABLE: bool = false;
359 #[doc(hidden)]
360 const TYP: hash::ColumnType;
361 #[doc(hidden)]
362 const FK: Option<(&'static str, &'static str)> = None;
363 #[doc(hidden)]
364 type Out<'t>;
365 #[doc(hidden)]
366 type Sql;
367 #[doc(hidden)]
368 fn from_sql<'a>(
369 value: rusqlite::types::ValueRef<'_>,
370 ) -> rusqlite::types::FromSqlResult<Self::Out<'a>>;
371}
372
373impl<T: Table> MyTyp for T {
374 const TYP: hash::ColumnType = hash::ColumnType::Integer;
375 const FK: Option<(&'static str, &'static str)> = Some((T::NAME, T::ID));
376 type Out<'t> = TableRow<'t, Self>;
377 type Sql = i64;
378
379 fn from_sql<'a>(
380 value: rusqlite::types::ValueRef<'_>,
381 ) -> rusqlite::types::FromSqlResult<Self::Out<'a>> {
382 Ok(TableRow {
383 _p: PhantomData,
384 _local: PhantomData,
385 idx: value.as_i64()?,
386 })
387 }
388}
389
390impl MyTyp for i64 {
391 const TYP: hash::ColumnType = hash::ColumnType::Integer;
392 type Out<'t> = Self;
393 type Sql = i64;
394
395 fn from_sql<'a>(
396 value: rusqlite::types::ValueRef<'_>,
397 ) -> rusqlite::types::FromSqlResult<Self::Out<'a>> {
398 value.as_i64()
399 }
400}
401
402impl MyTyp for f64 {
403 const TYP: hash::ColumnType = hash::ColumnType::Float;
404 type Out<'t> = Self;
405 type Sql = f64;
406
407 fn from_sql<'a>(
408 value: rusqlite::types::ValueRef<'_>,
409 ) -> rusqlite::types::FromSqlResult<Self::Out<'a>> {
410 value.as_f64()
411 }
412}
413
414impl MyTyp for bool {
415 const TYP: hash::ColumnType = hash::ColumnType::Integer;
416 type Out<'t> = Self;
417 type Sql = bool;
418
419 fn from_sql<'a>(
420 value: rusqlite::types::ValueRef<'_>,
421 ) -> rusqlite::types::FromSqlResult<Self::Out<'a>> {
422 Ok(value.as_i64()? != 0)
423 }
424}
425
426impl MyTyp for String {
427 const TYP: hash::ColumnType = hash::ColumnType::String;
428 type Out<'t> = Self;
429 type Sql = String;
430
431 fn from_sql<'a>(
432 value: rusqlite::types::ValueRef<'_>,
433 ) -> rusqlite::types::FromSqlResult<Self::Out<'a>> {
434 Ok(value.as_str()?.to_owned())
435 }
436}
437
438impl<T: MyTyp> MyTyp for Option<T> {
439 const TYP: hash::ColumnType = T::TYP;
440 const NULLABLE: bool = true;
441 const FK: Option<(&'static str, &'static str)> = T::FK;
442 type Out<'t> = Option<T::Out<'t>>;
443 type Sql = T::Sql;
444
445 fn from_sql<'a>(
446 value: rusqlite::types::ValueRef<'_>,
447 ) -> rusqlite::types::FromSqlResult<Self::Out<'a>> {
448 if value.data_type() == rusqlite::types::Type::Null {
449 Ok(None)
450 } else {
451 Ok(Some(T::from_sql(value)?))
452 }
453 }
454}
455
456impl MyTyp for NoTable {
457 const TYP: hash::ColumnType = hash::ColumnType::Integer;
458 type Out<'t> = NoTable;
459 type Sql = i64;
460
461 fn from_sql<'a>(
462 _value: rusqlite::types::ValueRef<'_>,
463 ) -> rusqlite::types::FromSqlResult<Self::Out<'a>> {
464 unreachable!()
465 }
466}
467
468pub struct Column<'t, S, T>(
476 pub(crate) Rc<dyn Typed<Typ = T> + 't>,
477 pub(crate) PhantomData<fn(&'t S) -> &'t S>,
478);
479
480impl<'t, S, T> Clone for Column<'t, S, T> {
481 fn clone(&self) -> Self {
482 Self(self.0.clone(), PhantomData)
483 }
484}
485
486impl<'t, S, T> Typed for Column<'t, S, T> {
487 type Typ = T;
488
489 fn build_expr(&self, b: ValueBuilder) -> SimpleExpr {
490 self.0.as_ref().build_expr(b)
491 }
492
493 fn build_table(&self, b: crate::value::ValueBuilder) -> MyAlias
494 where
495 Self::Typ: Table,
496 {
497 self.0.as_ref().build_table(b)
498 }
499}
500
501impl<'t, S: 't, T: 't> IntoColumn<'t, S> for Column<'t, S, T> {
502 type Owned = Self;
503 fn into_owned(self) -> Self::Owned {
504 self
505 }
506}
507
508impl<'t, S, T: Table> Deref for Column<'t, S, T> {
509 type Target = T::Ext<Self>;
510
511 fn deref(&self) -> &Self::Target {
512 RefCast::ref_cast(self)
513 }
514}
515
516fn escape_glob(s: impl AsRef<str>) -> String {
518 let mut escaped = String::new();
519 for c in s.as_ref().chars() {
520 match c {
521 '?' | '*' | '[' | ']' => {
524 escaped.push('[');
525 escaped.push(c);
526 escaped.push(']');
527 }
528 c => {
529 escaped.push(c);
530 }
531 }
532 }
533 escaped
534}