rust_query/
db.rs

1use std::{fmt::Debug, marker::PhantomData, ops::Deref};
2
3use ref_cast::RefCast;
4use sea_query::{Alias, Expr, SimpleExpr};
5
6use crate::{
7    alias::{Field, MyAlias},
8    value::{MyTyp, Typed, ValueBuilder},
9    IntoColumn, LocalClient, Table,
10};
11
12pub struct Col<T, X> {
13    pub(crate) _p: PhantomData<T>,
14    pub(crate) field: Field,
15    pub(crate) inner: X,
16}
17
18impl<T, X: Clone> Clone for Col<T, X> {
19    fn clone(&self) -> Self {
20        Self {
21            _p: self._p,
22            field: self.field,
23            inner: self.inner.clone(),
24        }
25    }
26}
27
28impl<T, X: Copy> Copy for Col<T, X> {}
29
30impl<T, X> Col<T, X> {
31    pub fn new(key: &'static str, x: X) -> Self {
32        Self {
33            _p: PhantomData,
34            field: Field::Str(key),
35            inner: x,
36        }
37    }
38}
39
40impl<T: Table, X: Clone> Deref for Col<T, X> {
41    type Target = T::Ext<Self>;
42
43    fn deref(&self) -> &Self::Target {
44        RefCast::ref_cast(self)
45    }
46}
47
48impl<T: MyTyp, P: Typed<Typ: Table>> Typed for Col<T, P> {
49    type Typ = T;
50    fn build_expr(&self, b: ValueBuilder) -> SimpleExpr {
51        Expr::col((self.inner.build_table(b), self.field)).into()
52    }
53}
54impl<'t, S, T: MyTyp, P: IntoColumn<'t, S, Typ: Table>> IntoColumn<'t, S> for Col<T, P> {
55    type Owned = Col<T, P::Owned>;
56
57    fn into_owned(self) -> Self::Owned {
58        Col {
59            _p: PhantomData,
60            field: self.field,
61            inner: self.inner.into_owned(),
62        }
63    }
64}
65
66/// Table reference that is the result of a join.
67/// It can only be used in the query where it was created.
68/// Invariant in `'t`.
69pub(crate) struct Join<'t, T> {
70    pub(crate) table: MyAlias,
71    pub(crate) _p: PhantomData<fn(&'t T) -> &'t T>,
72}
73
74impl<'t, T> Join<'t, T> {
75    pub(crate) fn new(table: MyAlias) -> Self {
76        Self {
77            table,
78            _p: PhantomData,
79        }
80    }
81}
82
83impl<'t, T> Clone for Join<'t, T> {
84    fn clone(&self) -> Self {
85        *self
86    }
87}
88
89impl<'t, T> Copy for Join<'t, T> {}
90
91impl<'t, T: Table> Deref for Join<'t, T> {
92    type Target = T::Ext<Self>;
93
94    fn deref(&self) -> &Self::Target {
95        RefCast::ref_cast(self)
96    }
97}
98
99impl<T: Table> Typed for Join<'_, T> {
100    type Typ = T;
101    fn build_expr(&self, b: ValueBuilder) -> SimpleExpr {
102        Expr::col((self.build_table(b), Alias::new(T::ID))).into()
103    }
104    fn build_table(&self, _: ValueBuilder) -> MyAlias {
105        self.table
106    }
107}
108impl<'t, T: Table> IntoColumn<'t, T::Schema> for Join<'t, T> {
109    type Owned = Self;
110
111    fn into_owned(self) -> Self::Owned {
112        self
113    }
114}
115
116/// Row reference that can be used in any query in the same transaction.
117///
118/// [TableRow] is covariant in `'t` and restricted to a single thread to prevent it from being used in a different transaction.
119pub struct TableRow<'t, T> {
120    pub(crate) _p: PhantomData<&'t T>,
121    pub(crate) _local: PhantomData<LocalClient>,
122    pub(crate) idx: i64,
123}
124
125impl<'t, T> PartialEq for TableRow<'t, T> {
126    fn eq(&self, other: &Self) -> bool {
127        self.idx == other.idx
128    }
129}
130
131impl<T> Debug for TableRow<'_, T> {
132    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
133        write!(f, "db_{}", self.idx)
134    }
135}
136
137impl<T> Clone for TableRow<'_, T> {
138    fn clone(&self) -> Self {
139        *self
140    }
141}
142impl<T> Copy for TableRow<'_, T> {}
143
144impl<T: Table> Deref for TableRow<'_, T> {
145    type Target = T::Ext<Self>;
146
147    fn deref(&self) -> &Self::Target {
148        RefCast::ref_cast(self)
149    }
150}
151
152impl<'t, T> From<TableRow<'t, T>> for sea_query::Value {
153    fn from(value: TableRow<T>) -> Self {
154        value.idx.into()
155    }
156}
157
158impl<T: Table> Typed for TableRow<'_, T> {
159    type Typ = T;
160    fn build_expr(&self, _: ValueBuilder) -> SimpleExpr {
161        Expr::val(self.idx).into()
162    }
163}
164
165impl<'t, T: Table> IntoColumn<'t, T::Schema> for TableRow<'t, T> {
166    type Owned = TableRow<'t, T>;
167
168    fn into_owned(self) -> Self::Owned {
169        self
170    }
171}
172
173/// This makes it possible to use TableRow as a parameter in
174/// rusqlite queries and statements.
175#[cfg(feature = "unchecked_transaction")]
176impl<T> rusqlite::ToSql for TableRow<'_, T> {
177    fn to_sql(&self) -> rusqlite::Result<rusqlite::types::ToSqlOutput<'_>> {
178        self.idx.to_sql()
179    }
180}
181
182#[cfg(test)]
183#[allow(unused)]
184mod tests {
185    use super::*;
186    struct Admin;
187
188    impl Table for Admin {
189        type Ext<T> = AdminDummy<T>;
190
191        type Schema = ();
192        type Referer = ();
193        fn get_referer_unchecked() -> Self::Referer {}
194
195        fn typs(_: &mut crate::hash::TypBuilder<Self::Schema>) {}
196
197        type Dummy<'t> = ();
198        fn dummy<'t>(_: impl IntoColumn<'t, Self::Schema, Typ = Self>) -> Self::Dummy<'t> {}
199    }
200
201    #[repr(transparent)]
202    #[derive(RefCast)]
203    struct AdminDummy<X>(X);
204
205    impl<X: Clone> AdminDummy<X> {
206        fn a(&self) -> Col<Admin, X> {
207            Col::new("a", self.0.clone())
208        }
209        fn b(&self) -> Col<Admin, X> {
210            Col::new("b", self.0.clone())
211        }
212    }
213
214    fn test(x: Join<Admin>) {
215        let _res = &x.a().b().a().a();
216    }
217}