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