rust_query/
select.rs

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