rust_query/value/
operations.rs

1use sea_query::{Alias, ExprTrait, extension::sqlite::SqliteExpr};
2
3use crate::value::MyTyp;
4
5use super::{EqTyp, Expr, IntoExpr, NumTyp};
6
7impl<'column, S, T: NumTyp> Expr<'column, S, T> {
8    /// Add two expressions together.
9    ///
10    /// ```
11    /// # use rust_query::IntoExpr;
12    /// # rust_query::private::doctest::get_txn(|txn| {
13    /// assert_eq!(txn.query_one(1.into_expr().add(2)), 3);
14    /// assert_eq!(txn.query_one(1.0.into_expr().add(2.0)), 3.0);
15    /// # });
16    /// ```
17    pub fn add(&self, rhs: impl IntoExpr<'column, S, Typ = T>) -> Expr<'column, S, T> {
18        let lhs = self.inner.clone();
19        let rhs = rhs.into_expr().inner;
20        Expr::adhoc(move |b| lhs.build_expr(b).add(rhs.build_expr(b)))
21    }
22
23    /// Subtract one expression from another.
24    ///
25    /// ```
26    /// # use rust_query::IntoExpr;
27    /// # rust_query::private::doctest::get_txn(|txn| {
28    /// assert_eq!(txn.query_one(1.into_expr().sub(2)), -1);
29    /// assert_eq!(txn.query_one(1.0.into_expr().sub(2.0)), -1.0);
30    /// # });
31    /// ```
32    pub fn sub(&self, rhs: impl IntoExpr<'column, S, Typ = T>) -> Expr<'column, S, T> {
33        let lhs = self.inner.clone();
34        let rhs = rhs.into_expr().inner;
35        Expr::adhoc(move |b| lhs.build_expr(b).sub(rhs.build_expr(b)))
36    }
37
38    /// Multiply two expressions together.
39    ///
40    /// ```
41    /// # use rust_query::IntoExpr;
42    /// # rust_query::private::doctest::get_txn(|txn| {
43    /// assert_eq!(txn.query_one(2.into_expr().mul(3)), 6);
44    /// assert_eq!(txn.query_one(2.0.into_expr().mul(3.0)), 6.0);
45    /// # });
46    /// ```
47    pub fn mul(&self, rhs: impl IntoExpr<'column, S, Typ = T>) -> Expr<'column, S, T> {
48        let lhs = self.inner.clone();
49        let rhs = rhs.into_expr().inner;
50        Expr::adhoc(move |b| lhs.build_expr(b).mul(rhs.build_expr(b)))
51    }
52
53    /// Compute the less than operator (<) of two expressions.
54    ///
55    /// ```
56    /// # use rust_query::IntoExpr;
57    /// # rust_query::private::doctest::get_txn(|txn| {
58    /// assert_eq!(txn.query_one(2.into_expr().lt(3)), true);
59    /// assert_eq!(txn.query_one(1.into_expr().lt(1)), false);
60    /// assert_eq!(txn.query_one(3.0.into_expr().lt(1.0)), false);
61    /// # });
62    /// ```
63    pub fn lt(&self, rhs: impl IntoExpr<'column, S, Typ = T>) -> Expr<'column, S, bool> {
64        let lhs = self.inner.clone();
65        let rhs = rhs.into_expr().inner;
66        Expr::adhoc(move |b| lhs.build_expr(b).lt(rhs.build_expr(b)))
67    }
68
69    /// Compute the less than or equal operator (<=) of two expressions.
70    ///
71    /// ```
72    /// # use rust_query::IntoExpr;
73    /// # rust_query::private::doctest::get_txn(|txn| {
74    /// assert_eq!(txn.query_one(2.into_expr().lte(2)), true);
75    /// assert_eq!(txn.query_one(3.0.into_expr().lte(1.0)), false);
76    /// # });
77    /// ```
78    pub fn lte(&self, rhs: impl IntoExpr<'column, S, Typ = T>) -> Expr<'column, S, bool> {
79        let lhs = self.inner.clone();
80        let rhs = rhs.into_expr().inner;
81        Expr::adhoc(move |b| lhs.build_expr(b).lte(rhs.build_expr(b)))
82    }
83
84    /// Compute the greater than operator (>) of two expressions.
85    ///
86    /// ```
87    /// # use rust_query::IntoExpr;
88    /// # rust_query::private::doctest::get_txn(|txn| {
89    /// assert_eq!(txn.query_one(2.into_expr().gt(2)), false);
90    /// assert_eq!(txn.query_one(3.0.into_expr().gt(1.0)), true);
91    /// # });
92    /// ```
93    pub fn gt(&self, rhs: impl IntoExpr<'column, S, Typ = T>) -> Expr<'column, S, bool> {
94        let lhs = self.inner.clone();
95        let rhs = rhs.into_expr().inner;
96        Expr::adhoc(move |b| lhs.build_expr(b).gt(rhs.build_expr(b)))
97    }
98
99    /// Compute the greater than or equal (>=) operator of two expressions.
100    ///
101    /// ```
102    /// # use rust_query::IntoExpr;
103    /// # rust_query::private::doctest::get_txn(|txn| {
104    /// assert_eq!(txn.query_one(2.into_expr().gte(3)), false);
105    /// assert_eq!(txn.query_one(3.0.into_expr().gte(3.0)), true);
106    /// # });
107    /// ```
108    pub fn gte(&self, rhs: impl IntoExpr<'column, S, Typ = T>) -> Expr<'column, S, bool> {
109        let lhs = self.inner.clone();
110        let rhs = rhs.into_expr().inner;
111        Expr::adhoc(move |b| lhs.build_expr(b).gte(rhs.build_expr(b)))
112    }
113}
114
115impl<'column, S, T: EqTyp + 'static> Expr<'column, S, T> {
116    /// Check whether two expressions are equal.
117    ///
118    /// ```
119    /// # use rust_query::IntoExpr;
120    /// # rust_query::private::doctest::get_txn(|txn| {
121    /// assert_eq!(txn.query_one(2.into_expr().eq(2)), true);
122    /// assert_eq!(txn.query_one(3.0.into_expr().eq(3.0)), true);
123    /// assert_eq!(txn.query_one("test".into_expr().eq("test")), true);
124    /// assert_eq!(txn.query_one(b"test".into_expr().eq(b"test" as &[u8])), true);
125    /// assert_eq!(txn.query_one(false.into_expr().eq(false)), true);
126    ///
127    /// assert_eq!(txn.query_one(1.into_expr().eq(2)), false);
128    /// # });
129    /// ```
130    pub fn eq(&self, rhs: impl IntoExpr<'column, S, Typ = T>) -> Expr<'column, S, bool> {
131        let lhs = self.inner.clone();
132        let rhs = rhs.into_expr().inner;
133        Expr::adhoc(move |b| lhs.build_expr(b).is(rhs.build_expr(b)))
134    }
135
136    /// Check whether two expressions are not equal.
137    ///
138    /// ```
139    /// # use rust_query::IntoExpr;
140    /// # rust_query::private::doctest::get_txn(|txn| {
141    /// assert_eq!(txn.query_one(2.into_expr().neq(2)), false);
142    /// assert_eq!(txn.query_one(3.0.into_expr().neq(3.1)), true);
143    /// assert_eq!(txn.query_one("test".into_expr().neq("test")), false);
144    /// assert_eq!(txn.query_one(b"test".into_expr().neq(b"test" as &[u8])), false);
145    /// assert_eq!(txn.query_one(false.into_expr().neq(false)), false);
146    ///
147    /// assert_eq!(txn.query_one(1.into_expr().neq(2)), true);
148    /// # });
149    /// ```
150    pub fn neq(&self, rhs: impl IntoExpr<'column, S, Typ = T>) -> Expr<'column, S, bool> {
151        let lhs = self.inner.clone();
152        let rhs = rhs.into_expr().inner;
153        Expr::adhoc(move |b| lhs.build_expr(b).is_not(rhs.build_expr(b)))
154    }
155}
156
157impl<'column, S> Expr<'column, S, bool> {
158    /// Checks whether an expression is false.
159    ///
160    /// ```
161    /// # use rust_query::IntoExpr;
162    /// # rust_query::private::doctest::get_txn(|txn| {
163    /// assert_eq!(txn.query_one(true.into_expr().not()), false);
164    /// assert_eq!(txn.query_one(false.into_expr().not()), true);
165    /// # });
166    /// ```
167    pub fn not(&self) -> Expr<'column, S, bool> {
168        let val = self.inner.clone();
169        Expr::adhoc(move |b| val.build_expr(b).not())
170    }
171
172    /// Check if two expressions are both true.
173    ///
174    /// ```
175    /// # use rust_query::IntoExpr;
176    /// # rust_query::private::doctest::get_txn(|txn| {
177    /// assert_eq!(txn.query_one(true.into_expr().and(true)), true);
178    /// assert_eq!(txn.query_one(false.into_expr().and(true)), false);
179    /// assert_eq!(txn.query_one(false.into_expr().and(false)), false);
180    /// # });
181    /// ```
182    pub fn and(&self, rhs: impl IntoExpr<'column, S, Typ = bool>) -> Expr<'column, S, bool> {
183        let lhs = self.inner.clone();
184        let rhs = rhs.into_expr().inner;
185        Expr::adhoc(move |b| lhs.build_expr(b).and(rhs.build_expr(b)))
186    }
187
188    /// Check if one of two expressions is true.
189    ///
190    /// ```
191    /// # use rust_query::IntoExpr;
192    /// # rust_query::private::doctest::get_txn(|txn| {
193    /// assert_eq!(txn.query_one(true.into_expr().or(true)), true);
194    /// assert_eq!(txn.query_one(false.into_expr().or(true)), true);
195    /// assert_eq!(txn.query_one(false.into_expr().or(false)), false);
196    /// # });
197    /// ```
198    pub fn or(&self, rhs: impl IntoExpr<'column, S, Typ = bool>) -> Expr<'column, S, bool> {
199        let lhs = self.inner.clone();
200        let rhs = rhs.into_expr().inner;
201        Expr::adhoc(move |b| lhs.build_expr(b).or(rhs.build_expr(b)))
202    }
203}
204
205impl<'column, S, Typ: MyTyp> Expr<'column, S, Option<Typ>> {
206    /// Use the first expression if it is [Some], otherwise use the second expression.
207    ///
208    /// ```
209    /// # use rust_query::IntoExpr;
210    /// # rust_query::private::doctest::get_txn(|txn| {
211    /// assert_eq!(txn.query_one(Some(10).into_expr().unwrap_or(5)), 10);
212    /// assert_eq!(txn.query_one(None::<String>.into_expr().unwrap_or("foo")), "foo");
213    /// # });
214    /// ```
215    pub fn unwrap_or(&self, rhs: impl IntoExpr<'column, S, Typ = Typ>) -> Expr<'column, S, Typ>
216    where
217        Self: IntoExpr<'column, S, Typ = Option<Typ>>,
218    {
219        let lhs = self.inner.clone();
220        let rhs = rhs.into_expr().inner;
221        let maybe_optional = rhs.maybe_optional();
222        Expr::adhoc_promise(
223            move |b| sea_query::Expr::expr(lhs.build_expr(b)).if_null(rhs.build_expr(b)),
224            maybe_optional,
225        )
226    }
227
228    /// Check that the expression is [Some].
229    ///
230    /// ```
231    /// # use rust_query::IntoExpr;
232    /// # rust_query::private::doctest::get_txn(|txn| {
233    /// assert_eq!(txn.query_one(Some(10).into_expr().is_some()), true);
234    /// assert_eq!(txn.query_one(None::<i64>.into_expr().is_some()), false);
235    /// # });
236    /// ```
237    pub fn is_some(&self) -> Expr<'column, S, bool> {
238        let val = self.inner.clone();
239        Expr::adhoc(move |b| val.build_expr(b).is_not_null())
240    }
241
242    /// Check that the expression is [None].
243    ///
244    /// ```
245    /// # use rust_query::IntoExpr;
246    /// # rust_query::private::doctest::get_txn(|txn| {
247    /// assert_eq!(txn.query_one(Some(10).into_expr().is_none()), false);
248    /// assert_eq!(txn.query_one(None::<i64>.into_expr().is_none()), true);
249    /// # });
250    /// ```
251    pub fn is_none(&self) -> Expr<'column, S, bool> {
252        let val = self.inner.clone();
253        Expr::adhoc(move |b| val.build_expr(b).is_null())
254    }
255}
256
257impl<'column, S> Expr<'column, S, i64> {
258    /// Convert the [i64] expression to [f64] type.
259    ///
260    /// ```
261    /// # use rust_query::IntoExpr;
262    /// # rust_query::private::doctest::get_txn(|txn| {
263    /// assert_eq!(txn.query_one(10.into_expr().as_float()), 10.0);
264    /// # });
265    /// ```
266    pub fn as_float(&self) -> Expr<'column, S, f64> {
267        let val = self.inner.clone();
268        Expr::adhoc(move |b| val.build_expr(b).cast_as(Alias::new("real")))
269    }
270}
271
272impl<'column, S> Expr<'column, S, String> {
273    /// Check if the expression starts with the string pattern.
274    ///
275    /// Matches case-sensitive. The pattern gets automatically escaped.
276    ///
277    /// ```
278    /// # use rust_query::IntoExpr;
279    /// # rust_query::private::doctest::get_txn(|txn| {
280    /// assert_eq!(txn.query_one("hello world".into_expr().starts_with("hello")), true);
281    /// assert_eq!(txn.query_one("hello world".into_expr().starts_with("Hello")), false);
282    /// # });
283    /// ```
284    pub fn starts_with(&self, pattern: impl AsRef<str>) -> Expr<'column, S, bool> {
285        self.glob(format!("{}*", escape_glob(pattern)))
286    }
287
288    /// Check if the expression ends with the string pattern.
289    ///
290    /// Matches case-sensitive. The pattern gets automatically escaped.
291    ///
292    /// ```
293    /// # use rust_query::IntoExpr;
294    /// # rust_query::private::doctest::get_txn(|txn| {
295    /// assert_eq!(txn.query_one("hello world".into_expr().ends_with("world")), true);
296    /// assert_eq!(txn.query_one("hello world".into_expr().ends_with("World")), false);
297    /// # });
298    /// ```
299    pub fn ends_with(&self, pattern: impl AsRef<str>) -> Expr<'column, S, bool> {
300        self.glob(format!("*{}", escape_glob(pattern)))
301    }
302
303    /// Check if the expression contains the string pattern.
304    ///
305    /// Matches case-sensitive. The pattern gets automatically escaped.
306    ///
307    /// ```
308    /// # use rust_query::IntoExpr;
309    /// # rust_query::private::doctest::get_txn(|txn| {
310    /// assert_eq!(txn.query_one("rhubarb".into_expr().contains("bar")), true);
311    /// assert_eq!(txn.query_one("rhubarb".into_expr().contains("Bar")), false);
312    /// # });
313    /// ```
314    pub fn contains(&self, pattern: impl AsRef<str>) -> Expr<'column, S, bool> {
315        self.glob(format!("*{}*", escape_glob(pattern)))
316    }
317
318    /// Check if the expression matches the pattern [sqlite docs](https://www.sqlite.org/lang_expr.html#like).
319    ///
320    /// This is a case-sensitive version of [like](Self::like). It uses Unix file globbing syntax for wild
321    /// cards. `*` matches any sequence of characters and `?` matches any single character. `[0-9]` matches
322    /// any single digit and `[a-z]` matches any single lowercase letter. `^` negates the pattern.
323    ///
324    /// ```
325    /// # use rust_query::IntoExpr;
326    /// # rust_query::private::doctest::get_txn(|txn| {
327    /// assert_eq!(txn.query_one("hello world".into_expr().glob("?ello*")), true);
328    /// assert_eq!(txn.query_one("hello world".into_expr().glob("Hell*")), false);
329    /// # });
330    /// ```
331    pub fn glob(&self, rhs: impl IntoExpr<'column, S, Typ = String>) -> Expr<'column, S, bool> {
332        let lhs = self.inner.clone();
333        let rhs = rhs.into_expr().inner;
334        Expr::adhoc(move |b| sea_query::Expr::expr(lhs.build_expr(b)).glob(rhs.build_expr(b)))
335    }
336
337    /// Check if the expression matches the pattern [sqlite docs](https://www.sqlite.org/lang_expr.html#like).
338    ///
339    /// As noted in the docs, it is **case-insensitive** for ASCII characters. Other characters are case-sensitive.
340    /// For creating patterns it uses `%` as a wildcard for any sequence of characters and `_` for any single character.
341    /// Special characters should be escaped with `\`.
342    ///
343    /// ```
344    /// # use rust_query::IntoExpr;
345    /// # rust_query::private::doctest::get_txn(|txn| {
346    /// assert_eq!(txn.query_one("hello world".into_expr().like("HELLO%")), true);
347    /// assert_eq!(txn.query_one("hello world".into_expr().like("he_o%")), false);
348    /// # });
349    /// ```
350    pub fn like(&self, pattern: impl Into<String>) -> Expr<'column, S, bool> {
351        let lhs = self.inner.clone();
352        let rhs = pattern.into();
353        Expr::adhoc(move |b| {
354            sea_query::Expr::expr(lhs.build_expr(b))
355                .like(sea_query::LikeExpr::new(&rhs).escape('\\'))
356        })
357    }
358}
359
360// This is a copy of the function from the glob crate https://github.com/rust-lang/glob/blob/49ee1e92bd6e8c5854c0b339634f9b4b733aba4f/src/lib.rs#L720-L737.
361fn escape_glob(s: impl AsRef<str>) -> String {
362    let mut escaped = String::new();
363    for c in s.as_ref().chars() {
364        match c {
365            // note that ! does not need escaping because it is only special
366            // inside brackets
367            '?' | '*' | '[' | ']' => {
368                escaped.push('[');
369                escaped.push(c);
370                escaped.push(']');
371            }
372            c => {
373                escaped.push(c);
374            }
375        }
376    }
377    escaped
378}