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}