rust_query/
db.rs

1use std::{fmt::Debug, marker::PhantomData, ops::Deref};
2
3use ref_cast::RefCast;
4use sea_query::{Alias, SimpleExpr};
5
6use crate::{
7    Expr, IntoExpr, LocalClient, Table,
8    alias::{Field, MyAlias},
9    value::{MyTyp, Typed, ValueBuilder},
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: MyTyp, P: Typed<Typ: Table>> Typed for Col<T, P> {
41    type Typ = T;
42    fn build_expr(&self, b: ValueBuilder) -> SimpleExpr {
43        sea_query::Expr::col((self.inner.build_table(b), self.field)).into()
44    }
45}
46
47/// Table reference that is the result of a join.
48/// It can only be used in the query where it was created.
49/// Invariant in `'t`.
50pub(crate) struct Join<T> {
51    pub(crate) table: MyAlias,
52    pub(crate) _p: PhantomData<T>,
53}
54
55impl<T> Join<T> {
56    pub(crate) fn new(table: MyAlias) -> Self {
57        Self {
58            table,
59            _p: PhantomData,
60        }
61    }
62}
63
64impl<T> Clone for Join<T> {
65    fn clone(&self) -> Self {
66        *self
67    }
68}
69
70impl<T> Copy for Join<T> {}
71
72impl<T: Table> Typed for Join<T> {
73    type Typ = T;
74    fn build_expr(&self, b: ValueBuilder) -> SimpleExpr {
75        sea_query::Expr::col((self.build_table(b), Alias::new(T::ID))).into()
76    }
77    fn build_table(&self, _: ValueBuilder) -> MyAlias {
78        self.table
79    }
80}
81
82/// Row reference that can be used in any query in the same transaction.
83///
84/// [TableRow] is covariant in `'t` and restricted to a single thread to prevent it from being used in a different transaction.
85///
86/// Note that the [TableRow] can typically only be used at the top level of each query (not inside aggregates).
87/// `rustc` sometimes suggested making the transaction lifetime `'static` to get around this issue.
88/// While it is a valid and correct suggestion, you probably don't want a `'static` transaction.
89///
90/// The appropriate solution is to use [crate::args::Aggregate::filter_on] to bring [TableRow]
91/// columns into the [crate::aggregate] inner scope.
92pub struct TableRow<'t, T> {
93    pub(crate) _p: PhantomData<&'t ()>,
94    pub(crate) _local: PhantomData<LocalClient>,
95    pub(crate) inner: TableRowInner<T>,
96}
97impl<'t, T> TableRow<'t, T> {
98    pub(crate) fn new(idx: i64) -> Self {
99        Self {
100            _p: PhantomData,
101            _local: PhantomData,
102            inner: TableRowInner {
103                _p: PhantomData,
104                idx,
105            },
106        }
107    }
108}
109
110impl<'t, T> Eq for TableRow<'t, T> {}
111
112impl<'t, T> PartialOrd for TableRow<'t, T> {
113    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
114        self.inner.idx.partial_cmp(&other.inner.idx)
115    }
116}
117
118impl<'t, T> Ord for TableRow<'t, T> {
119    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
120        self.inner.idx.cmp(&other.inner.idx)
121    }
122}
123
124pub(crate) struct TableRowInner<T> {
125    pub(crate) _p: PhantomData<T>,
126    pub(crate) idx: i64,
127}
128
129impl<'t, T> PartialEq for TableRow<'t, T> {
130    fn eq(&self, other: &Self) -> bool {
131        self.inner.idx == other.inner.idx
132    }
133}
134
135impl<T> Debug for TableRow<'_, T> {
136    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
137        write!(f, "db_{}", self.inner.idx)
138    }
139}
140
141impl<T> Clone for TableRow<'_, T> {
142    fn clone(&self) -> Self {
143        *self
144    }
145}
146impl<T> Copy for TableRow<'_, T> {}
147
148impl<T> Clone for TableRowInner<T> {
149    fn clone(&self) -> Self {
150        *self
151    }
152}
153impl<T> Copy for TableRowInner<T> {}
154
155impl<T: Table> Deref for TableRow<'_, T> {
156    type Target = T::Ext<Self>;
157
158    fn deref(&self) -> &Self::Target {
159        RefCast::ref_cast(self)
160    }
161}
162
163impl<'t, T> From<TableRow<'t, T>> for sea_query::Value {
164    fn from(value: TableRow<T>) -> Self {
165        value.inner.idx.into()
166    }
167}
168
169impl<T: Table> Typed for TableRowInner<T> {
170    type Typ = T;
171    fn build_expr(&self, _: ValueBuilder) -> SimpleExpr {
172        sea_query::Expr::val(self.idx).into()
173    }
174}
175
176impl<'t, S, T: Table> IntoExpr<'t, S> for TableRow<'t, T> {
177    type Typ = T;
178    fn into_expr(self) -> Expr<'t, S, Self::Typ> {
179        Expr::new(self.inner)
180    }
181}
182
183/// This makes it possible to use TableRow as a parameter in
184/// rusqlite queries and statements.
185impl<T> rusqlite::ToSql for TableRow<'_, T> {
186    fn to_sql(&self) -> rusqlite::Result<rusqlite::types::ToSqlOutput<'_>> {
187        self.inner.idx.to_sql()
188    }
189}
190
191#[cfg(test)]
192#[allow(unused)]
193mod tests {
194    use std::convert::Infallible;
195
196    use crate::{IntoSelectExt, Select, private::Reader};
197
198    use super::*;
199    struct Admin;
200
201    impl Table for Admin {
202        type MigrateFrom = Self;
203        type Ext<T> = AdminSelect<T>;
204
205        type Schema = ();
206        type Referer = ();
207        fn get_referer_unchecked() -> Self::Referer {}
208
209        fn typs(_: &mut crate::hash::TypBuilder<Self::Schema>) {}
210
211        type Conflict<'t> = Infallible;
212        type UpdateOk<'t> = ();
213        type Update<'t> = ();
214        type Insert<'t> = ();
215
216        fn read<'t>(val: &Self::Insert<'t>, f: &Reader<'t, Self::Schema>) {
217            todo!()
218        }
219
220        fn get_conflict_unchecked<'t>(val: &Self::Insert<'t>) -> Self::Conflict<'t> {
221            todo!()
222        }
223
224        fn update_into_try_update<'t>(val: Self::UpdateOk<'t>) -> Self::Update<'t> {
225            todo!()
226        }
227
228        fn apply_try_update<'t>(
229            val: Self::Update<'t>,
230            old: Expr<'t, Self::Schema, Self>,
231        ) -> Self::Insert<'t> {
232            todo!()
233        }
234
235        const ID: &'static str = "";
236        const NAME: &'static str = "";
237    }
238
239    #[repr(transparent)]
240    #[derive(RefCast)]
241    struct AdminSelect<X>(X);
242
243    impl<X: Clone> AdminSelect<X> {
244        fn a(&self) -> Col<Admin, X> {
245            Col::new("a", self.0.clone())
246        }
247        fn b(&self) -> Col<Admin, X> {
248            Col::new("b", self.0.clone())
249        }
250    }
251}