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