rust_query/
dummy_impl.rs

1use std::marker::PhantomData;
2
3use sea_query::Iden;
4
5use crate::{
6    Expr,
7    alias::MyAlias,
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 [MyAlias],
48}
49
50impl<'x> Row<'x> {
51    pub(crate) fn new(row: &'x rusqlite::Row<'x>, fields: &'x [MyAlias]) -> 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
79impl<'columns, 'transaction, S, Out: 'transaction> Select<'columns, 'transaction, S, Out> {
80    /// Map the result of a [Select] using native rust.
81    ///
82    /// This is useful when implementing [IntoSelect].
83    pub fn map<T>(
84        self,
85        f: impl 'transaction + FnMut(Out) -> T,
86    ) -> Select<'columns, 'transaction, S, T> {
87        Select::new(MapImpl {
88            dummy: self.inner,
89            func: f,
90        })
91    }
92}
93
94pub struct DynSelectImpl<'transaction, Out> {
95    inner: Box<dyn 'transaction + FnOnce(&mut Cacher) -> DynPrepared<'transaction, Out>>,
96}
97
98impl<'transaction, Out> SelectImpl<'transaction> for DynSelectImpl<'transaction, Out> {
99    type Out = Out;
100    type Prepared = DynPrepared<'transaction, Out>;
101
102    fn prepare(self, cacher: &mut Cacher) -> Self::Prepared {
103        (self.inner)(cacher)
104    }
105}
106
107pub struct DynPrepared<'transaction, Out> {
108    inner: Box<dyn 'transaction + Prepared<Out = Out>>,
109}
110
111impl<Out> Prepared for DynPrepared<'_, Out> {
112    type Out = Out;
113    fn call(&mut self, row: Row<'_>) -> Self::Out {
114        self.inner.call(row)
115    }
116}
117
118impl<'transaction, S, Out> Select<'_, 'transaction, S, Out> {
119    pub(crate) fn new(val: impl 'transaction + SelectImpl<'transaction, Out = Out>) -> Self {
120        Self {
121            inner: DynSelectImpl {
122                inner: Box::new(|cacher| DynPrepared {
123                    inner: Box::new(val.prepare(cacher)),
124                }),
125            },
126            _p: PhantomData,
127            _p2: PhantomData,
128        }
129    }
130}
131
132impl<'columns, 'transaction, S, Out: 'transaction> IntoSelect<'columns, 'transaction, S>
133    for Select<'columns, 'transaction, S, Out>
134{
135    type Out = Out;
136
137    fn into_select(self) -> Select<'columns, 'transaction, S, Self::Out> {
138        self
139    }
140}
141
142pub trait SelectImpl<'transaction> {
143    type Out;
144    #[doc(hidden)]
145    type Prepared: Prepared<Out = Self::Out>;
146    #[doc(hidden)]
147    fn prepare(self, cacher: &mut Cacher) -> Self::Prepared;
148}
149
150/// This trait is implemented by everything that can be retrieved from the database.
151///
152/// The most common type that implements [IntoSelect] is [Expr].
153/// Tuples of two values also implement [IntoSelect]. If you want to return more
154/// than two values, then you should use a struct that derives [derive@rust_query::Select].
155pub trait IntoSelect<'columns, 'transaction, S>: Sized {
156    /// The type that results from executing the [Select].
157    type Out: 'transaction;
158
159    /// This method is what tells rust-query how to turn the value into a [Select].
160    ///
161    /// The only way to implement this method is by constructing a different value
162    /// that implements [IntoSelect] and then calling the [IntoSelect::into_select] method
163    /// on that other value. The result can then be modified with [Select::map].
164    fn into_select(self) -> Select<'columns, 'transaction, S, Self::Out>;
165}
166
167/// [IntoSelectExt] adds extra methods to values that implement [IntoSelect].
168pub trait IntoSelectExt<'columns, 'transaction, S>: IntoSelect<'columns, 'transaction, S> {
169    /// Refer to [Select::map].
170    #[deprecated = "Please use `Select::map`"]
171    fn map_select<T>(
172        self,
173        f: impl 'transaction + FnMut(Self::Out) -> T,
174    ) -> Select<'columns, 'transaction, S, T>;
175}
176
177impl<'columns, 'transaction, S, X> IntoSelectExt<'columns, 'transaction, S> for X
178where
179    X: IntoSelect<'columns, 'transaction, S>,
180{
181    fn map_select<T>(
182        self,
183        f: impl 'transaction + FnMut(Self::Out) -> T,
184    ) -> Select<'columns, 'transaction, S, T> {
185        Select::new(MapImpl {
186            dummy: self.into_select().inner,
187            func: f,
188        })
189    }
190}
191
192/// This is the result of the [Select::map_select] method.
193///
194/// [MapImpl] retrieves the same columns as the [Select] that it wraps,
195/// but then it processes those columns using a rust closure.
196pub struct MapImpl<D, F> {
197    dummy: D,
198    func: F,
199}
200
201impl<'transaction, D, F, O> SelectImpl<'transaction> for MapImpl<D, F>
202where
203    D: SelectImpl<'transaction>,
204    F: FnMut(D::Out) -> O,
205{
206    type Out = O;
207    type Prepared = MapPrepared<D::Prepared, F>;
208
209    fn prepare(self, cacher: &mut Cacher) -> Self::Prepared {
210        MapPrepared {
211            inner: self.dummy.prepare(cacher),
212            map: self.func,
213        }
214    }
215}
216
217pub struct MapPrepared<X, M> {
218    inner: X,
219    map: M,
220}
221
222impl<X, M, Out> Prepared for MapPrepared<X, M>
223where
224    X: Prepared,
225    M: FnMut(X::Out) -> Out,
226{
227    type Out = Out;
228
229    fn call(&mut self, row: Row<'_>) -> Self::Out {
230        (self.map)(self.inner.call(row))
231    }
232}
233
234impl Prepared for () {
235    type Out = ();
236
237    fn call(&mut self, _row: Row<'_>) -> Self::Out {}
238}
239
240impl SelectImpl<'_> for () {
241    type Out = ();
242    type Prepared = ();
243
244    fn prepare(self, _cacher: &mut Cacher) -> Self::Prepared {}
245}
246
247impl<'columns, 'transaction, S> IntoSelect<'columns, 'transaction, S> for () {
248    type Out = ();
249
250    fn into_select(self) -> Select<'columns, 'transaction, S, Self::Out> {
251        Select::new(())
252    }
253}
254
255impl<'transaction, T: SecretFromSql<'transaction>> Prepared for Cached<T> {
256    type Out = T;
257
258    fn call(&mut self, row: Row<'_>) -> Self::Out {
259        row.get(*self)
260    }
261}
262
263pub struct ColumnImpl<T> {
264    pub(crate) expr: DynTyped<T>,
265}
266
267impl<'transaction, T: MyTyp> SelectImpl<'transaction> for ColumnImpl<T> {
268    type Out = T::Out<'transaction>;
269    type Prepared = Cached<Self::Out>;
270
271    fn prepare(self, cacher: &mut Cacher) -> Self::Prepared {
272        Cached {
273            idx: cacher.cache_erased(self.expr.erase()),
274            _p: PhantomData,
275        }
276    }
277}
278
279impl<'columns, 'transaction, S, T> IntoSelect<'columns, 'transaction, S> for Expr<'columns, S, T>
280where
281    T: MyTyp,
282{
283    type Out = T::Out<'transaction>;
284
285    fn into_select(self) -> Select<'columns, 'transaction, S, Self::Out> {
286        Select::new(ColumnImpl { expr: self.inner })
287    }
288}
289
290impl<'columns, 'transaction, S, T> IntoSelect<'columns, 'transaction, S> for &T
291where
292    T: IntoSelect<'columns, 'transaction, S> + Clone,
293{
294    type Out = T::Out;
295
296    fn into_select(self) -> Select<'columns, 'transaction, S, Self::Out> {
297        T::clone(self).into_select()
298    }
299}
300
301impl<A, B> Prepared for (A, B)
302where
303    A: Prepared,
304    B: Prepared,
305{
306    type Out = (A::Out, B::Out);
307
308    fn call(&mut self, row: Row<'_>) -> Self::Out {
309        (self.0.call(row), self.1.call(row))
310    }
311}
312
313impl<'transaction, A, B> SelectImpl<'transaction> for (A, B)
314where
315    A: SelectImpl<'transaction>,
316    B: SelectImpl<'transaction>,
317{
318    type Out = (A::Out, B::Out);
319    type Prepared = (A::Prepared, B::Prepared);
320
321    fn prepare(self, cacher: &mut Cacher) -> Self::Prepared {
322        let prepared_a = self.0.prepare(cacher);
323        let prepared_b = self.1.prepare(cacher);
324        (prepared_a, prepared_b)
325    }
326}
327
328impl<'columns, 'transaction, S, A, B> IntoSelect<'columns, 'transaction, S> for (A, B)
329where
330    A: IntoSelect<'columns, 'transaction, S>,
331    B: IntoSelect<'columns, 'transaction, S>,
332{
333    type Out = (A::Out, B::Out);
334
335    fn into_select(self) -> Select<'columns, 'transaction, S, Self::Out> {
336        Select::new((self.0.into_select().inner, self.1.into_select().inner))
337    }
338}
339
340#[cfg(test)]
341#[allow(unused)]
342mod tests {
343    use crate::IntoExpr;
344
345    use super::*;
346
347    struct User {
348        a: i64,
349        b: String,
350    }
351
352    struct UserSelect<A, B> {
353        a: A,
354        b: B,
355    }
356
357    impl<'columns, 'transaction, S, A, B> IntoSelect<'columns, 'transaction, S> for UserSelect<A, B>
358    where
359        A: IntoExpr<'columns, S, Typ = i64>,
360        B: IntoExpr<'columns, S, Typ = String>,
361    {
362        type Out = User;
363
364        fn into_select(self) -> Select<'columns, 'transaction, S, Self::Out> {
365            (self.a.into_expr(), self.b.into_expr())
366                .into_select()
367                .map((|(a, b)| User { a, b }) as fn((i64, String)) -> User)
368                .into_select()
369        }
370    }
371}