rust_query/
dummy_impl.rs

1use std::marker::PhantomData;
2
3use sea_query::Iden;
4
5use crate::{
6    Expr,
7    alias::Field,
8    value::{DynTyped, DynTypedExpr, MyTyp, SecretFromSql},
9};
10
11/// Opaque type used to implement [crate::Select].
12pub(crate) struct Cacher {
13    pub(crate) columns: Vec<DynTypedExpr>,
14}
15
16impl Cacher {
17    pub(crate) fn new() -> Self {
18        Self {
19            columns: Vec::new(),
20        }
21    }
22}
23
24pub struct Cached<T> {
25    pub(crate) idx: usize,
26    _p: PhantomData<T>,
27}
28
29impl<T> Clone for Cached<T> {
30    fn clone(&self) -> Self {
31        *self
32    }
33}
34impl<T> Copy for Cached<T> {}
35
36impl Cacher {
37    pub(crate) fn cache_erased(&mut self, val: DynTypedExpr) -> usize {
38        let idx = self.columns.len();
39        self.columns.push(val);
40        idx
41    }
42}
43
44#[derive(Clone, Copy)]
45pub(crate) struct Row<'x> {
46    pub(crate) row: &'x rusqlite::Row<'x>,
47    pub(crate) fields: &'x [Field],
48}
49
50impl<'x> Row<'x> {
51    pub(crate) fn new(row: &'x rusqlite::Row<'x>, fields: &'x [Field]) -> Self {
52        Self { row, fields }
53    }
54
55    pub fn get<'transaction, T: SecretFromSql<'transaction>>(&self, val: Cached<T>) -> T {
56        let field = self.fields[val.idx];
57        let idx = &*field.to_string();
58        T::from_sql(self.row.get_ref_unwrap(idx)).unwrap()
59    }
60}
61
62pub(crate) trait Prepared {
63    type Out;
64
65    fn call(&mut self, row: Row<'_>) -> Self::Out;
66}
67
68/// [Select] is used to define what to query from the database for each row.
69///
70/// It defines a set of expressions to evaluate in the database, and then how to turn the results into rust values.
71///
72/// For this reason many [rust_query] APIs accept values that implement [IntoSelect].
73pub struct Select<'columns, 'transaction, S, Out> {
74    pub(crate) inner: DynSelectImpl<'transaction, Out>,
75    pub(crate) _p: PhantomData<&'columns ()>,
76    pub(crate) _p2: PhantomData<S>,
77}
78
79pub struct DynSelectImpl<'transaction, Out> {
80    inner: Box<dyn 'transaction + FnOnce(&mut Cacher) -> DynPrepared<'transaction, Out>>,
81}
82
83impl<'transaction, Out> SelectImpl<'transaction> for DynSelectImpl<'transaction, Out> {
84    type Out = Out;
85    type Prepared = DynPrepared<'transaction, Out>;
86
87    fn prepare(self, cacher: &mut Cacher) -> Self::Prepared {
88        (self.inner)(cacher)
89    }
90}
91
92pub struct DynPrepared<'transaction, Out> {
93    inner: Box<dyn 'transaction + Prepared<Out = Out>>,
94}
95
96impl<Out> Prepared for DynPrepared<'_, Out> {
97    type Out = Out;
98    fn call(&mut self, row: Row<'_>) -> Self::Out {
99        self.inner.call(row)
100    }
101}
102
103impl<'transaction, S, Out> Select<'_, 'transaction, S, Out> {
104    pub(crate) fn new(val: impl 'transaction + SelectImpl<'transaction, Out = Out>) -> Self {
105        Self {
106            inner: DynSelectImpl {
107                inner: Box::new(|cacher| DynPrepared {
108                    inner: Box::new(val.prepare(cacher)),
109                }),
110            },
111            _p: PhantomData,
112            _p2: PhantomData,
113        }
114    }
115}
116
117impl<'columns, 'transaction, S, Out: 'transaction> IntoSelect<'columns, 'transaction, S>
118    for Select<'columns, 'transaction, S, Out>
119{
120    type Out = Out;
121
122    fn into_select(self) -> Select<'columns, 'transaction, S, Self::Out> {
123        self
124    }
125}
126
127pub trait SelectImpl<'transaction> {
128    type Out;
129    #[doc(hidden)]
130    type Prepared: Prepared<Out = Self::Out>;
131    #[doc(hidden)]
132    fn prepare(self, cacher: &mut Cacher) -> Self::Prepared;
133}
134
135/// This trait is implemented by everything that can be retrieved from the database.
136///
137/// Instead of implementing it yourself you probably want to use the [derive@rust_query::Select] macro.
138pub trait IntoSelect<'columns, 'transaction, S>: Sized {
139    /// The type that results from executing the [Select].
140    type Out: 'transaction;
141
142    /// This method is what tells rust-query how to turn the value into a [Select].
143    ///
144    /// The only way to implement this method is by constructing a different value
145    /// that implements [IntoSelect] and then calling the [IntoSelect::into_select] method
146    /// on that other value.
147    fn into_select(self) -> Select<'columns, 'transaction, S, Self::Out>;
148}
149
150/// [IntoSelectExt] adds extra methods to values that implement [IntoSelect].
151pub trait IntoSelectExt<'columns, 'transaction, S>: IntoSelect<'columns, 'transaction, S> {
152    /// Map the result of a [Select] using native rust.
153    ///
154    /// This is useful when retrieving custom types from the database.
155    /// It is also useful in migrations to process rows using arbitrary rust.
156    fn map_select<T>(
157        self,
158        f: impl 'transaction + FnMut(Self::Out) -> T,
159    ) -> Select<'columns, 'transaction, S, T>;
160}
161
162impl<'columns, 'transaction, S, X> IntoSelectExt<'columns, 'transaction, S> for X
163where
164    X: IntoSelect<'columns, 'transaction, S>,
165{
166    fn map_select<T>(
167        self,
168        f: impl 'transaction + FnMut(Self::Out) -> T,
169    ) -> Select<'columns, 'transaction, S, T> {
170        Select::new(MapImpl {
171            dummy: self.into_select().inner,
172            func: f,
173        })
174    }
175}
176
177/// This is the result of the [Select::map_select] method.
178///
179/// [MapImpl] retrieves the same columns as the [Select] that it wraps,
180/// but then it processes those columns using a rust closure.
181pub struct MapImpl<D, F> {
182    dummy: D,
183    func: F,
184}
185
186impl<'transaction, D, F, O> SelectImpl<'transaction> for MapImpl<D, F>
187where
188    D: SelectImpl<'transaction>,
189    F: FnMut(D::Out) -> O,
190{
191    type Out = O;
192    type Prepared = MapPrepared<D::Prepared, F>;
193
194    fn prepare(self, cacher: &mut Cacher) -> Self::Prepared {
195        MapPrepared {
196            inner: self.dummy.prepare(cacher),
197            map: self.func,
198        }
199    }
200}
201
202pub struct MapPrepared<X, M> {
203    inner: X,
204    map: M,
205}
206
207impl<X, M, Out> Prepared for MapPrepared<X, M>
208where
209    X: Prepared,
210    M: FnMut(X::Out) -> Out,
211{
212    type Out = Out;
213
214    fn call(&mut self, row: Row<'_>) -> Self::Out {
215        (self.map)(self.inner.call(row))
216    }
217}
218
219impl Prepared for () {
220    type Out = ();
221
222    fn call(&mut self, _row: Row<'_>) -> Self::Out {}
223}
224
225impl SelectImpl<'_> for () {
226    type Out = ();
227    type Prepared = ();
228
229    fn prepare(self, _cacher: &mut Cacher) -> Self::Prepared {}
230}
231
232impl<'columns, 'transaction, S> IntoSelect<'columns, 'transaction, S> for () {
233    type Out = ();
234
235    fn into_select(self) -> Select<'columns, 'transaction, S, Self::Out> {
236        Select::new(())
237    }
238}
239
240impl<'transaction, T: SecretFromSql<'transaction>> Prepared for Cached<T> {
241    type Out = T;
242
243    fn call(&mut self, row: Row<'_>) -> Self::Out {
244        row.get(*self)
245    }
246}
247
248pub struct ColumnImpl<T> {
249    pub(crate) expr: DynTyped<T>,
250}
251
252impl<'transaction, T: MyTyp> SelectImpl<'transaction> for ColumnImpl<T> {
253    type Out = T::Out<'transaction>;
254    type Prepared = Cached<Self::Out>;
255
256    fn prepare(self, cacher: &mut Cacher) -> Self::Prepared {
257        Cached {
258            idx: cacher.cache_erased(self.expr.erase()),
259            _p: PhantomData,
260        }
261    }
262}
263
264impl<'columns, 'transaction, S, T> IntoSelect<'columns, 'transaction, S> for Expr<'columns, S, T>
265where
266    T: MyTyp,
267{
268    type Out = T::Out<'transaction>;
269
270    fn into_select(self) -> Select<'columns, 'transaction, S, Self::Out> {
271        Select::new(ColumnImpl { expr: self.inner })
272    }
273}
274
275impl<'columns, 'transaction, S, T> IntoSelect<'columns, 'transaction, S> for &T
276where
277    T: IntoSelect<'columns, 'transaction, S> + Clone,
278{
279    type Out = T::Out;
280
281    fn into_select(self) -> Select<'columns, 'transaction, S, Self::Out> {
282        T::clone(self).into_select()
283    }
284}
285
286impl<'transaction, A, B> Prepared for (A, B)
287where
288    A: Prepared,
289    B: Prepared,
290{
291    type Out = (A::Out, B::Out);
292
293    fn call(&mut self, row: Row<'_>) -> Self::Out {
294        (self.0.call(row), self.1.call(row))
295    }
296}
297
298impl<'transaction, A, B> SelectImpl<'transaction> for (A, B)
299where
300    A: SelectImpl<'transaction>,
301    B: SelectImpl<'transaction>,
302{
303    type Out = (A::Out, B::Out);
304    type Prepared = (A::Prepared, B::Prepared);
305
306    fn prepare(self, cacher: &mut Cacher) -> Self::Prepared {
307        let prepared_a = self.0.prepare(cacher);
308        let prepared_b = self.1.prepare(cacher);
309        (prepared_a, prepared_b)
310    }
311}
312
313impl<'columns, 'transaction, S, A, B> IntoSelect<'columns, 'transaction, S> for (A, B)
314where
315    A: IntoSelect<'columns, 'transaction, S>,
316    B: IntoSelect<'columns, 'transaction, S>,
317{
318    type Out = (A::Out, B::Out);
319
320    fn into_select(self) -> Select<'columns, 'transaction, S, Self::Out> {
321        Select::new((self.0.into_select().inner, self.1.into_select().inner))
322    }
323}
324
325#[cfg(test)]
326#[allow(unused)]
327mod tests {
328    use crate::IntoExpr;
329
330    use super::*;
331
332    struct User {
333        a: i64,
334        b: String,
335    }
336
337    struct UserSelect<A, B> {
338        a: A,
339        b: B,
340    }
341
342    impl<'columns, 'transaction, S, A, B> IntoSelect<'columns, 'transaction, S> for UserSelect<A, B>
343    where
344        A: IntoExpr<'columns, S, Typ = i64>,
345        B: IntoExpr<'columns, S, Typ = String>,
346    {
347        type Out = User;
348
349        fn into_select(self) -> Select<'columns, 'transaction, S, Self::Out> {
350            (self.a.into_expr(), self.b.into_expr())
351                .map_select((|(a, b)| User { a, b }) as fn((i64, String)) -> User)
352                .into_select()
353        }
354    }
355}