rust_query/
dummy_impl.rs

1use std::marker::PhantomData;
2
3use sea_query::IntoIden;
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<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/// The most common type that implements [IntoSelect] is [Expr].
148/// Tuples of two values also implement [IntoSelect]. If you want to return more
149/// than two values, then you should use a struct that derives [derive@rust_query::Select].
150pub trait IntoSelect<'columns, S>: Sized {
151    /// The type that results from executing the [Select].
152    type Out: 'static;
153
154    /// This method is what tells rust-query how to turn the value into a [Select].
155    ///
156    /// The only way to implement this method is by constructing a different value
157    /// that implements [IntoSelect] and then calling the [IntoSelect::into_select] method
158    /// on that other value. The result can then be modified with [Select::map].
159    fn into_select(self) -> Select<'columns, S, Self::Out>;
160}
161
162/// This is the result of the [Select::map_select] method.
163///
164/// [MapImpl] retrieves the same columns as the [Select] that it wraps,
165/// but then it processes those columns using a rust closure.
166pub struct MapImpl<D, F> {
167    dummy: D,
168    func: F,
169}
170
171impl<D, F, O> SelectImpl for MapImpl<D, F>
172where
173    D: SelectImpl,
174    F: FnMut(D::Out) -> O,
175{
176    type Out = O;
177    type Prepared = MapPrepared<D::Prepared, F>;
178
179    fn prepare(self, cacher: &mut Cacher) -> Self::Prepared {
180        MapPrepared {
181            inner: self.dummy.prepare(cacher),
182            map: self.func,
183        }
184    }
185}
186
187pub struct MapPrepared<X, M> {
188    inner: X,
189    map: M,
190}
191
192impl<X, M, Out> Prepared for MapPrepared<X, M>
193where
194    X: Prepared,
195    M: FnMut(X::Out) -> Out,
196{
197    type Out = Out;
198
199    fn call(&mut self, row: Row<'_>) -> Self::Out {
200        (self.map)(self.inner.call(row))
201    }
202}
203
204impl Prepared for () {
205    type Out = ();
206
207    fn call(&mut self, _row: Row<'_>) -> Self::Out {}
208}
209
210impl SelectImpl for () {
211    type Out = ();
212    type Prepared = ();
213
214    fn prepare(self, _cacher: &mut Cacher) -> Self::Prepared {}
215}
216
217impl<'columns, S> IntoSelect<'columns, S> for () {
218    type Out = ();
219
220    fn into_select(self) -> Select<'columns, S, Self::Out> {
221        Select::new(())
222    }
223}
224
225impl<T: SecretFromSql> Prepared for Cached<T> {
226    type Out = T;
227
228    fn call(&mut self, row: Row<'_>) -> Self::Out {
229        row.get(*self)
230    }
231}
232
233pub struct ColumnImpl<T> {
234    pub(crate) expr: DynTyped<T>,
235}
236
237impl<T: MyTyp> SelectImpl for ColumnImpl<T> {
238    type Out = T::Out;
239    type Prepared = Cached<Self::Out>;
240
241    fn prepare(self, cacher: &mut Cacher) -> Self::Prepared {
242        Cached {
243            idx: cacher.cache_erased(self.expr.erase()),
244            _p: PhantomData,
245        }
246    }
247}
248
249impl<'columns, S, T> IntoSelect<'columns, S> for Expr<'columns, S, T>
250where
251    T: MyTyp,
252{
253    type Out = T::Out;
254
255    fn into_select(self) -> Select<'columns, S, Self::Out> {
256        Select::new(ColumnImpl { expr: self.inner })
257    }
258}
259
260impl<'columns, S, T> IntoSelect<'columns, S> for &T
261where
262    T: IntoSelect<'columns, S> + Clone,
263{
264    type Out = T::Out;
265
266    fn into_select(self) -> Select<'columns, S, Self::Out> {
267        T::clone(self).into_select()
268    }
269}
270
271impl<A, B> Prepared for (A, B)
272where
273    A: Prepared,
274    B: Prepared,
275{
276    type Out = (A::Out, B::Out);
277
278    fn call(&mut self, row: Row<'_>) -> Self::Out {
279        (self.0.call(row), self.1.call(row))
280    }
281}
282
283impl<A, B> SelectImpl for (A, B)
284where
285    A: SelectImpl,
286    B: SelectImpl,
287{
288    type Out = (A::Out, B::Out);
289    type Prepared = (A::Prepared, B::Prepared);
290
291    fn prepare(self, cacher: &mut Cacher) -> Self::Prepared {
292        let prepared_a = self.0.prepare(cacher);
293        let prepared_b = self.1.prepare(cacher);
294        (prepared_a, prepared_b)
295    }
296}
297
298impl<'columns, S, A, B> IntoSelect<'columns, S> for (A, B)
299where
300    A: IntoSelect<'columns, S>,
301    B: IntoSelect<'columns, S>,
302{
303    type Out = (A::Out, B::Out);
304
305    fn into_select(self) -> Select<'columns, S, Self::Out> {
306        Select::new((self.0.into_select().inner, self.1.into_select().inner))
307    }
308}
309
310#[cfg(test)]
311#[allow(unused)]
312mod tests {
313    use crate::IntoExpr;
314
315    use super::*;
316
317    struct User {
318        a: i64,
319        b: String,
320    }
321
322    struct UserSelect<A, B> {
323        a: A,
324        b: B,
325    }
326
327    impl<'columns, S, A, B> IntoSelect<'columns, S> for UserSelect<A, B>
328    where
329        A: IntoExpr<'columns, S, Typ = i64>,
330        B: IntoExpr<'columns, S, Typ = String>,
331    {
332        type Out = User;
333
334        fn into_select(self) -> Select<'columns, S, Self::Out> {
335            (self.a.into_expr(), self.b.into_expr())
336                .into_select()
337                .map((|(a, b)| User { a, b }) as fn((i64, String)) -> User)
338                .into_select()
339        }
340    }
341}