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