Skip to main content

cast_core/
query.rs

1//! Query builder. Typed where clauses on top of sea-query + sqlx.
2
3use std::marker::PhantomData;
4
5use sea_query::{Expr, Order, PostgresQueryBuilder, Query, SimpleExpr};
6use sea_query_binder::SqlxBinder;
7use sqlx::{FromRow, postgres::PgRow};
8
9use crate::column::Column;
10use crate::model::Model;
11use crate::pool::Pool;
12use crate::Error;
13
14pub struct QueryBuilder<M: Model> {
15    select: sea_query::SelectStatement,
16    _marker: PhantomData<M>,
17}
18
19impl<M: Model> Default for QueryBuilder<M> {
20    fn default() -> Self {
21        Self::new()
22    }
23}
24
25impl<M: Model> QueryBuilder<M>
26where
27    for<'r> M: FromRow<'r, PgRow>,
28{
29    pub fn new() -> Self {
30        let mut select = Query::select();
31        select
32            .from(sea_query::Alias::new(M::TABLE))
33            .columns(M::COLUMNS.iter().map(|c| sea_query::Alias::new(*c)));
34        Self {
35            select,
36            _marker: PhantomData,
37        }
38    }
39
40    /// Type-safe equality: column type must match value type.
41    pub fn where_eq<T>(mut self, column: Column<M, T>, value: T) -> Self
42    where
43        T: Into<sea_query::Value>,
44    {
45        self.select
46            .and_where(Expr::col(sea_query::Alias::new(column.name())).eq(value));
47        self
48    }
49
50    pub fn where_ne<T>(mut self, column: Column<M, T>, value: T) -> Self
51    where
52        T: Into<sea_query::Value>,
53    {
54        self.select
55            .and_where(Expr::col(sea_query::Alias::new(column.name())).ne(value));
56        self
57    }
58
59    pub fn where_gt<T>(mut self, column: Column<M, T>, value: T) -> Self
60    where
61        T: Into<sea_query::Value>,
62    {
63        self.select
64            .and_where(Expr::col(sea_query::Alias::new(column.name())).gt(value));
65        self
66    }
67
68    pub fn where_lt<T>(mut self, column: Column<M, T>, value: T) -> Self
69    where
70        T: Into<sea_query::Value>,
71    {
72        self.select
73            .and_where(Expr::col(sea_query::Alias::new(column.name())).lt(value));
74        self
75    }
76
77    pub fn where_in<T, I>(mut self, column: Column<M, T>, values: I) -> Self
78    where
79        T: Into<sea_query::Value>,
80        I: IntoIterator<Item = T>,
81    {
82        self.select
83            .and_where(Expr::col(sea_query::Alias::new(column.name())).is_in(values));
84        self
85    }
86
87    pub fn where_raw(mut self, raw: SimpleExpr) -> Self {
88        self.select.and_where(raw);
89        self
90    }
91
92    pub fn order_by<T>(mut self, column: Column<M, T>, ascending: bool) -> Self {
93        self.select.order_by(
94            sea_query::Alias::new(column.name()),
95            if ascending { Order::Asc } else { Order::Desc },
96        );
97        self
98    }
99
100    pub fn order_by_asc<T>(self, column: Column<M, T>) -> Self {
101        self.order_by(column, true)
102    }
103
104    pub fn order_by_desc<T>(self, column: Column<M, T>) -> Self {
105        self.order_by(column, false)
106    }
107
108    pub fn limit(mut self, n: u64) -> Self {
109        self.select.limit(n);
110        self
111    }
112
113    pub fn offset(mut self, n: u64) -> Self {
114        self.select.offset(n);
115        self
116    }
117
118    pub async fn get(self, pool: &Pool) -> Result<Vec<M>, Error> {
119        let (sql, values) = self.select.build_sqlx(PostgresQueryBuilder);
120        let rows = sqlx::query_as_with::<_, M, _>(&sql, values)
121            .fetch_all(pool)
122            .await?;
123        Ok(rows)
124    }
125
126    pub async fn first(mut self, pool: &Pool) -> Result<Option<M>, Error> {
127        self.select.limit(1);
128        let (sql, values) = self.select.build_sqlx(PostgresQueryBuilder);
129        let row = sqlx::query_as_with::<_, M, _>(&sql, values)
130            .fetch_optional(pool)
131            .await?;
132        Ok(row)
133    }
134
135    pub async fn count(self, pool: &Pool) -> Result<i64, Error> {
136        let mut count_query = self.select.clone();
137        count_query.clear_selects();
138        count_query.expr(Expr::count(Expr::col(sea_query::Alias::new("*"))));
139        let (sql, values) = count_query.build_sqlx(PostgresQueryBuilder);
140        let (count,): (i64,) = sqlx::query_as_with(&sql, values).fetch_one(pool).await?;
141        Ok(count)
142    }
143}