cot/db/query.rs
1//! Database query builder.
2
3use std::marker::PhantomData;
4
5use derive_more::with_trait::Debug;
6use sea_query::{ExprTrait, IntoColumnRef};
7
8use crate::db;
9use crate::db::{
10 Auto, Database, DatabaseBackend, DbFieldValue, DbValue, ForeignKey, FromDbValue, Identifier,
11 Model, StatementResult, ToDbFieldValue,
12};
13
14/// A query that can be executed on a database. Can be used to filter, update,
15/// or delete rows.
16///
17/// # Example
18///
19/// ```
20/// use cot::db::model;
21/// use cot::db::query::Query;
22///
23/// #[model]
24/// struct User {
25/// #[model(primary_key)]
26/// id: i32,
27/// name: String,
28/// age: i32,
29/// }
30///
31/// let query = Query::<User>::new();
32/// ```
33pub struct Query<T> {
34 filter: Option<Expr>,
35 limit: Option<u64>,
36 offset: Option<u64>,
37 phantom_data: PhantomData<fn() -> T>,
38}
39
40// manual implementation to avoid `T: Debug` in the trait bounds
41impl<T> Debug for Query<T> {
42 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
43 f.debug_struct("Query")
44 .field("filter", &self.filter)
45 .field("limit", &self.limit)
46 .field("offset", &self.offset)
47 .field("phantom_data", &self.phantom_data)
48 .finish()
49 }
50}
51
52// manual implementation to avoid `T: Clone` in the trait bounds
53impl<T> Clone for Query<T> {
54 fn clone(&self) -> Self {
55 Self {
56 filter: self.filter.clone(),
57 limit: self.limit,
58 offset: self.offset,
59 phantom_data: PhantomData,
60 }
61 }
62}
63
64// manual implementation to avoid `T: PartialEq` in the trait bounds
65impl<T> PartialEq for Query<T> {
66 fn eq(&self, other: &Self) -> bool {
67 self.filter == other.filter
68 }
69}
70
71impl<T: Model> Default for Query<T> {
72 fn default() -> Self {
73 Self::new()
74 }
75}
76
77impl<T: Model> Query<T> {
78 /// Create a new query.
79 ///
80 /// # Example
81 ///
82 /// ```
83 /// use cot::db::model;
84 /// use cot::db::query::Query;
85 ///
86 /// #[model]
87 /// struct User {
88 /// #[model(primary_key)]
89 /// id: i32,
90 /// name: String,
91 /// age: i32,
92 /// }
93 ///
94 /// let query = Query::<User>::new();
95 /// ```
96 #[must_use]
97 pub fn new() -> Self {
98 Self {
99 filter: None,
100 limit: None,
101 offset: None,
102 phantom_data: PhantomData,
103 }
104 }
105
106 /// Set the filter expression for the query.
107 ///
108 /// # Example
109 ///
110 /// ```
111 /// use cot::db::model;
112 /// use cot::db::query::{Expr, Query};
113 ///
114 /// #[model]
115 /// struct User {
116 /// #[model(primary_key)]
117 /// id: i32,
118 /// name: String,
119 /// age: i32,
120 /// }
121 ///
122 /// let query = Query::<User>::new().filter(Expr::eq(Expr::field("name"), Expr::value("John")));
123 /// ```
124 pub fn filter(&mut self, filter: Expr) -> &mut Self {
125 self.filter = Some(filter);
126 self
127 }
128
129 /// Set the limit for the query.
130 ///
131 /// # Example
132 ///
133 /// ```
134 /// use cot::db::model;
135 /// use cot::db::query::{Expr, Query};
136 ///
137 /// #[model]
138 /// struct User {
139 /// #[model(primary_key)]
140 /// id: i32,
141 /// name: String,
142 /// age: i32,
143 /// }
144 ///
145 /// let query = Query::<User>::new().limit(10);
146 /// ```
147 pub fn limit(&mut self, limit: u64) -> &mut Self {
148 self.limit = Some(limit);
149 self
150 }
151
152 /// Set the offset for the query.
153 ///
154 /// # Example
155 ///
156 /// ```
157 /// use cot::db::model;
158 /// use cot::db::query::{Expr, Query};
159 ///
160 /// #[model]
161 /// struct User {
162 /// #[model(primary_key)]
163 /// id: i32,
164 /// name: String,
165 /// age: i32,
166 /// }
167 ///
168 /// let query = Query::<User>::new().offset(10);
169 /// ```
170 pub fn offset(&mut self, offset: u64) -> &mut Self {
171 self.offset = Some(offset);
172 self
173 }
174
175 /// Execute the query and return all results.
176 ///
177 /// # Errors
178 ///
179 /// Returns an error if the query fails.
180 pub async fn all<DB: DatabaseBackend>(&self, db: &DB) -> db::Result<Vec<T>> {
181 db.query(self).await
182 }
183
184 /// Execute the query and return the first result.
185 ///
186 /// # Errors
187 ///
188 /// Returns an error if the query fails.
189 pub async fn get<DB: DatabaseBackend>(&self, db: &DB) -> db::Result<Option<T>> {
190 // TODO panic/error if more than one result
191 db.get(self).await
192 }
193
194 /// Execute the query and return the number of results.
195 ///
196 /// # Errors
197 ///
198 /// Returns an error if the query fails.
199 pub async fn count(&self, db: &Database) -> db::Result<u64> {
200 let mut select = sea_query::Query::select();
201 select
202 .from(T::TABLE_NAME)
203 .expr(sea_query::Expr::col(sea_query::Asterisk).count());
204 self.add_filter_to_statement(&mut select);
205 let row = db.fetch_option(&select).await?;
206 let count = match row {
207 #[expect(clippy::cast_sign_loss)]
208 Some(row) => row.get::<i64>(0)? as u64,
209 None => 0,
210 };
211 Ok(count)
212 }
213
214 /// Execute the query and check if any results exist.
215 ///
216 /// # Errors
217 ///
218 /// Returns an error if the query fails.
219 pub async fn exists<DB: DatabaseBackend>(&self, db: &DB) -> db::Result<bool> {
220 db.exists(self).await
221 }
222
223 /// Delete all rows that match the query.
224 ///
225 /// # Errors
226 ///
227 /// Returns an error if the query fails.
228 pub async fn delete<DB: DatabaseBackend>(&self, db: &DB) -> db::Result<StatementResult> {
229 db.delete(self).await
230 }
231
232 pub(super) fn add_filter_to_statement<S: sea_query::ConditionalStatement>(
233 &self,
234 statement: &mut S,
235 ) {
236 if let Some(filter) = &self.filter {
237 statement.and_where(filter.as_sea_query_expr());
238 }
239 }
240
241 pub(super) fn add_limit_to_statement(&self, statement: &mut sea_query::SelectStatement) {
242 if let Some(limit) = self.limit {
243 statement.limit(limit);
244 }
245 }
246
247 pub(super) fn add_offset_to_statement(&self, statement: &mut sea_query::SelectStatement) {
248 if let Some(offset) = self.offset {
249 statement.offset(offset);
250 }
251 }
252}
253
254/// An expression that can be used to filter, update, or delete rows.
255///
256/// This is used to create complex queries with multiple conditions. Typically,
257/// it is only internally used by the [`cot::db::query!`] macro to create a
258/// [`Query`].
259///
260/// # Example
261///
262/// ```
263/// use cot::db::{model, query};
264/// use cot::db::query::{Expr, Query};
265///
266/// #[model]
267/// struct MyModel {
268/// #[model(primary_key)]
269/// id: i32,
270/// };
271///
272/// let expr = Expr::eq(Expr::field("id"), Expr::value(5));
273///
274/// assert_eq!(
275/// <Query<MyModel>>::new().filter(expr),
276/// query!(MyModel, $id == 5)
277/// );
278/// ```
279#[derive(Debug, Clone, PartialEq)]
280#[non_exhaustive]
281pub enum Expr {
282 /// An expression containing a reference to a column.
283 ///
284 /// # Example
285 ///
286 /// ```
287 /// use cot::db::{model, query};
288 /// use cot::db::query::{Expr, Query};
289 ///
290 /// #[model]
291 /// struct MyModel {
292 /// #[model(primary_key)]
293 /// id: i32,
294 /// };
295 ///
296 /// let expr = Expr::eq(Expr::field("id"), Expr::value(5));
297 ///
298 /// assert_eq!(
299 /// <Query<MyModel>>::new().filter(expr),
300 /// query!(MyModel, $id == 5)
301 /// );
302 /// ```
303 Field(Identifier),
304 /// An expression containing a literal value.
305 ///
306 /// # Example
307 ///
308 /// ```
309 /// use cot::db::{model, query};
310 /// use cot::db::query::{Expr, Query};
311 ///
312 /// #[model]
313 /// struct MyModel {
314 /// #[model(primary_key)]
315 /// id: i32,
316 /// };
317 ///
318 /// let expr = Expr::ne(Expr::field("id"), Expr::value(5));
319 ///
320 /// assert_eq!(
321 /// <Query<MyModel>>::new().filter(expr),
322 /// query!(MyModel, $id != 5)
323 /// );
324 /// ```
325 Value(DbValue),
326 /// An `AND` expression.
327 ///
328 /// # Example
329 ///
330 /// ```
331 /// use cot::db::{model, query};
332 /// use cot::db::query::{Expr, Query};
333 ///
334 /// #[model]
335 /// struct MyModel {
336 /// #[model(primary_key)]
337 /// id: i32,
338 /// };
339 ///
340 /// let expr = Expr::and(
341 /// Expr::gt(Expr::field("id"), Expr::value(10)),
342 /// Expr::lt(Expr::field("id"), Expr::value(20))
343 /// );
344 /// assert_eq!(
345 /// <Query<MyModel>>::new().filter(expr),
346 /// query!(MyModel, $id > 10 && $id < 20)
347 /// );
348 /// ```
349 And(Box<Expr>, Box<Expr>),
350 /// An `OR` expression.
351 ///
352 /// # Example
353 ///
354 /// ```
355 /// use cot::db::{model, query};
356 /// use cot::db::query::{Expr, Query};
357 ///
358 /// #[model]
359 /// struct MyModel {
360 /// #[model(primary_key)]
361 /// id: i32,
362 /// };
363 ///
364 /// let expr = Expr::or(
365 /// Expr::gt(Expr::field("id"), Expr::value(10)),
366 /// Expr::lt(Expr::field("id"), Expr::value(20))
367 /// );
368 /// assert_eq!(
369 /// <Query<MyModel>>::new().filter(expr),
370 /// query!(MyModel, $id > 10 || $id < 20)
371 /// );
372 /// ```
373 Or(Box<Expr>, Box<Expr>),
374 /// An `=` expression.
375 ///
376 /// # Example
377 ///
378 /// ```
379 /// use cot::db::{model, query};
380 /// use cot::db::query::{Expr, Query};
381 ///
382 /// #[model]
383 /// struct MyModel {
384 /// #[model(primary_key)]
385 /// id: i32,
386 /// };
387 ///
388 /// let expr = Expr::eq(Expr::field("id"), Expr::value(5));
389 ///
390 /// assert_eq!(
391 /// <Query<MyModel>>::new().filter(expr),
392 /// query!(MyModel, $id == 5)
393 /// );
394 /// ```
395 Eq(Box<Expr>, Box<Expr>),
396 /// A `!=` expression.
397 ///
398 /// # Example
399 ///
400 /// ```
401 /// use cot::db::{model, query};
402 /// use cot::db::query::{Expr, Query};
403 ///
404 /// #[model]
405 /// struct MyModel {
406 /// #[model(primary_key)]
407 /// id: i32,
408 /// };
409 ///
410 /// let expr = Expr::ne(Expr::field("id"), Expr::value(5));
411 ///
412 /// assert_eq!(
413 /// <Query<MyModel>>::new().filter(expr),
414 /// query!(MyModel, $id != 5)
415 /// );
416 /// ```
417 Ne(Box<Expr>, Box<Expr>),
418 /// A `<` expression.
419 ///
420 /// # Example
421 ///
422 /// ```
423 /// use cot::db::{model, query};
424 /// use cot::db::query::{Expr, Query};
425 ///
426 /// #[model]
427 /// struct MyModel {
428 /// #[model(primary_key)]
429 /// id: i32,
430 /// };
431 ///
432 /// let expr = Expr::lt(Expr::field("id"), Expr::value(5));
433 ///
434 /// assert_eq!(
435 /// <Query<MyModel>>::new().filter(expr),
436 /// query!(MyModel, $id < 5)
437 /// );
438 /// ```
439 Lt(Box<Expr>, Box<Expr>),
440 /// A `<=` expression.
441 ///
442 /// # Example
443 ///
444 /// ```
445 /// use cot::db::{model, query};
446 /// use cot::db::query::{Expr, Query};
447 ///
448 /// #[model]
449 /// struct MyModel {
450 /// #[model(primary_key)]
451 /// id: i32,
452 /// };
453 ///
454 /// let expr = Expr::lte(Expr::field("id"), Expr::value(5));
455 ///
456 /// assert_eq!(
457 /// <Query<MyModel>>::new().filter(expr),
458 /// query!(MyModel, $id <= 5)
459 /// );
460 /// ```
461 Lte(Box<Expr>, Box<Expr>),
462 /// A `>` expression.
463 ///
464 /// # Example
465 ///
466 /// ```
467 /// use cot::db::{model, query};
468 /// use cot::db::query::{Expr, Query};
469 ///
470 /// #[model]
471 /// struct MyModel {
472 /// #[model(primary_key)]
473 /// id: i32,
474 /// };
475 ///
476 /// let expr = Expr::gt(Expr::field("id"), Expr::value(5));
477 ///
478 /// assert_eq!(
479 /// <Query<MyModel>>::new().filter(expr),
480 /// query!(MyModel, $id > 5)
481 /// );
482 /// ```
483 Gt(Box<Expr>, Box<Expr>),
484 /// A `>=` expression.
485 ///
486 /// # Example
487 ///
488 /// ```
489 /// use cot::db::{model, query};
490 /// use cot::db::query::{Expr, Query};
491 ///
492 /// #[model]
493 /// struct MyModel {
494 /// #[model(primary_key)]
495 /// id: i32,
496 /// };
497 ///
498 /// let expr = Expr::gte(Expr::field("id"), Expr::value(5));
499 ///
500 /// assert_eq!(
501 /// <Query<MyModel>>::new().filter(expr),
502 /// query!(MyModel, $id >= 5)
503 /// );
504 /// ```
505 Gte(Box<Expr>, Box<Expr>),
506 /// A `+` expression.
507 ///
508 /// # Example
509 ///
510 /// ```
511 /// use cot::db::{model, query};
512 /// use cot::db::query::{Expr, Query};
513 ///
514 /// #[model]
515 /// struct MyModel {
516 /// #[model(primary_key)]
517 /// id: i32,
518 /// id_2: i32,
519 /// };
520 ///
521 /// let expr = Expr::eq(Expr::field("id"), Expr::add(Expr::field("id_2"), Expr::value(5)));
522 ///
523 /// assert_eq!(
524 /// <Query<MyModel>>::new().filter(expr),
525 /// query!(MyModel, $id == $id_2 + 5)
526 /// );
527 /// ```
528 Add(Box<Expr>, Box<Expr>),
529 /// A `-` expression.
530 ///
531 /// # Example
532 ///
533 /// ```
534 /// use cot::db::{model, query};
535 /// use cot::db::query::{Expr, Query};
536 ///
537 /// #[model]
538 /// struct MyModel {
539 /// #[model(primary_key)]
540 /// id: i32,
541 /// id_2: i32,
542 /// };
543 ///
544 /// let expr = Expr::eq(Expr::field("id"), Expr::sub(Expr::field("id_2"), Expr::value(5)));
545 ///
546 /// assert_eq!(
547 /// <Query<MyModel>>::new().filter(expr),
548 /// query!(MyModel, $id == $id_2 - 5)
549 /// );
550 /// ```
551 Sub(Box<Expr>, Box<Expr>),
552 /// A `*` expression.
553 ///
554 /// # Example
555 ///
556 /// ```
557 /// use cot::db::{model, query};
558 /// use cot::db::query::{Expr, Query};
559 ///
560 /// #[model]
561 /// struct MyModel {
562 /// #[model(primary_key)]
563 /// id: i32,
564 /// id_2: i32,
565 /// };
566 ///
567 /// let expr = Expr::eq(Expr::field("id"), Expr::mul(Expr::field("id_2"), Expr::value(2)));
568 ///
569 /// assert_eq!(
570 /// <Query<MyModel>>::new().filter(expr),
571 /// query!(MyModel, $id == $id_2 * 2)
572 /// );
573 /// ```
574 Mul(Box<Expr>, Box<Expr>),
575 /// A `/` expression.
576 ///
577 /// # Example
578 ///
579 /// ```
580 /// use cot::db::{model, query};
581 /// use cot::db::query::{Expr, Query};
582 ///
583 /// #[model]
584 /// struct MyModel {
585 /// #[model(primary_key)]
586 /// id: i32,
587 /// id_2: i32,
588 /// };
589 ///
590 /// let expr = Expr::eq(Expr::field("id"), Expr::div(Expr::field("id_2"), Expr::value(2)));
591 ///
592 /// assert_eq!(
593 /// <Query<MyModel>>::new().filter(expr),
594 /// query!(MyModel, $id == $id_2 / 2)
595 /// );
596 /// ```
597 Div(Box<Expr>, Box<Expr>),
598}
599
600impl Expr {
601 /// Create a new field expression. This represents a reference to a column
602 /// in the database.
603 ///
604 /// # Example
605 ///
606 /// ```
607 /// use cot::db::{model, query};
608 /// use cot::db::query::{Expr, Query};
609 ///
610 /// #[model]
611 /// struct MyModel {
612 /// #[model(primary_key)]
613 /// id: i32,
614 /// };
615 ///
616 /// let expr = Expr::eq(Expr::field("id"), Expr::value(5));
617 ///
618 /// assert_eq!(
619 /// <Query<MyModel>>::new().filter(expr),
620 /// query!(MyModel, $id == 5)
621 /// );
622 /// ```
623 #[must_use]
624 pub fn field<T: Into<Identifier>>(identifier: T) -> Self {
625 Self::Field(identifier.into())
626 }
627
628 /// Create a new value expression. This represents a literal value that gets
629 /// passed into the SQL query.
630 ///
631 /// # Panics
632 ///
633 /// If the value provided is a [`DbFieldValue::Auto`].
634 ///
635 /// # Example
636 ///
637 /// ```
638 /// use cot::db::{model, query};
639 /// use cot::db::query::{Expr, Query};
640 ///
641 /// #[model]
642 /// struct MyModel {
643 /// #[model(primary_key)]
644 /// id: i32,
645 /// };
646 ///
647 /// let expr = Expr::ne(Expr::field("id"), Expr::value(5));
648 ///
649 /// assert_eq!(
650 /// <Query<MyModel>>::new().filter(expr),
651 /// query!(MyModel, $id != 5)
652 /// );
653 /// ```
654 #[must_use]
655 #[expect(clippy::needless_pass_by_value)]
656 pub fn value<T: ToDbFieldValue>(value: T) -> Self {
657 match value.to_db_field_value() {
658 DbFieldValue::Value(value) => Self::Value(value),
659 DbFieldValue::Auto => panic!("Cannot create query with a non-value field"),
660 }
661 }
662
663 /// Create a new `AND` expression.
664 ///
665 /// # Example
666 ///
667 /// ```
668 /// use cot::db::{model, query};
669 /// use cot::db::query::{Expr, Query};
670 ///
671 /// #[model]
672 /// struct MyModel {
673 /// #[model(primary_key)]
674 /// id: i32,
675 /// };
676 ///
677 /// let expr = Expr::and(
678 /// Expr::gt(Expr::field("id"), Expr::value(10)),
679 /// Expr::lt(Expr::field("id"), Expr::value(20))
680 /// );
681 /// assert_eq!(
682 /// <Query<MyModel>>::new().filter(expr),
683 /// query!(MyModel, $id > 10 && $id < 20)
684 /// );
685 /// ```
686 #[must_use]
687 pub fn and(lhs: Self, rhs: Self) -> Self {
688 Self::And(Box::new(lhs), Box::new(rhs))
689 }
690
691 /// Create a new `OR` expression.
692 ///
693 /// # Example
694 ///
695 /// ```
696 /// use cot::db::{model, query};
697 /// use cot::db::query::{Expr, Query};
698 ///
699 /// #[model]
700 /// struct MyModel {
701 /// #[model(primary_key)]
702 /// id: i32,
703 /// };
704 ///
705 /// let expr = Expr::or(
706 /// Expr::gt(Expr::field("id"), Expr::value(10)),
707 /// Expr::lt(Expr::field("id"), Expr::value(20))
708 /// );
709 /// assert_eq!(
710 /// <Query<MyModel>>::new().filter(expr),
711 /// query!(MyModel, $id > 10 || $id < 20)
712 /// );
713 /// ```
714 #[must_use]
715 pub fn or(lhs: Self, rhs: Self) -> Self {
716 Self::Or(Box::new(lhs), Box::new(rhs))
717 }
718
719 /// Create a new `=` expression.
720 ///
721 /// # Example
722 ///
723 /// ```
724 /// use cot::db::{model, query};
725 /// use cot::db::query::{Expr, Query};
726 ///
727 /// #[model]
728 /// struct MyModel {
729 /// #[model(primary_key)]
730 /// id: i32,
731 /// };
732 ///
733 /// let expr = Expr::eq(Expr::field("id"), Expr::value(5));
734 ///
735 /// assert_eq!(
736 /// <Query<MyModel>>::new().filter(expr),
737 /// query!(MyModel, $id == 5)
738 /// );
739 /// ```
740 #[must_use]
741 pub fn eq(lhs: Self, rhs: Self) -> Self {
742 Self::Eq(Box::new(lhs), Box::new(rhs))
743 }
744
745 /// Create a new `!=` expression.
746 ///
747 /// # Example
748 ///
749 /// ```
750 /// use cot::db::{model, query};
751 /// use cot::db::query::{Expr, Query};
752 ///
753 /// #[model]
754 /// struct MyModel {
755 /// #[model(primary_key)]
756 /// id: i32,
757 /// };
758 ///
759 /// let expr = Expr::ne(Expr::field("id"), Expr::value(5));
760 ///
761 /// assert_eq!(
762 /// <Query<MyModel>>::new().filter(expr),
763 /// query!(MyModel, $id != 5)
764 /// );
765 /// ```
766 #[must_use]
767 pub fn ne(lhs: Self, rhs: Self) -> Self {
768 Self::Ne(Box::new(lhs), Box::new(rhs))
769 }
770
771 /// Create a new `<` expression.
772 ///
773 /// # Example
774 ///
775 /// ```
776 /// use cot::db::{model, query};
777 /// use cot::db::query::{Expr, Query};
778 ///
779 /// #[model]
780 /// struct MyModel {
781 /// #[model(primary_key)]
782 /// id: i32,
783 /// };
784 ///
785 /// let expr = Expr::lt(Expr::field("id"), Expr::value(5));
786 ///
787 /// assert_eq!(
788 /// <Query<MyModel>>::new().filter(expr),
789 /// query!(MyModel, $id < 5)
790 /// );
791 /// ```
792 #[must_use]
793 pub fn lt(lhs: Self, rhs: Self) -> Self {
794 Self::Lt(Box::new(lhs), Box::new(rhs))
795 }
796
797 /// Create a new `<=` expression.
798 ///
799 /// # Example
800 ///
801 /// ```
802 /// use cot::db::{model, query};
803 /// use cot::db::query::{Expr, Query};
804 ///
805 /// #[model]
806 /// struct MyModel {
807 /// #[model(primary_key)]
808 /// id: i32,
809 /// };
810 ///
811 /// let expr = Expr::lte(Expr::field("id"), Expr::value(5));
812 ///
813 /// assert_eq!(
814 /// <Query<MyModel>>::new().filter(expr),
815 /// query!(MyModel, $id <= 5)
816 /// );
817 /// ```
818 #[must_use]
819 pub fn lte(lhs: Self, rhs: Self) -> Self {
820 Self::Lte(Box::new(lhs), Box::new(rhs))
821 }
822
823 /// Create a new `>` expression.
824 ///
825 /// # Example
826 ///
827 /// ```
828 /// use cot::db::{model, query};
829 /// use cot::db::query::{Expr, Query};
830 ///
831 /// #[model]
832 /// struct MyModel {
833 /// #[model(primary_key)]
834 /// id: i32,
835 /// };
836 ///
837 /// let expr = Expr::gt(Expr::field("id"), Expr::value(5));
838 ///
839 /// assert_eq!(
840 /// <Query<MyModel>>::new().filter(expr),
841 /// query!(MyModel, $id > 5)
842 /// );
843 /// ```
844 #[must_use]
845 pub fn gt(lhs: Self, rhs: Self) -> Self {
846 Self::Gt(Box::new(lhs), Box::new(rhs))
847 }
848
849 /// Create a new `>=` expression.
850 ///
851 /// # Example
852 ///
853 /// ```
854 /// use cot::db::{model, query};
855 /// use cot::db::query::{Expr, Query};
856 ///
857 /// #[model]
858 /// struct MyModel {
859 /// #[model(primary_key)]
860 /// id: i32,
861 /// };
862 ///
863 /// let expr = Expr::gte(Expr::field("id"), Expr::value(5));
864 ///
865 /// assert_eq!(
866 /// <Query<MyModel>>::new().filter(expr),
867 /// query!(MyModel, $id >= 5)
868 /// );
869 /// ```
870 #[must_use]
871 pub fn gte(lhs: Self, rhs: Self) -> Self {
872 Self::Gte(Box::new(lhs), Box::new(rhs))
873 }
874
875 /// Create a new `+` expression.
876 ///
877 /// # Example
878 ///
879 /// ```
880 /// use cot::db::{model, query};
881 /// use cot::db::query::{Expr, Query};
882 ///
883 /// #[model]
884 /// struct MyModel {
885 /// #[model(primary_key)]
886 /// id: i32,
887 /// id_2: i32,
888 /// };
889 ///
890 /// let expr = Expr::eq(Expr::field("id"), Expr::add(Expr::field("id_2"), Expr::value(5)));
891 ///
892 /// assert_eq!(
893 /// <Query<MyModel>>::new().filter(expr),
894 /// query!(MyModel, $id == $id_2 + 5)
895 /// );
896 /// ```
897 #[expect(clippy::should_implement_trait)]
898 #[must_use]
899 pub fn add(lhs: Self, rhs: Self) -> Self {
900 Self::Add(Box::new(lhs), Box::new(rhs))
901 }
902
903 /// Create a new `-` expression.
904 ///
905 /// # Example
906 ///
907 /// ```
908 /// use cot::db::{model, query};
909 /// use cot::db::query::{Expr, Query};
910 ///
911 /// #[model]
912 /// struct MyModel {
913 /// #[model(primary_key)]
914 /// id: i32,
915 /// id_2: i32,
916 /// };
917 ///
918 /// let expr = Expr::eq(Expr::field("id"), Expr::sub(Expr::field("id_2"), Expr::value(5)));
919 ///
920 /// assert_eq!(
921 /// <Query<MyModel>>::new().filter(expr),
922 /// query!(MyModel, $id == $id_2 - 5)
923 /// );
924 /// ```
925 #[expect(clippy::should_implement_trait)]
926 #[must_use]
927 pub fn sub(lhs: Self, rhs: Self) -> Self {
928 Self::Sub(Box::new(lhs), Box::new(rhs))
929 }
930
931 /// Create a new `*` expression.
932 ///
933 /// # Example
934 ///
935 /// ```
936 /// use cot::db::{model, query};
937 /// use cot::db::query::{Expr, Query};
938 ///
939 /// #[model]
940 /// struct MyModel {
941 /// #[model(primary_key)]
942 /// id: i32,
943 /// id_2: i32,
944 /// };
945 ///
946 /// let expr = Expr::eq(Expr::field("id"), Expr::mul(Expr::field("id_2"), Expr::value(2)));
947 ///
948 /// assert_eq!(
949 /// <Query<MyModel>>::new().filter(expr),
950 /// query!(MyModel, $id == $id_2 * 2)
951 /// );
952 /// ```
953 #[expect(clippy::should_implement_trait)]
954 #[must_use]
955 pub fn mul(lhs: Self, rhs: Self) -> Self {
956 Self::Mul(Box::new(lhs), Box::new(rhs))
957 }
958
959 /// Create a new `/` expression.
960 ///
961 /// # Example
962 ///
963 /// ```
964 /// use cot::db::{model, query};
965 /// use cot::db::query::{Expr, Query};
966 ///
967 /// #[model]
968 /// struct MyModel {
969 /// #[model(primary_key)]
970 /// id: i32,
971 /// id_2: i32,
972 /// };
973 ///
974 /// let expr = Expr::eq(Expr::field("id"), Expr::div(Expr::field("id_2"), Expr::value(2)));
975 ///
976 /// assert_eq!(
977 /// <Query<MyModel>>::new().filter(expr),
978 /// query!(MyModel, $id == $id_2 / 2)
979 /// );
980 /// ```
981 #[expect(clippy::should_implement_trait)]
982 #[must_use]
983 pub fn div(lhs: Self, rhs: Self) -> Self {
984 Self::Div(Box::new(lhs), Box::new(rhs))
985 }
986
987 /// Returns the expression as a [`sea_query::SimpleExpr`].
988 ///
989 /// # Example
990 ///
991 /// ```
992 /// use cot::db::Identifier;
993 /// use cot::db::query::Expr;
994 /// use sea_query::IntoColumnRef;
995 ///
996 /// let expr = Expr::eq(Expr::field("id"), Expr::value(5));
997 ///
998 /// assert_eq!(
999 /// expr.as_sea_query_expr(),
1000 /// sea_query::SimpleExpr::eq(
1001 /// sea_query::SimpleExpr::Column(Identifier::new("id").into_column_ref()),
1002 /// sea_query::SimpleExpr::Value(sea_query::Value::Int(Some(5)))
1003 /// )
1004 /// );
1005 /// ```
1006 #[must_use]
1007 pub fn as_sea_query_expr(&self) -> sea_query::SimpleExpr {
1008 match self {
1009 Self::Field(identifier) => (*identifier).into_column_ref().into(),
1010 Self::Value(value) => (*value).clone().into(),
1011 Self::And(lhs, rhs) => lhs.as_sea_query_expr().and(rhs.as_sea_query_expr()),
1012 Self::Or(lhs, rhs) => lhs.as_sea_query_expr().or(rhs.as_sea_query_expr()),
1013 Self::Eq(lhs, rhs) => lhs.as_sea_query_expr().eq(rhs.as_sea_query_expr()),
1014 Self::Ne(lhs, rhs) => lhs.as_sea_query_expr().ne(rhs.as_sea_query_expr()),
1015 Self::Lt(lhs, rhs) => lhs.as_sea_query_expr().lt(rhs.as_sea_query_expr()),
1016 Self::Lte(lhs, rhs) => lhs.as_sea_query_expr().lte(rhs.as_sea_query_expr()),
1017 Self::Gt(lhs, rhs) => lhs.as_sea_query_expr().gt(rhs.as_sea_query_expr()),
1018 Self::Gte(lhs, rhs) => lhs.as_sea_query_expr().gte(rhs.as_sea_query_expr()),
1019 Self::Add(lhs, rhs) => lhs.as_sea_query_expr().add(rhs.as_sea_query_expr()),
1020 Self::Sub(lhs, rhs) => lhs.as_sea_query_expr().sub(rhs.as_sea_query_expr()),
1021 Self::Mul(lhs, rhs) => lhs.as_sea_query_expr().mul(rhs.as_sea_query_expr()),
1022 Self::Div(lhs, rhs) => lhs.as_sea_query_expr().div(rhs.as_sea_query_expr()),
1023 }
1024 }
1025}
1026
1027/// A reference to a field in a database table.
1028///
1029/// This is used to create expressions that reference a specific column in a
1030/// table with a specific type. This allows for type-safe creation of queries
1031/// with some common operators like `=`, `!=`, `+`, `-`, `*`, and `/`.
1032#[derive(Debug)]
1033pub struct FieldRef<T> {
1034 identifier: Identifier,
1035 phantom_data: PhantomData<T>,
1036}
1037
1038impl<T: FromDbValue + ToDbFieldValue> FieldRef<T> {
1039 /// Create a new field reference.
1040 #[must_use]
1041 pub const fn new(identifier: Identifier) -> Self {
1042 Self {
1043 identifier,
1044 phantom_data: PhantomData,
1045 }
1046 }
1047}
1048
1049impl<T> FieldRef<T> {
1050 /// Returns the field reference as an [`Expr`].
1051 #[must_use]
1052 pub fn as_expr(&self) -> Expr {
1053 Expr::Field(self.identifier)
1054 }
1055}
1056
1057/// A trait for types that can be compared in database expressions.
1058pub trait ExprEq<T> {
1059 /// Creates an expression that checks if the field is equal to the given
1060 /// value.
1061 ///
1062 /// # Examples
1063 ///
1064 /// ```
1065 /// use cot::db::query::{Expr, ExprEq, Query};
1066 /// use cot::db::{model, query};
1067 ///
1068 /// #[model]
1069 /// struct MyModel {
1070 /// #[model(primary_key)]
1071 /// id: i32,
1072 /// };
1073 ///
1074 /// let expr = <MyModel as cot::db::Model>::Fields::id.eq(5);
1075 ///
1076 /// assert_eq!(
1077 /// <Query<MyModel>>::new().filter(expr),
1078 /// query!(MyModel, $id == 5)
1079 /// );
1080 /// ```
1081 fn eq<V: IntoField<T>>(self, other: V) -> Expr;
1082
1083 /// Creates an expression that checks if the field is not equal to the given
1084 /// value.
1085 ///
1086 /// # Examples
1087 ///
1088 /// ```
1089 /// use cot::db::query::{Expr, ExprEq, Query};
1090 /// use cot::db::{model, query};
1091 ///
1092 /// #[model]
1093 /// struct MyModel {
1094 /// #[model(primary_key)]
1095 /// id: i32,
1096 /// };
1097 ///
1098 /// let expr = <MyModel as cot::db::Model>::Fields::id.ne(5);
1099 ///
1100 /// assert_eq!(
1101 /// <Query<MyModel>>::new().filter(expr),
1102 /// query!(MyModel, $id != 5)
1103 /// );
1104 /// ```
1105 fn ne<V: IntoField<T>>(self, other: V) -> Expr;
1106}
1107
1108impl<T: ToDbFieldValue + 'static> ExprEq<T> for FieldRef<T> {
1109 fn eq<V: IntoField<T>>(self, other: V) -> Expr {
1110 Expr::eq(self.as_expr(), Expr::value(other.into_field()))
1111 }
1112
1113 fn ne<V: IntoField<T>>(self, other: V) -> Expr {
1114 Expr::ne(self.as_expr(), Expr::value(other.into_field()))
1115 }
1116}
1117
1118/// A trait for database types that can be added to each other.
1119pub trait ExprAdd<T> {
1120 /// Creates an expression that adds the field to the given value.
1121 ///
1122 /// # Examples
1123 ///
1124 /// ```
1125 /// use cot::db::query::{Expr, ExprAdd, Query};
1126 /// use cot::db::{model, query};
1127 ///
1128 /// #[model]
1129 /// struct MyModel {
1130 /// #[model(primary_key)]
1131 /// id: i32,
1132 /// };
1133 ///
1134 /// let expr = <MyModel as cot::db::Model>::Fields::id.add(5);
1135 ///
1136 /// assert_eq!(
1137 /// <Query<MyModel>>::new().filter(Expr::eq(Expr::field("id"), expr)),
1138 /// query!(MyModel, $id == $id + 5)
1139 /// );
1140 /// ```
1141 fn add<V: Into<T>>(self, other: V) -> Expr;
1142}
1143
1144/// A trait for database types that can be subtracted from each other.
1145pub trait ExprSub<T> {
1146 /// Creates an expression that subtracts the field from the given value.
1147 ///
1148 /// # Examples
1149 ///
1150 /// ```
1151 /// use cot::db::query::{Expr, ExprSub, Query};
1152 /// use cot::db::{model, query};
1153 ///
1154 /// #[model]
1155 /// struct MyModel {
1156 /// #[model(primary_key)]
1157 /// id: i32,
1158 /// };
1159 ///
1160 /// let expr = <MyModel as cot::db::Model>::Fields::id.sub(5);
1161 ///
1162 /// assert_eq!(
1163 /// <Query<MyModel>>::new().filter(Expr::eq(Expr::field("id"), expr)),
1164 /// query!(MyModel, $id == $id - 5)
1165 /// );
1166 /// ```
1167 fn sub<V: Into<T>>(self, other: V) -> Expr;
1168}
1169
1170/// A trait for database types that can be multiplied by each other.
1171pub trait ExprMul<T> {
1172 /// Creates an expression that multiplies the field by the given value.
1173 ///
1174 /// # Examples
1175 ///
1176 /// ```
1177 /// use cot::db::query::{Expr, ExprMul, Query};
1178 /// use cot::db::{model, query};
1179 ///
1180 /// #[model]
1181 /// struct MyModel {
1182 /// #[model(primary_key)]
1183 /// id: i32,
1184 /// };
1185 ///
1186 /// let expr = <MyModel as cot::db::Model>::Fields::id.mul(2);
1187 ///
1188 /// assert_eq!(
1189 /// <Query<MyModel>>::new().filter(Expr::eq(Expr::field("id"), expr)),
1190 /// query!(MyModel, $id == $id * 2)
1191 /// );
1192 /// ```
1193 fn mul<V: Into<T>>(self, other: V) -> Expr;
1194}
1195
1196/// A trait for database types that can be divided by each other.
1197pub trait ExprDiv<T> {
1198 /// Creates an expression that divides the field by the given value.
1199 ///
1200 /// # Examples
1201 ///
1202 /// ```
1203 /// use cot::db::query::{Expr, ExprDiv, Query};
1204 /// use cot::db::{model, query};
1205 ///
1206 /// #[model]
1207 /// struct MyModel {
1208 /// #[model(primary_key)]
1209 /// id: i32,
1210 /// };
1211 ///
1212 /// let expr = <MyModel as cot::db::Model>::Fields::id.div(2);
1213 ///
1214 /// assert_eq!(
1215 /// <Query<MyModel>>::new().filter(Expr::eq(Expr::field("id"), expr)),
1216 /// query!(MyModel, $id == $id / 2)
1217 /// );
1218 /// ```
1219 fn div<V: Into<T>>(self, other: V) -> Expr;
1220}
1221
1222/// A trait for database types that can be ordered.
1223pub trait ExprOrd<T> {
1224 /// Creates an expression that checks if the field is less than the given
1225 /// value.
1226 ///
1227 /// # Examples
1228 ///
1229 /// ```
1230 /// use cot::db::query::{Expr, ExprOrd, Query};
1231 /// use cot::db::{model, query};
1232 ///
1233 /// #[model]
1234 /// struct MyModel {
1235 /// #[model(primary_key)]
1236 /// id: i32,
1237 /// };
1238 ///
1239 /// let expr = <MyModel as cot::db::Model>::Fields::id.lt(5);
1240 ///
1241 /// assert_eq!(
1242 /// <Query<MyModel>>::new().filter(expr),
1243 /// query!(MyModel, $id < 5)
1244 /// );
1245 /// ```
1246 fn lt<V: IntoField<T>>(self, other: V) -> Expr;
1247 /// Creates an expression that checks if the field is less than or equal to
1248 /// the given value.
1249 ///
1250 /// # Examples
1251 ///
1252 /// ```
1253 /// use cot::db::query::{Expr, ExprOrd, Query};
1254 /// use cot::db::{model, query};
1255 ///
1256 /// #[model]
1257 /// struct MyModel {
1258 /// #[model(primary_key)]
1259 /// id: i32,
1260 /// };
1261 ///
1262 /// let expr = <MyModel as cot::db::Model>::Fields::id.lte(5);
1263 ///
1264 /// assert_eq!(
1265 /// <Query<MyModel>>::new().filter(expr),
1266 /// query!(MyModel, $id <= 5)
1267 /// );
1268 /// ```
1269 fn lte<V: IntoField<T>>(self, other: V) -> Expr;
1270
1271 /// Creates an expression that checks if the field is greater than the given
1272 /// value.
1273 ///
1274 /// # Examples
1275 ///
1276 /// ```
1277 /// use cot::db::query::{Expr, ExprOrd, Query};
1278 /// use cot::db::{model, query};
1279 ///
1280 /// #[model]
1281 /// struct MyModel {
1282 /// #[model(primary_key)]
1283 /// id: i32,
1284 /// };
1285 ///
1286 /// let expr = <MyModel as cot::db::Model>::Fields::id.gt(5);
1287 ///
1288 /// assert_eq!(
1289 /// <Query<MyModel>>::new().filter(expr),
1290 /// query!(MyModel, $id > 5)
1291 /// );
1292 /// ```
1293 fn gt<V: IntoField<T>>(self, other: V) -> Expr;
1294
1295 /// Creates an expression that checks if the field is greater than or equal
1296 /// to the given value.
1297 ///
1298 /// # Examples
1299 ///
1300 /// ```
1301 /// use cot::db::query::{Expr, ExprOrd, Query};
1302 /// use cot::db::{model, query};
1303 ///
1304 /// #[model]
1305 /// struct MyModel {
1306 /// #[model(primary_key)]
1307 /// id: i32,
1308 /// };
1309 ///
1310 /// let expr = <MyModel as cot::db::Model>::Fields::id.gte(5);
1311 ///
1312 /// assert_eq!(
1313 /// <Query<MyModel>>::new().filter(expr),
1314 /// query!(MyModel, $id >= 5)
1315 /// );
1316 /// ```
1317 fn gte<V: IntoField<T>>(self, other: V) -> Expr;
1318}
1319
1320impl<T: ToDbFieldValue + Ord + 'static> ExprOrd<T> for FieldRef<T> {
1321 fn lt<V: IntoField<T>>(self, other: V) -> Expr {
1322 Expr::lt(self.as_expr(), Expr::value(other.into_field()))
1323 }
1324
1325 fn lte<V: IntoField<T>>(self, other: V) -> Expr {
1326 Expr::lte(self.as_expr(), Expr::value(other.into_field()))
1327 }
1328
1329 fn gt<V: IntoField<T>>(self, other: V) -> Expr {
1330 Expr::gt(self.as_expr(), Expr::value(other.into_field()))
1331 }
1332
1333 fn gte<V: IntoField<T>>(self, other: V) -> Expr {
1334 Expr::gte(self.as_expr(), Expr::value(other.into_field()))
1335 }
1336}
1337
1338macro_rules! impl_expr {
1339 ($ty:ty, $trait:ident, $method:ident) => {
1340 impl $trait<$ty> for FieldRef<$ty> {
1341 fn $method<V: Into<$ty>>(self, other: V) -> Expr {
1342 Expr::$method(self.as_expr(), Expr::value(other.into()))
1343 }
1344 }
1345 };
1346}
1347
1348macro_rules! impl_num_expr {
1349 ($ty:ty) => {
1350 impl_expr!($ty, ExprAdd, add);
1351 impl_expr!($ty, ExprSub, sub);
1352 impl_expr!($ty, ExprMul, mul);
1353 impl_expr!($ty, ExprDiv, div);
1354 };
1355}
1356
1357impl_num_expr!(i8);
1358impl_num_expr!(i16);
1359impl_num_expr!(i32);
1360impl_num_expr!(i64);
1361impl_num_expr!(u8);
1362impl_num_expr!(u16);
1363impl_num_expr!(u32);
1364impl_num_expr!(u64);
1365impl_num_expr!(f32);
1366impl_num_expr!(f64);
1367
1368/// A trait for database types that can be converted to the field type.
1369///
1370/// This trait is mostly a helper trait to make comparisons like `$id == 5`
1371/// where `id` is of type [`Auto`] or [`ForeignKey`] easier to write and more
1372/// readable.
1373///
1374/// # Example
1375///
1376/// ```
1377/// use cot::db::query::{Expr, ExprEq, Query};
1378/// use cot::db::{Auto, model, query};
1379///
1380/// #[model]
1381/// struct MyModel {
1382/// #[model(primary_key)]
1383/// id: Auto<i32>,
1384/// };
1385///
1386/// // uses the `IntoField` trait to convert the `5` to `Auto<i32>`
1387/// let expr = <MyModel as cot::db::Model>::Fields::id.eq(5);
1388/// ```
1389pub trait IntoField<T> {
1390 /// Converts the type to the field type.
1391 fn into_field(self) -> T;
1392}
1393
1394impl<T: ToDbFieldValue> IntoField<T> for T {
1395 fn into_field(self) -> T {
1396 self
1397 }
1398}
1399
1400impl<T> IntoField<Auto<T>> for T {
1401 fn into_field(self) -> Auto<T> {
1402 Auto::fixed(self)
1403 }
1404}
1405
1406impl IntoField<String> for &str {
1407 fn into_field(self) -> String {
1408 self.to_string()
1409 }
1410}
1411
1412impl<T: Model + Send + Sync> IntoField<ForeignKey<T>> for T {
1413 fn into_field(self) -> ForeignKey<T> {
1414 ForeignKey::from(self)
1415 }
1416}
1417
1418impl<T: Model + Send + Sync> IntoField<ForeignKey<T>> for &T {
1419 fn into_field(self) -> ForeignKey<T> {
1420 ForeignKey::from(self)
1421 }
1422}
1423
1424#[cfg(test)]
1425mod tests {
1426 use cot_macros::model;
1427
1428 use super::*;
1429 use crate::db::{MockDatabaseBackend, RowsNum};
1430
1431 #[model]
1432 #[derive(std::fmt::Debug, PartialEq, Eq)]
1433 struct MockModel {
1434 #[model(primary_key)]
1435 id: i32,
1436 }
1437
1438 #[test]
1439 fn query_new() {
1440 let query: Query<MockModel> = Query::new();
1441
1442 assert!(query.filter.is_none());
1443 assert!(query.limit.is_none());
1444 assert!(query.offset.is_none());
1445 }
1446
1447 #[test]
1448 fn query_default() {
1449 let query: Query<MockModel> = Query::default();
1450
1451 assert!(query.filter.is_none());
1452 assert!(query.limit.is_none());
1453 assert!(query.offset.is_none());
1454 }
1455
1456 #[test]
1457 fn query_filter() {
1458 let mut query: Query<MockModel> = Query::new();
1459
1460 query.filter(Expr::eq(Expr::field("name"), Expr::value("John")));
1461
1462 assert!(query.filter.is_some());
1463 }
1464
1465 #[test]
1466 fn query_limit() {
1467 let mut query: Query<MockModel> = Query::new();
1468 query.limit(10);
1469 assert!(query.limit.is_some());
1470 assert_eq!(query.limit.unwrap(), 10);
1471 }
1472
1473 #[test]
1474 fn query_offset() {
1475 let mut query: Query<MockModel> = Query::new();
1476 query.offset(10);
1477 assert!(query.offset.is_some());
1478 assert_eq!(query.offset.unwrap(), 10);
1479 }
1480
1481 #[cot::test]
1482 async fn query_all() {
1483 let mut db = MockDatabaseBackend::new();
1484 db.expect_query().returning(|_| Ok(Vec::<MockModel>::new()));
1485 let query: Query<MockModel> = Query::new();
1486
1487 let result = query.all(&db).await;
1488
1489 assert_eq!(result.unwrap(), Vec::<MockModel>::new());
1490 }
1491
1492 #[cot::test]
1493 async fn query_get() {
1494 let mut db = MockDatabaseBackend::new();
1495 db.expect_get().returning(|_| Ok(Option::<MockModel>::None));
1496 let query: Query<MockModel> = Query::new();
1497
1498 let result = query.get(&db).await;
1499
1500 assert_eq!(result.unwrap(), Option::<MockModel>::None);
1501 }
1502
1503 #[cot::test]
1504 async fn query_exists() {
1505 let mut db = MockDatabaseBackend::new();
1506 db.expect_exists()
1507 .returning(|_: &Query<MockModel>| Ok(false));
1508
1509 let query: Query<MockModel> = Query::new();
1510
1511 let result = query.exists(&db).await;
1512 assert!(result.is_ok());
1513 }
1514
1515 #[cot::test]
1516 async fn query_delete() {
1517 let mut db = MockDatabaseBackend::new();
1518 db.expect_delete()
1519 .returning(|_: &Query<MockModel>| Ok(StatementResult::new(RowsNum(0))));
1520 let query: Query<MockModel> = Query::new();
1521
1522 let result = query.delete(&db).await;
1523
1524 assert!(result.is_ok());
1525 }
1526
1527 #[test]
1528 fn expr_field() {
1529 let expr = Expr::field("name");
1530 if let Expr::Field(identifier) = expr {
1531 assert_eq!(identifier.to_string(), "name");
1532 } else {
1533 panic!("Expected Expr::Field");
1534 }
1535 }
1536
1537 #[test]
1538 fn expr_value() {
1539 let expr = Expr::value(30);
1540 if let Expr::Value(value) = expr {
1541 assert_eq!(value.to_string(), "30");
1542 } else {
1543 panic!("Expected Expr::Value");
1544 }
1545 }
1546
1547 macro_rules! test_expr_constructor {
1548 ($test_name:ident, $match:ident, $constructor:ident) => {
1549 #[test]
1550 fn $test_name() {
1551 let expr = Expr::$constructor(Expr::field("name"), Expr::value("John"));
1552 if let Expr::$match(lhs, rhs) = expr {
1553 assert!(matches!(*lhs, Expr::Field(_)));
1554 assert!(matches!(*rhs, Expr::Value(_)));
1555 } else {
1556 panic!(concat!("Expected Expr::", stringify!($match)));
1557 }
1558 }
1559 };
1560 }
1561
1562 test_expr_constructor!(expr_and, And, and);
1563 test_expr_constructor!(expr_or, Or, or);
1564 test_expr_constructor!(expr_eq, Eq, eq);
1565 test_expr_constructor!(expr_ne, Ne, ne);
1566 test_expr_constructor!(expr_lt, Lt, lt);
1567 test_expr_constructor!(expr_lte, Lte, lte);
1568 test_expr_constructor!(expr_gt, Gt, gt);
1569 test_expr_constructor!(expr_gte, Gte, gte);
1570 test_expr_constructor!(expr_add, Add, add);
1571 test_expr_constructor!(expr_sub, Sub, sub);
1572 test_expr_constructor!(expr_mul, Mul, mul);
1573 test_expr_constructor!(expr_div, Div, div);
1574}