rust_query/value/
operations.rs

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