rust_query/value/operations.rs
1use sea_query::{Alias, ExprTrait, extension::sqlite::SqliteExpr};
2
3use super::{EqTyp, Expr, IntoExpr, NumTyp, Typed};
4
5impl<'column, S, T: NumTyp> Expr<'column, S, T> {
6 /// Add two expressions together.
7 ///
8 /// ```
9 /// # use rust_query::IntoExpr;
10 /// # let mut client = rust_query::private::doctest::get_client();
11 /// # let txn = rust_query::private::doctest::get_txn(&mut client);
12 /// assert_eq!(txn.query_one(1.into_expr().add(2)), 3);
13 /// assert_eq!(txn.query_one(1.0.into_expr().add(2.0)), 3.0);
14 /// ```
15 pub fn add(&self, rhs: impl IntoExpr<'column, S, Typ = T>) -> Expr<'column, S, T> {
16 let lhs = self.inner.clone();
17 let rhs = rhs.into_expr().inner;
18 Expr::adhoc(move |b| lhs.build_expr(b).add(rhs.build_expr(b)))
19 }
20
21 /// Subtract one expression from another.
22 ///
23 /// ```
24 /// # use rust_query::IntoExpr;
25 /// # let mut client = rust_query::private::doctest::get_client();
26 /// # let txn = rust_query::private::doctest::get_txn(&mut client);
27 /// assert_eq!(txn.query_one(1.into_expr().sub(2)), -1);
28 /// assert_eq!(txn.query_one(1.0.into_expr().sub(2.0)), -1.0);
29 /// ```
30 pub fn sub(&self, rhs: impl IntoExpr<'column, S, Typ = T>) -> Expr<'column, S, T> {
31 let lhs = self.inner.clone();
32 let rhs = rhs.into_expr().inner;
33 Expr::adhoc(move |b| lhs.build_expr(b).sub(rhs.build_expr(b)))
34 }
35
36 /// Multiply two expressions together.
37 ///
38 /// ```
39 /// # use rust_query::IntoExpr;
40 /// # let mut client = rust_query::private::doctest::get_client();
41 /// # let txn = rust_query::private::doctest::get_txn(&mut client);
42 /// assert_eq!(txn.query_one(2.into_expr().mul(3)), 6);
43 /// assert_eq!(txn.query_one(2.0.into_expr().mul(3.0)), 6.0);
44 /// ```
45 pub fn mul(&self, rhs: impl IntoExpr<'column, S, Typ = T>) -> Expr<'column, S, T> {
46 let lhs = self.inner.clone();
47 let rhs = rhs.into_expr().inner;
48 Expr::adhoc(move |b| lhs.build_expr(b).mul(rhs.build_expr(b)))
49 }
50
51 /// Compute the less than operator (<) of two expressions.
52 ///
53 /// ```
54 /// # use rust_query::IntoExpr;
55 /// # let mut client = rust_query::private::doctest::get_client();
56 /// # let txn = rust_query::private::doctest::get_txn(&mut client);
57 /// assert_eq!(txn.query_one(2.into_expr().lt(3)), true);
58 /// assert_eq!(txn.query_one(1.into_expr().lt(1)), false);
59 /// assert_eq!(txn.query_one(3.0.into_expr().lt(1.0)), false);
60 /// ```
61 pub fn lt(&self, rhs: impl IntoExpr<'column, S, Typ = T>) -> Expr<'column, S, bool> {
62 let lhs = self.inner.clone();
63 let rhs = rhs.into_expr().inner;
64 Expr::adhoc(move |b| lhs.build_expr(b).lt(rhs.build_expr(b)))
65 }
66
67 /// Compute the less than or equal operator (<=) of two expressions.
68 ///
69 /// ```
70 /// # use rust_query::IntoExpr;
71 /// # let mut client = rust_query::private::doctest::get_client();
72 /// # let txn = rust_query::private::doctest::get_txn(&mut client);
73 /// assert_eq!(txn.query_one(2.into_expr().lte(2)), true);
74 /// assert_eq!(txn.query_one(3.0.into_expr().lte(1.0)), false);
75 /// ```
76 pub fn lte(&self, rhs: impl IntoExpr<'column, S, Typ = T>) -> Expr<'column, S, bool> {
77 let lhs = self.inner.clone();
78 let rhs = rhs.into_expr().inner;
79 Expr::adhoc(move |b| lhs.build_expr(b).lte(rhs.build_expr(b)))
80 }
81
82 /// Compute the greater than operator (>) of two expressions.
83 ///
84 /// ```
85 /// # use rust_query::IntoExpr;
86 /// # let mut client = rust_query::private::doctest::get_client();
87 /// # let txn = rust_query::private::doctest::get_txn(&mut client);
88 /// assert_eq!(txn.query_one(2.into_expr().gt(2)), false);
89 /// assert_eq!(txn.query_one(3.0.into_expr().gt(1.0)), true);
90 /// ```
91 pub fn gt(&self, rhs: impl IntoExpr<'column, S, Typ = T>) -> Expr<'column, S, bool> {
92 let lhs = self.inner.clone();
93 let rhs = rhs.into_expr().inner;
94 Expr::adhoc(move |b| lhs.build_expr(b).gt(rhs.build_expr(b)))
95 }
96
97 /// Compute the greater than or equal (>=) operator of two expressions.
98 ///
99 /// ```
100 /// # use rust_query::IntoExpr;
101 /// # let mut client = rust_query::private::doctest::get_client();
102 /// # let txn = rust_query::private::doctest::get_txn(&mut client);
103 /// assert_eq!(txn.query_one(2.into_expr().gte(3)), false);
104 /// assert_eq!(txn.query_one(3.0.into_expr().gte(3.0)), true);
105 /// ```
106 pub fn gte(&self, rhs: impl IntoExpr<'column, S, Typ = T>) -> Expr<'column, S, bool> {
107 let lhs = self.inner.clone();
108 let rhs = rhs.into_expr().inner;
109 Expr::adhoc(move |b| lhs.build_expr(b).gte(rhs.build_expr(b)))
110 }
111}
112
113impl<'column, S, T: EqTyp + 'static> Expr<'column, S, T> {
114 /// Check whether two expressions are equal.
115 ///
116 /// ```
117 /// # use rust_query::IntoExpr;
118 /// # let mut client = rust_query::private::doctest::get_client();
119 /// # let txn = rust_query::private::doctest::get_txn(&mut client);
120 /// assert_eq!(txn.query_one(2.into_expr().eq(2)), true);
121 /// assert_eq!(txn.query_one(3.0.into_expr().eq(3.0)), true);
122 /// assert_eq!(txn.query_one("test".into_expr().eq("test")), true);
123 /// assert_eq!(txn.query_one(b"test".into_expr().eq(b"test" as &[u8])), true);
124 /// assert_eq!(txn.query_one(false.into_expr().eq(false)), true);
125 ///
126 /// assert_eq!(txn.query_one(1.into_expr().eq(2)), false);
127 /// ```
128 pub fn eq(&self, rhs: impl IntoExpr<'column, S, Typ = T>) -> Expr<'column, S, bool> {
129 let lhs = self.inner.clone();
130 let rhs = rhs.into_expr().inner;
131 Expr::adhoc(move |b| lhs.build_expr(b).eq(rhs.build_expr(b)))
132 }
133}
134
135impl<'column, S> Expr<'column, S, bool> {
136 /// Checks whether an expression is false.
137 ///
138 /// ```
139 /// # use rust_query::IntoExpr;
140 /// # let mut client = rust_query::private::doctest::get_client();
141 /// # let txn = rust_query::private::doctest::get_txn(&mut client);
142 /// assert_eq!(txn.query_one(true.into_expr().not()), false);
143 /// assert_eq!(txn.query_one(false.into_expr().not()), true);
144 /// ```
145 pub fn not(&self) -> Expr<'column, S, bool> {
146 let val = self.inner.clone();
147 Expr::adhoc(move |b| val.build_expr(b).not())
148 }
149
150 /// Check if two expressions are both true.
151 ///
152 /// ```
153 /// # use rust_query::IntoExpr;
154 /// # let mut client = rust_query::private::doctest::get_client();
155 /// # let txn = rust_query::private::doctest::get_txn(&mut client);
156 /// assert_eq!(txn.query_one(true.into_expr().and(true)), true);
157 /// assert_eq!(txn.query_one(false.into_expr().and(true)), false);
158 /// assert_eq!(txn.query_one(false.into_expr().and(false)), false);
159 /// ```
160 pub fn and(&self, rhs: impl IntoExpr<'column, S, Typ = bool>) -> Expr<'column, S, bool> {
161 let lhs = self.inner.clone();
162 let rhs = rhs.into_expr().inner;
163 Expr::adhoc(move |b| lhs.build_expr(b).and(rhs.build_expr(b)))
164 }
165
166 /// Check if one of two expressions is true.
167 ///
168 /// ```
169 /// # use rust_query::IntoExpr;
170 /// # let mut client = rust_query::private::doctest::get_client();
171 /// # let txn = rust_query::private::doctest::get_txn(&mut client);
172 /// assert_eq!(txn.query_one(true.into_expr().or(true)), true);
173 /// assert_eq!(txn.query_one(false.into_expr().or(true)), true);
174 /// assert_eq!(txn.query_one(false.into_expr().or(false)), false);
175 /// ```
176 pub fn or(&self, rhs: impl IntoExpr<'column, S, Typ = bool>) -> Expr<'column, S, bool> {
177 let lhs = self.inner.clone();
178 let rhs = rhs.into_expr().inner;
179 Expr::adhoc(move |b| lhs.build_expr(b).or(rhs.build_expr(b)))
180 }
181}
182
183impl<'column, S, Typ: 'static> Expr<'column, S, Option<Typ>> {
184 /// Use the first expression if it is [Some], otherwise use the second expression.
185 ///
186 /// ```
187 /// # use rust_query::IntoExpr;
188 /// # let mut client = rust_query::private::doctest::get_client();
189 /// # let txn = rust_query::private::doctest::get_txn(&mut client);
190 /// assert_eq!(txn.query_one(Some(10).into_expr().unwrap_or(5)), 10);
191 /// assert_eq!(txn.query_one(None::<String>.into_expr().unwrap_or("foo")), "foo");
192 /// ```
193 pub fn unwrap_or(&self, rhs: impl IntoExpr<'column, S, Typ = Typ>) -> Expr<'column, S, Typ>
194 where
195 Self: IntoExpr<'column, S, Typ = Option<Typ>>,
196 {
197 let lhs = self.inner.clone();
198 let rhs = rhs.into_expr().inner;
199 Expr::adhoc(move |b| sea_query::Expr::expr(lhs.build_expr(b)).if_null(rhs.build_expr(b)))
200 }
201
202 /// Check that the expression is [Some].
203 ///
204 /// ```
205 /// # use rust_query::IntoExpr;
206 /// # let mut client = rust_query::private::doctest::get_client();
207 /// # let txn = rust_query::private::doctest::get_txn(&mut client);
208 /// assert_eq!(txn.query_one(Some(10).into_expr().is_some()), true);
209 /// assert_eq!(txn.query_one(None::<i64>.into_expr().is_some()), false);
210 /// ```
211 pub fn is_some(&self) -> Expr<'column, S, bool> {
212 let val = self.inner.clone();
213 Expr::adhoc(move |b| val.build_expr(b).is_not_null())
214 }
215
216 /// Check that the expression is [None].
217 ///
218 /// ```
219 /// # use rust_query::IntoExpr;
220 /// # let mut client = rust_query::private::doctest::get_client();
221 /// # let txn = rust_query::private::doctest::get_txn(&mut client);
222 /// assert_eq!(txn.query_one(Some(10).into_expr().is_none()), false);
223 /// assert_eq!(txn.query_one(None::<i64>.into_expr().is_none()), true);
224 /// ```
225 pub fn is_none(&self) -> Expr<'column, S, bool> {
226 let val = self.inner.clone();
227 Expr::adhoc(move |b| val.build_expr(b).is_null())
228 }
229}
230
231impl<'column, S> Expr<'column, S, i64> {
232 /// Convert the [i64] expression to [f64] type.
233 ///
234 /// ```
235 /// # use rust_query::IntoExpr;
236 /// # let mut client = rust_query::private::doctest::get_client();
237 /// # let txn = rust_query::private::doctest::get_txn(&mut client);
238 /// assert_eq!(txn.query_one(10.into_expr().as_float()), 10.0);
239 /// ```
240 pub fn as_float(&self) -> Expr<'column, S, f64> {
241 let val = self.inner.clone();
242 Expr::adhoc(move |b| val.build_expr(b).cast_as(Alias::new("real")))
243 }
244}
245
246impl<'column, S> Expr<'column, S, String> {
247 /// Check if the expression starts with the string pattern.
248 ///
249 /// Matches case-sensitive. The pattern gets automatically escaped.
250 ///
251 /// ```
252 /// # use rust_query::IntoExpr;
253 /// # let mut client = rust_query::private::doctest::get_client();
254 /// # let txn = rust_query::private::doctest::get_txn(&mut client);
255 /// assert_eq!(txn.query_one("hello world".into_expr().starts_with("hello")), true);
256 /// assert_eq!(txn.query_one("hello world".into_expr().starts_with("Hello")), false);
257 /// ```
258 pub fn starts_with(&self, pattern: impl AsRef<str>) -> Expr<'column, S, bool> {
259 self.glob(format!("{}*", escape_glob(pattern)))
260 }
261
262 /// Check if the expression ends with the string pattern.
263 ///
264 /// Matches case-sensitive. The pattern gets automatically escaped.
265 ///
266 /// ```
267 /// # use rust_query::IntoExpr;
268 /// # let mut client = rust_query::private::doctest::get_client();
269 /// # let txn = rust_query::private::doctest::get_txn(&mut client);
270 /// assert_eq!(txn.query_one("hello world".into_expr().ends_with("world")), true);
271 /// assert_eq!(txn.query_one("hello world".into_expr().ends_with("World")), false);
272 /// ```
273 pub fn ends_with(&self, pattern: impl AsRef<str>) -> Expr<'column, S, bool> {
274 self.glob(format!("*{}", escape_glob(pattern)))
275 }
276
277 /// Check if the expression contains the string pattern.
278 ///
279 /// Matches case-sensitive. The pattern gets automatically escaped.
280 ///
281 /// ```
282 /// # use rust_query::IntoExpr;
283 /// # let mut client = rust_query::private::doctest::get_client();
284 /// # let txn = rust_query::private::doctest::get_txn(&mut client);
285 /// assert_eq!(txn.query_one("rhubarb".into_expr().contains("bar")), true);
286 /// assert_eq!(txn.query_one("rhubarb".into_expr().contains("Bar")), false);
287 /// ```
288 pub fn contains(&self, pattern: impl AsRef<str>) -> Expr<'column, S, bool> {
289 self.glob(format!("*{}*", escape_glob(pattern)))
290 }
291
292 /// Check if the expression matches the pattern [docs](https://www.sqlite.org/lang_expr.html#like).
293 ///
294 /// This is a case-sensitive version of [like](Self::like). It uses Unix file globbing syntax for wild
295 /// cards. `*` matches any sequence of characters and `?` matches any single character. `[0-9]` matches
296 /// any single digit and `[a-z]` matches any single lowercase letter. `^` negates the pattern.
297 ///
298 /// ```
299 /// # use rust_query::IntoExpr;
300 /// # let mut client = rust_query::private::doctest::get_client();
301 /// # let txn = rust_query::private::doctest::get_txn(&mut client);
302 /// assert_eq!(txn.query_one("hello world".into_expr().glob("?ello*")), true);
303 /// assert_eq!(txn.query_one("hello world".into_expr().glob("Hell*")), false);
304 /// ```
305 pub fn glob(&self, rhs: impl IntoExpr<'column, S, Typ = String>) -> Expr<'column, S, bool> {
306 let lhs = self.inner.clone();
307 let rhs = rhs.into_expr().inner;
308 Expr::adhoc(move |b| sea_query::Expr::expr(lhs.build_expr(b)).glob(rhs.build_expr(b)))
309 }
310
311 /// Check if the expression matches the pattern [docs](https://www.sqlite.org/lang_expr.html#like).
312 ///
313 /// As noted in the docs, it is **case-insensitive** for ASCII characters. Other characters are case-sensitive.
314 /// For creating patterns it uses `%` as a wildcard for any sequence of characters and `_` for any single character.
315 /// Special characters should be escaped with `\`.
316 ///
317 /// ```
318 /// # use rust_query::IntoExpr;
319 /// # let mut client = rust_query::private::doctest::get_client();
320 /// # let txn = rust_query::private::doctest::get_txn(&mut client);
321 /// assert_eq!(txn.query_one("hello world".into_expr().like("HELLO%")), true);
322 /// assert_eq!(txn.query_one("hello world".into_expr().like("he_o%")), false);
323 /// ```
324 pub fn like(&self, pattern: impl Into<String>) -> Expr<'column, S, bool> {
325 let lhs = self.inner.clone();
326 let rhs = pattern.into();
327 Expr::adhoc(move |b| {
328 sea_query::Expr::expr(lhs.build_expr(b))
329 .like(sea_query::LikeExpr::new(&rhs).escape('\\'))
330 })
331 }
332}
333
334// This is a copy of the function from the glob crate https://github.com/rust-lang/glob/blob/49ee1e92bd6e8c5854c0b339634f9b4b733aba4f/src/lib.rs#L720-L737.
335fn escape_glob(s: impl AsRef<str>) -> String {
336 let mut escaped = String::new();
337 for c in s.as_ref().chars() {
338 match c {
339 // note that ! does not need escaping because it is only special
340 // inside brackets
341 '?' | '*' | '[' | ']' => {
342 escaped.push('[');
343 escaped.push(c);
344 escaped.push(']');
345 }
346 c => {
347 escaped.push(c);
348 }
349 }
350 }
351 escaped
352}