Skip to main content

rust_query/
value.rs

1pub mod aggregate;
2mod db_typ;
3pub mod from_expr;
4pub mod into_expr;
5#[cfg(feature = "jiff-02")]
6mod jiff_operations;
7mod operations;
8pub mod optional;
9
10use std::{cell::OnceCell, fmt::Debug, marker::PhantomData, ops::Deref, rc::Rc};
11
12use crate::{
13    IntoExpr, IntoSelect, Select, Table,
14    db::TableRow,
15    lower::{self, JoinableTable},
16    mutable::Mutable,
17    private::IntoJoinable,
18};
19pub use db_typ::{DbTyp, StorableTyp};
20
21pub trait NumTyp: OrdTyp + Clone + Copy {
22    const ZERO: &str;
23}
24
25impl NumTyp for i64 {
26    const ZERO: &str = "0";
27}
28impl NumTyp for f64 {
29    const ZERO: &str = "0.0";
30}
31
32pub trait OrdTyp: EqTyp {}
33impl OrdTyp for String {}
34impl OrdTyp for Vec<u8> {}
35impl OrdTyp for i64 {}
36impl OrdTyp for f64 {}
37impl OrdTyp for bool {}
38#[cfg(feature = "jiff-02")]
39impl OrdTyp for jiff::Timestamp {}
40#[cfg(feature = "jiff-02")]
41impl OrdTyp for jiff::civil::Date {}
42
43pub trait BuffTyp: DbTyp {}
44impl BuffTyp for String {}
45impl BuffTyp for Vec<u8> {}
46
47#[diagnostic::on_unimplemented(
48    message = "Columns with type `{Self}` can not be checked for equality",
49    note = "`EqTyp` is also implemented for all table types"
50)]
51pub trait EqTyp: DbTyp {}
52
53impl EqTyp for String {}
54impl EqTyp for Vec<u8> {}
55impl EqTyp for i64 {}
56impl EqTyp for f64 {}
57impl EqTyp for bool {}
58#[cfg(feature = "jiff-02")]
59impl EqTyp for jiff::Timestamp {}
60#[cfg(feature = "jiff-02")]
61impl EqTyp for jiff::civil::Date {}
62#[diagnostic::do_not_recommend]
63impl<T: Table> EqTyp for TableRow<T> {}
64
65pub trait OptTable: DbTyp {
66    type Schema;
67    type Select;
68    type Mutable<'t>;
69    fn select_opt_mutable(
70        val: Expr<'_, Self::Schema, Self>,
71    ) -> Select<'_, Self::Schema, Self::Select>;
72
73    fn into_mutable<'t>(val: Self::Select) -> Self::Mutable<'t>;
74}
75
76impl<T: Table> OptTable for TableRow<T> {
77    type Schema = T::Schema;
78    type Select = (T::Select, TableRow<T>);
79    type Mutable<'t> = Mutable<'t, T>;
80    fn select_opt_mutable(
81        val: Expr<'_, Self::Schema, Self>,
82    ) -> Select<'_, Self::Schema, Self::Select> {
83        (T::into_select(val.clone()), val).into_select()
84    }
85
86    fn into_mutable<'t>((inner, row_id): Self::Select) -> Self::Mutable<'t> {
87        Mutable::new(T::select_mutable(inner), row_id)
88    }
89}
90
91impl<T: Table> OptTable for Option<TableRow<T>> {
92    type Schema = T::Schema;
93    type Select = Option<(T::Select, TableRow<T>)>;
94    type Mutable<'t> = Option<Mutable<'t, T>>;
95    fn select_opt_mutable(
96        val: Expr<'_, Self::Schema, Self>,
97    ) -> Select<'_, Self::Schema, Self::Select> {
98        crate::optional(|row| {
99            let val = row.and(val);
100            row.then_select((T::into_select(val.clone()), val))
101        })
102    }
103
104    fn into_mutable<'t>(val: Self::Select) -> Self::Mutable<'t> {
105        val.map(TableRow::<T>::into_mutable)
106    }
107}
108
109/// This is an expression that can be used in queries.
110///
111/// - The lifetime parameter `'column` specifies which columns need to be in scope.
112/// - The type parameter `S` specifies the expected schema of the query.
113/// - And finally the type paramter `T` specifies the type of the expression.
114///
115/// [Expr] implements [Deref] to have column fields in case the expression has a table type.
116pub struct Expr<'column, S, T: DbTyp> {
117    pub(crate) _local: PhantomData<*const ()>,
118    pub(crate) inner: Rc<lower::Expr>,
119    pub(crate) _p: PhantomData<&'column ()>,
120    pub(crate) _p2: PhantomData<S>,
121    pub(crate) ext: OnceCell<Box<T::Ext<'static>>>,
122    pub(crate) nullable: bool,
123}
124
125#[cfg_attr(feature = "__mutants", mutants::skip)]
126impl<S, T: DbTyp> Debug for Expr<'_, S, T> {
127    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
128        write!(f, "Expr of type {}", std::any::type_name::<T>())
129    }
130}
131
132impl<'column, S, T: DbTyp> Expr<'column, S, T> {
133    /// Extremely easy to use API. Should only be used by the macro to implement migrations.
134    #[doc(hidden)]
135    pub fn _migrate<OldS>(prev: impl IntoExpr<'column, OldS>) -> Self {
136        let prev = prev.into_expr().inner;
137        Self::new(prev)
138    }
139}
140
141pub fn adhoc_expr<S, T: DbTyp>(f: lower::Expr) -> Expr<'static, S, T> {
142    Expr::adhoc(f)
143}
144
145pub fn new_column<'x, S, C: DbTyp, T: Table>(
146    table: impl IntoExpr<'x, S, Typ = TableRow<T>>,
147    name: &'static str,
148) -> Expr<'x, S, C> {
149    let table = table.into_expr();
150    Expr::new_inner(
151        table
152            .inner
153            .col(JoinableTable::Table(T::NAME), name, T::ID, table.nullable),
154        table.nullable || C::NULLABLE,
155    )
156}
157
158pub fn unique_from_joinable<'inner, T: Table>(
159    j: impl IntoJoinable<'inner, T::Schema, Typ = TableRow<T>>,
160) -> Expr<'inner, T::Schema, Option<TableRow<T>>> {
161    let joinable = j.into_joinable();
162    let unique = Rc::new(lower::Unique {
163        table: joinable.table,
164        conds: joinable.conds,
165        guaranteed: false,
166    });
167    Expr::adhoc(lower::Expr::RowIndex(lower::RowLike::Unique(unique), T::ID))
168}
169
170impl<S, T: DbTyp> Expr<'_, S, T> {
171    pub(crate) fn adhoc(e: lower::Expr) -> Self {
172        Self::new(Rc::new(e))
173    }
174
175    pub(crate) fn new(val: Rc<lower::Expr>) -> Self {
176        Self::new_inner(val, true)
177    }
178
179    pub(crate) fn new_inner(val: Rc<lower::Expr>, nullable: bool) -> Self {
180        Self {
181            _local: PhantomData,
182            inner: val,
183            _p: PhantomData,
184            _p2: PhantomData,
185            ext: OnceCell::new(),
186            nullable,
187        }
188    }
189}
190
191impl<S, T: DbTyp> Clone for Expr<'_, S, T> {
192    fn clone(&self) -> Self {
193        Self {
194            _local: PhantomData,
195            inner: self.inner.clone(),
196            _p: self._p,
197            _p2: self._p2,
198            ext: OnceCell::new(),
199            nullable: self.nullable,
200        }
201    }
202}
203
204impl<'t, T: Table> Deref for Expr<'t, T::Schema, TableRow<T>> {
205    type Target = T::Ext2<'t>;
206
207    fn deref(&self) -> &Self::Target {
208        T::covariant_ext(self.ext.get_or_init(|| {
209            let expr = Expr {
210                _local: PhantomData,
211                inner: self.inner.clone(),
212                _p: PhantomData::<&'static ()>,
213                _p2: PhantomData,
214                ext: OnceCell::new(),
215                nullable: self.nullable,
216            };
217            Box::new(T::build_ext2(&expr))
218        }))
219    }
220}