Skip to main content

rust_query/
select.rs

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