rust_query/value/operations.rs
1use sea_query::{Alias, ExprTrait, extension::sqlite::SqliteExpr};
2
3use crate::{
4 ast::CONST_0,
5 value::{BuffTyp, OrdTyp},
6};
7
8use super::{EqTyp, Expr, IntoExpr, NumTyp};
9
10impl<'column, S, T: NumTyp> Expr<'column, S, T> {
11 /// Add two expressions together.
12 ///
13 /// ```
14 /// # use rust_query::IntoExpr;
15 /// # rust_query::private::doctest::get_txn(|txn| {
16 /// assert_eq!(txn.query_one(1.into_expr().add(2)), 3);
17 /// assert_eq!(txn.query_one(1.0.into_expr().add(2.0)), 3.0);
18 /// # });
19 /// ```
20 pub fn add(&self, rhs: impl IntoExpr<'column, S, Typ = T>) -> Self {
21 let lhs = self.inner.clone();
22 let rhs = rhs.into_expr().inner;
23 Expr::adhoc(move |b| lhs.build_expr(b).add(rhs.build_expr(b)))
24 }
25
26 /// Subtract one expression from another.
27 ///
28 /// ```
29 /// # use rust_query::IntoExpr;
30 /// # rust_query::private::doctest::get_txn(|txn| {
31 /// assert_eq!(txn.query_one(1.into_expr().sub(2)), -1);
32 /// assert_eq!(txn.query_one(1.0.into_expr().sub(2.0)), -1.0);
33 /// # });
34 /// ```
35 pub fn sub(&self, rhs: impl IntoExpr<'column, S, Typ = T>) -> Self {
36 let lhs = self.inner.clone();
37 let rhs = rhs.into_expr().inner;
38 Expr::adhoc(move |b| lhs.build_expr(b).sub(rhs.build_expr(b)))
39 }
40
41 /// Multiply two expressions together.
42 ///
43 /// ```
44 /// # use rust_query::IntoExpr;
45 /// # rust_query::private::doctest::get_txn(|txn| {
46 /// assert_eq!(txn.query_one(2.into_expr().mul(3)), 6);
47 /// assert_eq!(txn.query_one(2.0.into_expr().mul(3.0)), 6.0);
48 /// # });
49 /// ```
50 pub fn mul(&self, rhs: impl IntoExpr<'column, S, Typ = T>) -> Self {
51 let lhs = self.inner.clone();
52 let rhs = rhs.into_expr().inner;
53 Expr::adhoc(move |b| lhs.build_expr(b).mul(rhs.build_expr(b)))
54 }
55
56 /// Divide one expression by another.
57 ///
58 /// For integers, the result is truncated towards zero.
59 /// See also [Expr::modulo].
60 ///
61 /// ```
62 /// # use rust_query::IntoExpr;
63 /// # rust_query::private::doctest::get_txn(|txn| {
64 /// assert_eq!(txn.query_one(5.into_expr().div(3)), 1);
65 /// assert_eq!(txn.query_one((-5).into_expr().div(3)), -1);
66 /// assert_eq!(txn.query_one(1.0.into_expr().div(2.0)), 0.5);
67 /// # });
68 /// ```
69 pub fn div(&self, rhs: impl IntoExpr<'column, S, Typ = T>) -> Self {
70 let lhs = self.inner.clone();
71 let rhs = rhs.into_expr().inner;
72 Expr::adhoc(move |b| lhs.build_expr(b).div(rhs.build_expr(b)))
73 }
74
75 /// Get the sign of the expression.
76 ///
77 /// The result is -1, 0 or 1 depending on if the expression is
78 /// negative, zero or positive.
79 ///
80 /// ```
81 /// # use rust_query::IntoExpr;
82 /// # rust_query::private::doctest::get_txn(|txn| {
83 /// assert_eq!(txn.query_one(2.into_expr().sign()), 1);
84 /// assert_eq!(txn.query_one((-5.0).into_expr().sign()), -1);
85 /// assert_eq!(txn.query_one((-0.0).into_expr().sign()), 0);
86 /// # });
87 /// ```
88 pub fn sign(&self) -> Expr<'column, S, i64> {
89 let lhs = self.inner.clone();
90 Expr::adhoc(move |b| {
91 sea_query::Expr::expr(sea_query::Func::cust("sign").arg(lhs.build_expr(b)))
92 })
93 }
94
95 /// Get the absolute value of the expression.
96 ///
97 /// ```
98 /// # use rust_query::IntoExpr;
99 /// # rust_query::private::doctest::get_txn(|txn| {
100 /// assert_eq!(txn.query_one(2.into_expr().abs()), 2);
101 /// assert_eq!(txn.query_one((-5.0).into_expr().abs()), 5.0);
102 /// # });
103 /// ```
104 pub fn abs(&self) -> Self {
105 let lhs = self.inner.clone();
106 Expr::adhoc(move |b| {
107 sea_query::Expr::expr(sea_query::Func::cust("abs").arg(lhs.build_expr(b)))
108 })
109 }
110}
111
112impl<'column, S, T: OrdTyp> Expr<'column, S, T> {
113 /// Compute the less than operator (<) of two expressions.
114 ///
115 /// ```
116 /// # use rust_query::IntoExpr;
117 /// # rust_query::private::doctest::get_txn(|txn| {
118 /// assert_eq!(txn.query_one(2.into_expr().lt(3)), true);
119 /// assert_eq!(txn.query_one(1.into_expr().lt(1)), false);
120 /// assert_eq!(txn.query_one(3.0.into_expr().lt(1.0)), false);
121 /// # });
122 /// ```
123 pub fn lt(&self, rhs: impl IntoExpr<'column, S, Typ = T>) -> Expr<'column, S, bool> {
124 let lhs = self.inner.clone();
125 let rhs = rhs.into_expr().inner;
126 Expr::adhoc(move |b| lhs.build_expr(b).lt(rhs.build_expr(b)))
127 }
128
129 /// Compute the less than or equal operator (<=) of two expressions.
130 ///
131 /// ```
132 /// # use rust_query::IntoExpr;
133 /// # rust_query::private::doctest::get_txn(|txn| {
134 /// assert_eq!(txn.query_one(2.into_expr().lte(2)), true);
135 /// assert_eq!(txn.query_one(3.0.into_expr().lte(1.0)), false);
136 /// # });
137 /// ```
138 pub fn lte(&self, rhs: impl IntoExpr<'column, S, Typ = T>) -> Expr<'column, S, bool> {
139 let lhs = self.inner.clone();
140 let rhs = rhs.into_expr().inner;
141 Expr::adhoc(move |b| lhs.build_expr(b).lte(rhs.build_expr(b)))
142 }
143
144 /// Compute the greater than operator (>) of two expressions.
145 ///
146 /// ```
147 /// # use rust_query::IntoExpr;
148 /// # rust_query::private::doctest::get_txn(|txn| {
149 /// assert_eq!(txn.query_one(2.into_expr().gt(2)), false);
150 /// assert_eq!(txn.query_one(3.0.into_expr().gt(1.0)), true);
151 /// # });
152 /// ```
153 pub fn gt(&self, rhs: impl IntoExpr<'column, S, Typ = T>) -> Expr<'column, S, bool> {
154 let lhs = self.inner.clone();
155 let rhs = rhs.into_expr().inner;
156 Expr::adhoc(move |b| lhs.build_expr(b).gt(rhs.build_expr(b)))
157 }
158
159 /// Compute the greater than or equal (>=) operator of two expressions.
160 ///
161 /// ```
162 /// # use rust_query::IntoExpr;
163 /// # rust_query::private::doctest::get_txn(|txn| {
164 /// assert_eq!(txn.query_one(2.into_expr().gte(3)), false);
165 /// assert_eq!(txn.query_one(3.0.into_expr().gte(3.0)), true);
166 /// # });
167 /// ```
168 pub fn gte(&self, rhs: impl IntoExpr<'column, S, Typ = T>) -> Expr<'column, S, bool> {
169 let lhs = self.inner.clone();
170 let rhs = rhs.into_expr().inner;
171 Expr::adhoc(move |b| lhs.build_expr(b).gte(rhs.build_expr(b)))
172 }
173
174 /// Get the maximum of two values.
175 ///
176 /// ```
177 /// # use rust_query::IntoExpr;
178 /// # rust_query::private::doctest::get_txn(|txn| {
179 /// assert_eq!(txn.query_one(2.into_expr().max(3)), 3);
180 /// assert_eq!(txn.query_one(5.0.into_expr().max(3.0)), 5.0);
181 /// # });
182 /// ```
183 pub fn max(&self, rhs: impl IntoExpr<'column, S, Typ = T>) -> Self {
184 let lhs = self.inner.clone();
185 let rhs = rhs.into_expr().inner;
186 Expr::adhoc(move |b| {
187 sea_query::Expr::expr(
188 sea_query::Func::cust("max")
189 .arg(lhs.build_expr(b))
190 .arg(rhs.build_expr(b)),
191 )
192 })
193 }
194
195 /// Get the minimum of two values.
196 ///
197 /// ```
198 /// # use rust_query::IntoExpr;
199 /// # rust_query::private::doctest::get_txn(|txn| {
200 /// assert_eq!(txn.query_one(2.into_expr().min(3)), 2);
201 /// assert_eq!(txn.query_one(5.0.into_expr().min(3.0)), 3.0);
202 /// # });
203 /// ```
204 pub fn min(&self, rhs: impl IntoExpr<'column, S, Typ = T>) -> Self {
205 let lhs = self.inner.clone();
206 let rhs = rhs.into_expr().inner;
207 Expr::adhoc(move |b| {
208 sea_query::Expr::expr(
209 sea_query::Func::cust("min")
210 .arg(lhs.build_expr(b))
211 .arg(rhs.build_expr(b)),
212 )
213 })
214 }
215
216 /// Check if a value is between two other values.
217 ///
218 /// The range is inclusive on both sides.
219 ///
220 /// ```
221 /// # use rust_query::IntoExpr;
222 /// # rust_query::private::doctest::get_txn(|txn| {
223 /// assert_eq!(txn.query_one(2.into_expr().between(2, 3)), true);
224 /// assert_eq!(txn.query_one(3.into_expr().between(2, 3)), true);
225 /// assert_eq!(txn.query_one(5.into_expr().between(2, 3)), false);
226 /// assert_eq!(txn.query_one(1.into_expr().between(2, 3)), false);
227 /// # });
228 /// ```
229 pub fn between(
230 &self,
231 low: impl IntoExpr<'column, S, Typ = T>,
232 high: impl IntoExpr<'column, S, Typ = T>,
233 ) -> Expr<'column, S, bool> {
234 let lhs = self.inner.clone();
235 let low = low.into_expr().inner;
236 let high = high.into_expr().inner;
237 Expr::adhoc(move |b| {
238 lhs.build_expr(b)
239 .between(low.build_expr(b), high.build_expr(b))
240 })
241 }
242}
243
244impl<'column, S, T: EqTyp + 'static> Expr<'column, S, T> {
245 /// Check whether two expressions are equal.
246 ///
247 /// ```
248 /// # use rust_query::IntoExpr;
249 /// # rust_query::private::doctest::get_txn(|txn| {
250 /// assert_eq!(txn.query_one(2.into_expr().eq(2)), true);
251 /// assert_eq!(txn.query_one(3.0.into_expr().eq(3.0)), true);
252 /// assert_eq!(txn.query_one("test".into_expr().eq("test")), true);
253 /// assert_eq!(txn.query_one(b"test".into_expr().eq(b"test" as &[u8])), true);
254 /// assert_eq!(txn.query_one(false.into_expr().eq(false)), true);
255 ///
256 /// assert_eq!(txn.query_one(1.into_expr().eq(2)), false);
257 /// # });
258 /// ```
259 pub fn eq(&self, rhs: impl IntoExpr<'column, S, Typ = T>) -> Expr<'column, S, bool> {
260 let lhs = self.inner.clone();
261 let rhs = rhs.into_expr().inner;
262 Expr::adhoc(move |b| lhs.build_expr(b).is(rhs.build_expr(b)))
263 }
264
265 /// Check whether two expressions are not equal.
266 ///
267 /// ```
268 /// # use rust_query::IntoExpr;
269 /// # rust_query::private::doctest::get_txn(|txn| {
270 /// assert_eq!(txn.query_one(2.into_expr().neq(2)), false);
271 /// assert_eq!(txn.query_one(3.0.into_expr().neq(3.1)), true);
272 /// assert_eq!(txn.query_one("test".into_expr().neq("test")), false);
273 /// assert_eq!(txn.query_one(b"test".into_expr().neq(b"test" as &[u8])), false);
274 /// assert_eq!(txn.query_one(false.into_expr().neq(false)), false);
275 ///
276 /// assert_eq!(txn.query_one(1.into_expr().neq(2)), true);
277 /// # });
278 /// ```
279 pub fn neq(&self, rhs: impl IntoExpr<'column, S, Typ = T>) -> Expr<'column, S, bool> {
280 let lhs = self.inner.clone();
281 let rhs = rhs.into_expr().inner;
282 Expr::adhoc(move |b| lhs.build_expr(b).is_not(rhs.build_expr(b)))
283 }
284}
285
286impl<'column, S> Expr<'column, S, bool> {
287 /// Checks whether an expression is false.
288 ///
289 /// ```
290 /// # use rust_query::IntoExpr;
291 /// # rust_query::private::doctest::get_txn(|txn| {
292 /// assert_eq!(txn.query_one(true.into_expr().not()), false);
293 /// assert_eq!(txn.query_one(false.into_expr().not()), true);
294 /// # });
295 /// ```
296 pub fn not(&self) -> Self {
297 let val = self.inner.clone();
298 Expr::adhoc(move |b| val.build_expr(b).not())
299 }
300
301 /// Check if two expressions are both true.
302 ///
303 /// ```
304 /// # use rust_query::IntoExpr;
305 /// # rust_query::private::doctest::get_txn(|txn| {
306 /// assert_eq!(txn.query_one(true.into_expr().and(true)), true);
307 /// assert_eq!(txn.query_one(false.into_expr().and(true)), false);
308 /// assert_eq!(txn.query_one(false.into_expr().and(false)), false);
309 /// # });
310 /// ```
311 pub fn and(&self, rhs: impl IntoExpr<'column, S, Typ = bool>) -> Self {
312 let lhs = self.inner.clone();
313 let rhs = rhs.into_expr().inner;
314 Expr::adhoc(move |b| lhs.build_expr(b).and(rhs.build_expr(b)))
315 }
316
317 /// Check if one of two expressions is true.
318 ///
319 /// ```
320 /// # use rust_query::IntoExpr;
321 /// # rust_query::private::doctest::get_txn(|txn| {
322 /// assert_eq!(txn.query_one(true.into_expr().or(true)), true);
323 /// assert_eq!(txn.query_one(false.into_expr().or(true)), true);
324 /// assert_eq!(txn.query_one(false.into_expr().or(false)), false);
325 /// # });
326 /// ```
327 pub fn or(&self, rhs: impl IntoExpr<'column, S, Typ = bool>) -> Self {
328 let lhs = self.inner.clone();
329 let rhs = rhs.into_expr().inner;
330 Expr::adhoc(move |b| lhs.build_expr(b).or(rhs.build_expr(b)))
331 }
332}
333
334impl<'column, S, Typ: EqTyp> Expr<'column, S, Option<Typ>> {
335 /// Use the first expression if it is [Some], otherwise use the second expression.
336 ///
337 /// ```
338 /// # use rust_query::IntoExpr;
339 /// # rust_query::private::doctest::get_txn(|txn| {
340 /// assert_eq!(txn.query_one(Some(10).into_expr().unwrap_or(5)), 10);
341 /// assert_eq!(txn.query_one(None::<String>.into_expr().unwrap_or("foo")), "foo");
342 /// # });
343 /// ```
344 pub fn unwrap_or(&self, rhs: impl IntoExpr<'column, S, Typ = Typ>) -> Expr<'column, S, Typ> {
345 let lhs = self.inner.clone();
346 let rhs = rhs.into_expr().inner;
347 let maybe_optional = rhs.maybe_optional;
348 Expr::adhoc_promise(
349 move |b| sea_query::Expr::expr(lhs.build_expr(b)).if_null(rhs.build_expr(b)),
350 maybe_optional,
351 )
352 }
353
354 /// Check that the expression is [Some].
355 ///
356 /// ```
357 /// # use rust_query::IntoExpr;
358 /// # rust_query::private::doctest::get_txn(|txn| {
359 /// assert_eq!(txn.query_one(Some(10).into_expr().is_some()), true);
360 /// assert_eq!(txn.query_one(None::<i64>.into_expr().is_some()), false);
361 /// # });
362 /// ```
363 pub fn is_some(&self) -> Expr<'column, S, bool> {
364 let val = self.inner.clone();
365 Expr::adhoc(move |b| val.build_expr(b).is_not_null())
366 }
367
368 /// Check that the expression is [None].
369 ///
370 /// ```
371 /// # use rust_query::IntoExpr;
372 /// # rust_query::private::doctest::get_txn(|txn| {
373 /// assert_eq!(txn.query_one(Some(10).into_expr().is_none()), false);
374 /// assert_eq!(txn.query_one(None::<i64>.into_expr().is_none()), true);
375 /// # });
376 /// ```
377 pub fn is_none(&self) -> Expr<'column, S, bool> {
378 let val = self.inner.clone();
379 Expr::adhoc(move |b| val.build_expr(b).is_null())
380 }
381}
382
383impl<'column, S> Expr<'column, S, i64> {
384 /// Convert the [i64] expression to [f64] type.
385 ///
386 /// The conversion may not be lossless for integers larger than 2^53 or smaller than (-2^53).
387 ///
388 /// ```
389 /// # use rust_query::IntoExpr;
390 /// # rust_query::private::doctest::get_txn(|txn| {
391 /// assert_eq!(txn.query_one(10.into_expr().to_f64()), 10.0);
392 /// # });
393 /// ```
394 pub fn to_f64(&self) -> Expr<'column, S, f64> {
395 let val = self.inner.clone();
396 Expr::adhoc(move |b| val.build_expr(b).cast_as(Alias::new("REAL")))
397 }
398
399 /// Calculate the remainder for integer division.
400 ///
401 /// The remainder is the missing part after division.
402 ///
403 /// ```
404 /// # use rust_query::IntoExpr;
405 /// # rust_query::private::doctest::get_txn(|txn| {
406 /// assert_eq!(txn.query_one(5.into_expr().div(3)), 1);
407 /// assert_eq!(txn.query_one(5.into_expr().modulo(3)), 2);
408 /// assert_eq!(txn.query_one((-5).into_expr().div(3)), -1);
409 /// assert_eq!(txn.query_one((-5).into_expr().modulo(3)), -2);
410 /// # });
411 /// ```
412 pub fn modulo(&self, rhs: impl IntoExpr<'column, S, Typ = i64>) -> Self {
413 let lhs = self.inner.clone();
414 let rhs = rhs.into_expr().inner;
415 Expr::adhoc(move |b| lhs.build_expr(b).modulo(rhs.build_expr(b)))
416 }
417}
418
419impl<'column, S> Expr<'column, S, f64> {
420 /// Convert the [f64] expression to [i64] type.
421 ///
422 /// Always rounds towards zero for floats that are not already an integer.
423 ///
424 /// Values outside the range `i64::MIN..=i64::MAX`, will be converted to
425 /// the closest integer in that range.
426 ///
427 /// ```
428 /// # use rust_query::IntoExpr;
429 /// # rust_query::private::doctest::get_txn(|txn| {
430 /// assert_eq!(txn.query_one(10.9.into_expr().to_i64()), 10);
431 /// assert_eq!(txn.query_one((-10.9).into_expr().to_i64()), -10);
432 /// assert_eq!(txn.query_one((342143124.0).into_expr().to_i64()), 342143124);
433 /// assert_eq!(txn.query_one((f64::MIN).into_expr().to_i64()), i64::MIN);
434 /// assert_eq!(txn.query_one((f64::NEG_INFINITY).into_expr().to_i64()), i64::MIN);
435 /// # });
436 /// ```
437 pub fn to_i64(&self) -> Expr<'column, S, i64> {
438 let val = self.inner.clone();
439 Expr::adhoc(move |b| val.build_expr(b).cast_as(Alias::new("INTEGER")))
440 }
441
442 /// Round the [f64] expression down.
443 ///
444 /// ```
445 /// # use rust_query::IntoExpr;
446 /// # rust_query::private::doctest::get_txn(|txn| {
447 /// assert_eq!(txn.query_one(10.9.into_expr().floor()), 10.0);
448 /// assert_eq!(txn.query_one((-10.9).into_expr().floor()), -11.0);
449 /// assert_eq!(txn.query_one((f64::MIN).into_expr().floor()), f64::MIN);
450 /// assert_eq!(txn.query_one((f64::NEG_INFINITY).into_expr().floor()), f64::NEG_INFINITY);
451 /// # });
452 /// ```
453 pub fn floor(&self) -> Expr<'column, S, f64> {
454 let val = self.inner.clone();
455 Expr::adhoc(move |b| sea_query::Func::cust("floor").arg(val.build_expr(b)).into())
456 }
457
458 /// Round the [f64] expression up.
459 ///
460 /// ```
461 /// # use rust_query::IntoExpr;
462 /// # rust_query::private::doctest::get_txn(|txn| {
463 /// assert_eq!(txn.query_one(10.9.into_expr().ceil()), 11.0);
464 /// assert_eq!(txn.query_one((-10.9).into_expr().ceil()), -10.0);
465 /// assert_eq!(txn.query_one((f64::MIN).into_expr().ceil()), f64::MIN);
466 /// assert_eq!(txn.query_one((f64::NEG_INFINITY).into_expr().ceil()), f64::NEG_INFINITY);
467 /// # });
468 /// ```
469 pub fn ceil(&self) -> Expr<'column, S, f64> {
470 let val = self.inner.clone();
471 Expr::adhoc(move |b| sea_query::Func::cust("ceil").arg(val.build_expr(b)).into())
472 }
473
474 /// Round the [f64] expression to the specified precision.
475 ///
476 /// Precision specifies the number of decimal places to keep.
477 /// A negative precision is treated as zero.
478 ///
479 /// ```
480 /// # use rust_query::IntoExpr;
481 /// # rust_query::private::doctest::get_txn(|txn| {
482 /// assert_eq!(txn.query_one(10.85.into_expr().round_with_precision(0)), 11.0);
483 /// assert_eq!(txn.query_one((-10.85).into_expr().round_with_precision(0)), -11.0);
484 /// assert_eq!(txn.query_one(10.85.into_expr().round_with_precision(1)), 10.8);
485 /// assert_eq!(txn.query_one((-10.85).into_expr().round_with_precision(1)), -10.8);
486 /// assert_eq!(txn.query_one((f64::MIN).into_expr().round_with_precision(0)), f64::MIN);
487 /// assert_eq!(txn.query_one((f64::NEG_INFINITY).into_expr().round_with_precision(0)), f64::NEG_INFINITY);
488 /// # });
489 /// ```
490 pub fn round_with_precision(&self, precision: impl IntoExpr<'column, S, Typ = i64>) -> Self {
491 let val = self.inner.clone();
492 let precision = precision.into_expr().inner;
493 Expr::adhoc(move |b| {
494 sea_query::Func::round_with_precision(val.build_expr(b), precision.build_expr(b)).into()
495 })
496 }
497}
498
499impl<'column, S> Expr<'column, S, String> {
500 /// Check if the expression starts with the string pattern.
501 ///
502 /// Matches case-sensitive. The pattern gets automatically escaped.
503 ///
504 /// ```
505 /// # use rust_query::IntoExpr;
506 /// # rust_query::private::doctest::get_txn(|txn| {
507 /// assert_eq!(txn.query_one("hello world".into_expr().starts_with("hello")), true);
508 /// assert_eq!(txn.query_one("hello world".into_expr().starts_with("Hello")), false);
509 /// # });
510 /// ```
511 pub fn starts_with(&self, pattern: impl AsRef<str>) -> Expr<'column, S, bool> {
512 self.glob(format!("{}*", escape_glob(pattern)))
513 }
514
515 /// Check if the expression ends with the string pattern.
516 ///
517 /// Matches case-sensitive. The pattern gets automatically escaped.
518 ///
519 /// ```
520 /// # use rust_query::IntoExpr;
521 /// # rust_query::private::doctest::get_txn(|txn| {
522 /// assert_eq!(txn.query_one("hello world".into_expr().ends_with("world")), true);
523 /// assert_eq!(txn.query_one("hello world".into_expr().ends_with("World")), false);
524 /// # });
525 /// ```
526 pub fn ends_with(&self, pattern: impl AsRef<str>) -> Expr<'column, S, bool> {
527 self.glob(format!("*{}", escape_glob(pattern)))
528 }
529
530 /// Check if the expression contains the string pattern.
531 ///
532 /// Matches case-sensitive. The pattern gets automatically escaped.
533 ///
534 /// ```
535 /// # use rust_query::IntoExpr;
536 /// # rust_query::private::doctest::get_txn(|txn| {
537 /// assert_eq!(txn.query_one("rhubarb".into_expr().contains("bar")), true);
538 /// assert_eq!(txn.query_one("rhubarb".into_expr().contains("Bar")), false);
539 /// # });
540 /// ```
541 #[doc(alias = "instr")]
542 pub fn contains(&self, rhs: impl IntoExpr<'column, S, Typ = String>) -> Expr<'column, S, bool> {
543 let lhs = self.inner.clone();
544 let rhs = rhs.into_expr().inner;
545 Expr::adhoc(move |b| {
546 sea_query::Expr::expr(
547 sea_query::Func::cust("instr")
548 .arg(lhs.build_expr(b))
549 .arg(rhs.build_expr(b)),
550 )
551 .is_not(CONST_0)
552 })
553 }
554
555 /// Replace all occurences of a string with another string.
556 ///
557 /// ```
558 /// # use rust_query::IntoExpr;
559 /// # rust_query::private::doctest::get_txn(|txn| {
560 /// assert_eq!(txn.query_one("very,cool,list".into_expr().replace(",", "::")), "very::cool::list");
561 /// assert_eq!(txn.query_one("rarar".into_expr().replace("rar", "rer")), "rerar");
562 /// # });
563 /// ```
564 pub fn replace(
565 &self,
566 pattern: impl IntoExpr<'column, S, Typ = String>,
567 new: impl IntoExpr<'column, S, Typ = String>,
568 ) -> Self {
569 let lhs = self.inner.clone();
570 let pat = pattern.into_expr().inner;
571 let new = new.into_expr().inner;
572 Expr::adhoc(move |b| {
573 sea_query::Func::cust("replace")
574 .args([lhs.build_expr(b), pat.build_expr(b), new.build_expr(b)])
575 .into()
576 })
577 }
578
579 /// Removes all characters from a set from the start of a string.
580 ///
581 /// ```
582 /// # use rust_query::IntoExpr;
583 /// # rust_query::private::doctest::get_txn(|txn| {
584 /// assert_eq!(txn.query_one("abacda".into_expr().ltrim("ab")), "cda");
585 /// # });
586 /// ```
587 pub fn ltrim(&self, char_set: impl IntoExpr<'column, S, Typ = String>) -> Self {
588 let lhs = self.inner.clone();
589 let char_set = char_set.into_expr().inner;
590 Expr::adhoc(move |b| {
591 sea_query::Func::cust("ltrim")
592 .args([lhs.build_expr(b), char_set.build_expr(b)])
593 .into()
594 })
595 }
596
597 /// Removes all characters from a set from the end of a string.
598 ///
599 /// ```
600 /// # use rust_query::IntoExpr;
601 /// # rust_query::private::doctest::get_txn(|txn| {
602 /// assert_eq!(txn.query_one("adcaba".into_expr().rtrim("ab")), "adc");
603 /// # });
604 /// ```
605 pub fn rtrim(&self, char_set: impl IntoExpr<'column, S, Typ = String>) -> Self {
606 let lhs = self.inner.clone();
607 let char_set = char_set.into_expr().inner;
608 Expr::adhoc(move |b| {
609 sea_query::Func::cust("rtrim")
610 .args([lhs.build_expr(b), char_set.build_expr(b)])
611 .into()
612 })
613 }
614
615 /// Removes all characters from a set from both the start and end of a string.
616 ///
617 /// ```
618 /// # use rust_query::IntoExpr;
619 /// # rust_query::private::doctest::get_txn(|txn| {
620 /// assert_eq!(txn.query_one("ffzfzf".into_expr().trim("f")), "zfz");
621 /// # });
622 /// ```
623 pub fn trim(&self, char_set: impl IntoExpr<'column, S, Typ = String>) -> Self {
624 let lhs = self.inner.clone();
625 let char_set = char_set.into_expr().inner;
626 Expr::adhoc(move |b| {
627 sea_query::Func::cust("trim")
628 .args([lhs.build_expr(b), char_set.build_expr(b)])
629 .into()
630 })
631 }
632
633 /// Check if the expression matches the pattern [sqlite docs](https://www.sqlite.org/lang_expr.html#like).
634 ///
635 /// This is a case-sensitive version of [like](Self::like). It uses Unix file globbing syntax for wild
636 /// cards. `*` matches any sequence of characters and `?` matches any single character. `[0-9]` matches
637 /// any single digit and `[a-z]` matches any single lowercase letter. `^` negates the pattern.
638 ///
639 /// ```
640 /// # use rust_query::IntoExpr;
641 /// # rust_query::private::doctest::get_txn(|txn| {
642 /// assert_eq!(txn.query_one("hello world".into_expr().glob("?ello*")), true);
643 /// assert_eq!(txn.query_one("hello world".into_expr().glob("Hell*")), false);
644 /// # });
645 /// ```
646 pub fn glob(&self, rhs: impl IntoExpr<'column, S, Typ = String>) -> Expr<'column, S, bool> {
647 let lhs = self.inner.clone();
648 let rhs = rhs.into_expr().inner;
649 Expr::adhoc(move |b| sea_query::Expr::expr(lhs.build_expr(b)).glob(rhs.build_expr(b)))
650 }
651
652 /// Check if the expression matches the pattern [sqlite docs](https://www.sqlite.org/lang_expr.html#like).
653 ///
654 /// As noted in the docs, it is **case-insensitive** for ASCII characters. Other characters are case-sensitive.
655 /// For creating patterns it uses `%` as a wildcard for any sequence of characters and `_` for any single character.
656 /// Special characters should be escaped with `\`.
657 ///
658 /// ```
659 /// # use rust_query::IntoExpr;
660 /// # rust_query::private::doctest::get_txn(|txn| {
661 /// assert_eq!(txn.query_one("hello world".into_expr().like("HELLO%")), true);
662 /// assert_eq!(txn.query_one("hello world".into_expr().like("he_o%")), false);
663 /// # });
664 /// ```
665 pub fn like(&self, pattern: impl Into<String>) -> Expr<'column, S, bool> {
666 let lhs = self.inner.clone();
667 let rhs = pattern.into();
668 Expr::adhoc(move |b| {
669 sea_query::Expr::expr(lhs.build_expr(b))
670 .like(sea_query::LikeExpr::new(&rhs).escape('\\'))
671 })
672 }
673
674 /// Concatenate two strings.
675 ///
676 /// ```
677 /// # use rust_query::IntoExpr;
678 /// # rust_query::private::doctest::get_txn(|txn| {
679 /// assert_eq!(txn.query_one("hello ".into_expr().concat("world").concat("!")), "hello world!");
680 /// # });
681 /// ```
682 pub fn concat(&self, rhs: impl IntoExpr<'column, S, Typ = String>) -> Self {
683 let lhs = self.inner.clone();
684 let rhs = rhs.into_expr().inner;
685 Expr::adhoc(move |b| {
686 sea_query::Expr::expr(
687 sea_query::Func::cust("concat")
688 .arg(lhs.build_expr(b))
689 .arg(rhs.build_expr(b)),
690 )
691 })
692 }
693
694 /// Convert ascii to lowercase.
695 ///
696 /// ```
697 /// # use rust_query::IntoExpr;
698 /// # rust_query::private::doctest::get_txn(|txn| {
699 /// assert_eq!(txn.query_one("Hello".into_expr().lower()), "hello");
700 /// assert_eq!(txn.query_one("WHAT".into_expr().lower()), "what");
701 /// # });
702 /// ```
703 pub fn lower(&self) -> Self {
704 let lhs = self.inner.clone();
705 Expr::adhoc(move |b| {
706 sea_query::Expr::expr(sea_query::Func::cust("lower").arg(lhs.build_expr(b)))
707 })
708 }
709
710 /// Convert ascii to uppercase.
711 ///
712 /// ```
713 /// # use rust_query::IntoExpr;
714 /// # rust_query::private::doctest::get_txn(|txn| {
715 /// assert_eq!(txn.query_one("Hello".into_expr().upper()), "HELLO");
716 /// assert_eq!(txn.query_one("what".into_expr().upper()), "WHAT");
717 /// # });
718 /// ```
719 pub fn upper(&self) -> Self {
720 let lhs = self.inner.clone();
721 Expr::adhoc(move |b| {
722 sea_query::Expr::expr(sea_query::Func::cust("upper").arg(lhs.build_expr(b)))
723 })
724 }
725
726 /// The number of unicode code points in the string.
727 ///
728 /// ```
729 /// # use rust_query::IntoExpr;
730 /// # rust_query::private::doctest::get_txn(|txn| {
731 /// assert_eq!(txn.query_one("€".into_expr().char_len()), 1);
732 /// assert_eq!(txn.query_one("what".into_expr().char_len()), 4);
733 /// # });
734 /// ```
735 pub fn char_len(&self) -> Expr<'column, S, i64> {
736 let lhs = self.inner.clone();
737 Expr::adhoc(move |b| {
738 sea_query::Expr::expr(sea_query::Func::cust("length").arg(lhs.build_expr(b)))
739 })
740 }
741}
742
743impl<'column, S, T: BuffTyp> Expr<'column, S, T> {
744 /// The length of the value in bytes.
745 ///
746 /// The byte length of strings can depend on the encoding (UTF-8 or UTF-16).
747 ///
748 /// ```
749 /// # use rust_query::IntoExpr;
750 /// # rust_query::private::doctest::get_txn(|txn| {
751 /// assert_eq!(txn.query_one("€".into_expr().byte_len()), 3);
752 /// assert_eq!(txn.query_one("what".into_expr().byte_len()), 4);
753 /// assert_eq!(txn.query_one(vec![1, 2].into_expr().byte_len()), 2);
754 /// # });
755 /// ```
756 pub fn byte_len(&self) -> Expr<'column, S, i64> {
757 let lhs = self.inner.clone();
758 Expr::adhoc(move |b| {
759 sea_query::Expr::expr(sea_query::Func::cust("octet_length").arg(lhs.build_expr(b)))
760 })
761 }
762}
763
764impl<'column, S> Expr<'column, S, Vec<u8>> {
765 /// Create a new blob of zero bytes of the specified length.
766 ///
767 /// ```
768 /// # use rust_query::Expr;
769 /// # rust_query::private::doctest::get_txn(|txn| {
770 /// assert_eq!(txn.query_one(Expr::zero_blob(40)), vec![0; 40]);
771 /// # });
772 /// ```
773 pub fn zero_blob(len: impl IntoExpr<'column, S, Typ = i64>) -> Self {
774 let len = len.into_expr().inner;
775 Expr::adhoc(move |b| {
776 sea_query::Expr::expr(sea_query::Func::cust("zeroblob").arg(len.build_expr(b)))
777 })
778 }
779}
780
781// This is a copy of the function from the glob crate https://github.com/rust-lang/glob/blob/49ee1e92bd6e8c5854c0b339634f9b4b733aba4f/src/lib.rs#L720-L737.
782fn escape_glob(s: impl AsRef<str>) -> String {
783 let mut escaped = String::new();
784 for c in s.as_ref().chars() {
785 match c {
786 // note that ! does not need escaping because it is only special
787 // inside brackets
788 '?' | '*' | '[' | ']' => {
789 escaped.push('[');
790 escaped.push(c);
791 escaped.push(']');
792 }
793 c => {
794 escaped.push(c);
795 }
796 }
797 }
798 escaped
799}