Skip to main content

rust_query/value/
operations.rs

1use sea_query::{Alias, ExprTrait, extension::sqlite::SqliteExpr};
2
3use crate::{ast::CONST_0, value::BuffTyp};
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>) -> Self {
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>) -> Self {
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>) -> Self {
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    /// Divide one expression by another.
54    ///
55    /// For integers, the result is truncated towards zero.
56    /// See also [Expr::modulo].
57    ///
58    /// ```
59    /// # use rust_query::IntoExpr;
60    /// # rust_query::private::doctest::get_txn(|txn| {
61    /// assert_eq!(txn.query_one(5.into_expr().div(3)), 1);
62    /// assert_eq!(txn.query_one((-5).into_expr().div(3)), -1);
63    /// assert_eq!(txn.query_one(1.0.into_expr().div(2.0)), 0.5);
64    /// # });
65    /// ```
66    pub fn div(&self, rhs: impl IntoExpr<'column, S, Typ = T>) -> Self {
67        let lhs = self.inner.clone();
68        let rhs = rhs.into_expr().inner;
69        Expr::adhoc(move |b| lhs.build_expr(b).div(rhs.build_expr(b)))
70    }
71
72    /// Compute the less than operator (<) of two expressions.
73    ///
74    /// ```
75    /// # use rust_query::IntoExpr;
76    /// # rust_query::private::doctest::get_txn(|txn| {
77    /// assert_eq!(txn.query_one(2.into_expr().lt(3)), true);
78    /// assert_eq!(txn.query_one(1.into_expr().lt(1)), false);
79    /// assert_eq!(txn.query_one(3.0.into_expr().lt(1.0)), false);
80    /// # });
81    /// ```
82    pub fn lt(&self, rhs: impl IntoExpr<'column, S, Typ = T>) -> Expr<'column, S, bool> {
83        let lhs = self.inner.clone();
84        let rhs = rhs.into_expr().inner;
85        Expr::adhoc(move |b| lhs.build_expr(b).lt(rhs.build_expr(b)))
86    }
87
88    /// Compute the less than or equal operator (<=) of two expressions.
89    ///
90    /// ```
91    /// # use rust_query::IntoExpr;
92    /// # rust_query::private::doctest::get_txn(|txn| {
93    /// assert_eq!(txn.query_one(2.into_expr().lte(2)), true);
94    /// assert_eq!(txn.query_one(3.0.into_expr().lte(1.0)), false);
95    /// # });
96    /// ```
97    pub fn lte(&self, rhs: impl IntoExpr<'column, S, Typ = T>) -> Expr<'column, S, bool> {
98        let lhs = self.inner.clone();
99        let rhs = rhs.into_expr().inner;
100        Expr::adhoc(move |b| lhs.build_expr(b).lte(rhs.build_expr(b)))
101    }
102
103    /// Compute the greater than operator (>) of two expressions.
104    ///
105    /// ```
106    /// # use rust_query::IntoExpr;
107    /// # rust_query::private::doctest::get_txn(|txn| {
108    /// assert_eq!(txn.query_one(2.into_expr().gt(2)), false);
109    /// assert_eq!(txn.query_one(3.0.into_expr().gt(1.0)), true);
110    /// # });
111    /// ```
112    pub fn gt(&self, rhs: impl IntoExpr<'column, S, Typ = T>) -> Expr<'column, S, bool> {
113        let lhs = self.inner.clone();
114        let rhs = rhs.into_expr().inner;
115        Expr::adhoc(move |b| lhs.build_expr(b).gt(rhs.build_expr(b)))
116    }
117
118    /// Compute the greater than or equal (>=) operator of two expressions.
119    ///
120    /// ```
121    /// # use rust_query::IntoExpr;
122    /// # rust_query::private::doctest::get_txn(|txn| {
123    /// assert_eq!(txn.query_one(2.into_expr().gte(3)), false);
124    /// assert_eq!(txn.query_one(3.0.into_expr().gte(3.0)), true);
125    /// # });
126    /// ```
127    pub fn gte(&self, rhs: impl IntoExpr<'column, S, Typ = T>) -> Expr<'column, S, bool> {
128        let lhs = self.inner.clone();
129        let rhs = rhs.into_expr().inner;
130        Expr::adhoc(move |b| lhs.build_expr(b).gte(rhs.build_expr(b)))
131    }
132
133    /// Get the maximum of two values.
134    ///
135    /// ```
136    /// # use rust_query::IntoExpr;
137    /// # rust_query::private::doctest::get_txn(|txn| {
138    /// assert_eq!(txn.query_one(2.into_expr().max(3)), 3);
139    /// assert_eq!(txn.query_one(5.0.into_expr().max(3.0)), 5.0);
140    /// # });
141    /// ```
142    pub fn max(&self, rhs: impl IntoExpr<'column, S, Typ = T>) -> Self {
143        let lhs = self.inner.clone();
144        let rhs = rhs.into_expr().inner;
145        Expr::adhoc(move |b| {
146            sea_query::Expr::expr(
147                sea_query::Func::cust("max")
148                    .arg(lhs.build_expr(b))
149                    .arg(rhs.build_expr(b)),
150            )
151        })
152    }
153
154    /// Get the minimum of two values.
155    ///
156    /// ```
157    /// # use rust_query::IntoExpr;
158    /// # rust_query::private::doctest::get_txn(|txn| {
159    /// assert_eq!(txn.query_one(2.into_expr().min(3)), 2);
160    /// assert_eq!(txn.query_one(5.0.into_expr().min(3.0)), 3.0);
161    /// # });
162    /// ```
163    pub fn min(&self, rhs: impl IntoExpr<'column, S, Typ = T>) -> Self {
164        let lhs = self.inner.clone();
165        let rhs = rhs.into_expr().inner;
166        Expr::adhoc(move |b| {
167            sea_query::Expr::expr(
168                sea_query::Func::cust("min")
169                    .arg(lhs.build_expr(b))
170                    .arg(rhs.build_expr(b)),
171            )
172        })
173    }
174
175    /// Get the sign of the expression.
176    ///
177    /// The result is -1, 0 or 1 depending on if the expression is
178    /// negative, zero or positive.
179    ///
180    /// ```
181    /// # use rust_query::IntoExpr;
182    /// # rust_query::private::doctest::get_txn(|txn| {
183    /// assert_eq!(txn.query_one(2.into_expr().sign()), 1);
184    /// assert_eq!(txn.query_one((-5.0).into_expr().sign()), -1);
185    /// assert_eq!(txn.query_one((-0.0).into_expr().sign()), 0);
186    /// # });
187    /// ```
188    pub fn sign(&self) -> Expr<'column, S, i64> {
189        let lhs = self.inner.clone();
190        Expr::adhoc(move |b| {
191            sea_query::Expr::expr(sea_query::Func::cust("sign").arg(lhs.build_expr(b)))
192        })
193    }
194
195    /// Get the absolute value of the expression.
196    ///
197    /// ```
198    /// # use rust_query::IntoExpr;
199    /// # rust_query::private::doctest::get_txn(|txn| {
200    /// assert_eq!(txn.query_one(2.into_expr().abs()), 2);
201    /// assert_eq!(txn.query_one((-5.0).into_expr().abs()), 5.0);
202    /// # });
203    /// ```
204    pub fn abs(&self) -> Self {
205        let lhs = self.inner.clone();
206        Expr::adhoc(move |b| {
207            sea_query::Expr::expr(sea_query::Func::cust("abs").arg(lhs.build_expr(b)))
208        })
209    }
210
211    /// Check if a value is between two other values.
212    ///
213    /// The range is inclusive on both sides.
214    ///
215    /// ```
216    /// # use rust_query::IntoExpr;
217    /// # rust_query::private::doctest::get_txn(|txn| {
218    /// assert_eq!(txn.query_one(2.into_expr().between(2, 3)), true);
219    /// assert_eq!(txn.query_one(3.into_expr().between(2, 3)), true);
220    /// assert_eq!(txn.query_one(5.into_expr().between(2, 3)), false);
221    /// assert_eq!(txn.query_one(1.into_expr().between(2, 3)), false);
222    /// # });
223    /// ```
224    pub fn between(
225        &self,
226        low: impl IntoExpr<'column, S, Typ = T>,
227        high: impl IntoExpr<'column, S, Typ = T>,
228    ) -> Expr<'column, S, bool> {
229        let lhs = self.inner.clone();
230        let low = low.into_expr().inner;
231        let high = high.into_expr().inner;
232        Expr::adhoc(move |b| {
233            lhs.build_expr(b)
234                .between(low.build_expr(b), high.build_expr(b))
235        })
236    }
237}
238
239impl<'column, S, T: EqTyp + 'static> Expr<'column, S, T> {
240    /// Check whether two expressions are equal.
241    ///
242    /// ```
243    /// # use rust_query::IntoExpr;
244    /// # rust_query::private::doctest::get_txn(|txn| {
245    /// assert_eq!(txn.query_one(2.into_expr().eq(2)), true);
246    /// assert_eq!(txn.query_one(3.0.into_expr().eq(3.0)), true);
247    /// assert_eq!(txn.query_one("test".into_expr().eq("test")), true);
248    /// assert_eq!(txn.query_one(b"test".into_expr().eq(b"test" as &[u8])), true);
249    /// assert_eq!(txn.query_one(false.into_expr().eq(false)), true);
250    ///
251    /// assert_eq!(txn.query_one(1.into_expr().eq(2)), false);
252    /// # });
253    /// ```
254    pub fn eq(&self, rhs: impl IntoExpr<'column, S, Typ = T>) -> Expr<'column, S, bool> {
255        let lhs = self.inner.clone();
256        let rhs = rhs.into_expr().inner;
257        Expr::adhoc(move |b| lhs.build_expr(b).is(rhs.build_expr(b)))
258    }
259
260    /// Check whether two expressions are not equal.
261    ///
262    /// ```
263    /// # use rust_query::IntoExpr;
264    /// # rust_query::private::doctest::get_txn(|txn| {
265    /// assert_eq!(txn.query_one(2.into_expr().neq(2)), false);
266    /// assert_eq!(txn.query_one(3.0.into_expr().neq(3.1)), true);
267    /// assert_eq!(txn.query_one("test".into_expr().neq("test")), false);
268    /// assert_eq!(txn.query_one(b"test".into_expr().neq(b"test" as &[u8])), false);
269    /// assert_eq!(txn.query_one(false.into_expr().neq(false)), false);
270    ///
271    /// assert_eq!(txn.query_one(1.into_expr().neq(2)), true);
272    /// # });
273    /// ```
274    pub fn neq(&self, rhs: impl IntoExpr<'column, S, Typ = T>) -> Expr<'column, S, bool> {
275        let lhs = self.inner.clone();
276        let rhs = rhs.into_expr().inner;
277        Expr::adhoc(move |b| lhs.build_expr(b).is_not(rhs.build_expr(b)))
278    }
279}
280
281impl<'column, S> Expr<'column, S, bool> {
282    /// Checks whether an expression is false.
283    ///
284    /// ```
285    /// # use rust_query::IntoExpr;
286    /// # rust_query::private::doctest::get_txn(|txn| {
287    /// assert_eq!(txn.query_one(true.into_expr().not()), false);
288    /// assert_eq!(txn.query_one(false.into_expr().not()), true);
289    /// # });
290    /// ```
291    pub fn not(&self) -> Self {
292        let val = self.inner.clone();
293        Expr::adhoc(move |b| val.build_expr(b).not())
294    }
295
296    /// Check if two expressions are both true.
297    ///
298    /// ```
299    /// # use rust_query::IntoExpr;
300    /// # rust_query::private::doctest::get_txn(|txn| {
301    /// assert_eq!(txn.query_one(true.into_expr().and(true)), true);
302    /// assert_eq!(txn.query_one(false.into_expr().and(true)), false);
303    /// assert_eq!(txn.query_one(false.into_expr().and(false)), false);
304    /// # });
305    /// ```
306    pub fn and(&self, rhs: impl IntoExpr<'column, S, Typ = bool>) -> Self {
307        let lhs = self.inner.clone();
308        let rhs = rhs.into_expr().inner;
309        Expr::adhoc(move |b| lhs.build_expr(b).and(rhs.build_expr(b)))
310    }
311
312    /// Check if one of two expressions is true.
313    ///
314    /// ```
315    /// # use rust_query::IntoExpr;
316    /// # rust_query::private::doctest::get_txn(|txn| {
317    /// assert_eq!(txn.query_one(true.into_expr().or(true)), true);
318    /// assert_eq!(txn.query_one(false.into_expr().or(true)), true);
319    /// assert_eq!(txn.query_one(false.into_expr().or(false)), false);
320    /// # });
321    /// ```
322    pub fn or(&self, rhs: impl IntoExpr<'column, S, Typ = bool>) -> Self {
323        let lhs = self.inner.clone();
324        let rhs = rhs.into_expr().inner;
325        Expr::adhoc(move |b| lhs.build_expr(b).or(rhs.build_expr(b)))
326    }
327}
328
329impl<'column, S, Typ: EqTyp> Expr<'column, S, Option<Typ>> {
330    /// Use the first expression if it is [Some], otherwise use the second expression.
331    ///
332    /// ```
333    /// # use rust_query::IntoExpr;
334    /// # rust_query::private::doctest::get_txn(|txn| {
335    /// assert_eq!(txn.query_one(Some(10).into_expr().unwrap_or(5)), 10);
336    /// assert_eq!(txn.query_one(None::<String>.into_expr().unwrap_or("foo")), "foo");
337    /// # });
338    /// ```
339    pub fn unwrap_or(&self, rhs: impl IntoExpr<'column, S, Typ = Typ>) -> Expr<'column, S, Typ> {
340        let lhs = self.inner.clone();
341        let rhs = rhs.into_expr().inner;
342        let maybe_optional = rhs.maybe_optional;
343        Expr::adhoc_promise(
344            move |b| sea_query::Expr::expr(lhs.build_expr(b)).if_null(rhs.build_expr(b)),
345            maybe_optional,
346        )
347    }
348
349    /// Check that the expression is [Some].
350    ///
351    /// ```
352    /// # use rust_query::IntoExpr;
353    /// # rust_query::private::doctest::get_txn(|txn| {
354    /// assert_eq!(txn.query_one(Some(10).into_expr().is_some()), true);
355    /// assert_eq!(txn.query_one(None::<i64>.into_expr().is_some()), false);
356    /// # });
357    /// ```
358    pub fn is_some(&self) -> Expr<'column, S, bool> {
359        let val = self.inner.clone();
360        Expr::adhoc(move |b| val.build_expr(b).is_not_null())
361    }
362
363    /// Check that the expression is [None].
364    ///
365    /// ```
366    /// # use rust_query::IntoExpr;
367    /// # rust_query::private::doctest::get_txn(|txn| {
368    /// assert_eq!(txn.query_one(Some(10).into_expr().is_none()), false);
369    /// assert_eq!(txn.query_one(None::<i64>.into_expr().is_none()), true);
370    /// # });
371    /// ```
372    pub fn is_none(&self) -> Expr<'column, S, bool> {
373        let val = self.inner.clone();
374        Expr::adhoc(move |b| val.build_expr(b).is_null())
375    }
376}
377
378impl<'column, S> Expr<'column, S, i64> {
379    /// Convert the [i64] expression to [f64] type.
380    ///
381    /// The conversion may not be lossless for integers larger than 2^53 or smaller than (-2^53).
382    ///
383    /// ```
384    /// # use rust_query::IntoExpr;
385    /// # rust_query::private::doctest::get_txn(|txn| {
386    /// assert_eq!(txn.query_one(10.into_expr().to_f64()), 10.0);
387    /// # });
388    /// ```
389    pub fn to_f64(&self) -> Expr<'column, S, f64> {
390        let val = self.inner.clone();
391        Expr::adhoc(move |b| val.build_expr(b).cast_as(Alias::new("REAL")))
392    }
393
394    /// Calculate the remainder for integer division.
395    ///
396    /// The remainder is the missing part after division.
397    ///
398    /// ```
399    /// # use rust_query::IntoExpr;
400    /// # rust_query::private::doctest::get_txn(|txn| {
401    /// assert_eq!(txn.query_one(5.into_expr().div(3)), 1);
402    /// assert_eq!(txn.query_one(5.into_expr().modulo(3)), 2);
403    /// assert_eq!(txn.query_one((-5).into_expr().div(3)), -1);
404    /// assert_eq!(txn.query_one((-5).into_expr().modulo(3)), -2);
405    /// # });
406    /// ```
407    pub fn modulo(&self, rhs: impl IntoExpr<'column, S, Typ = i64>) -> Self {
408        let lhs = self.inner.clone();
409        let rhs = rhs.into_expr().inner;
410        Expr::adhoc(move |b| lhs.build_expr(b).modulo(rhs.build_expr(b)))
411    }
412
413    /// Get the current timestamp as milliseconds since unix epoch.
414    pub fn unix_epoch() -> Self {
415        Expr::adhoc(|_| sea_query::Expr::cust("unixepoch('now')"))
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}