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}