Skip to main content

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}