1use crate::filter::{Filter, FilterValue};
4use crate::operations::*;
5use crate::traits::{Model, QueryEngine};
6
7pub struct QueryBuilder<E: QueryEngine, M: Model> {
12 engine: E,
13 _model: std::marker::PhantomData<M>,
14}
15
16impl<E: QueryEngine, M: Model + crate::row::FromRow> QueryBuilder<E, M> {
17 pub fn new(engine: E) -> Self {
19 Self {
20 engine,
21 _model: std::marker::PhantomData,
22 }
23 }
24
25 pub fn find_many(&self) -> FindManyOperation<E, M> {
27 FindManyOperation::new(self.engine.clone())
28 }
29
30 pub fn find_unique(&self) -> FindUniqueOperation<E, M> {
32 FindUniqueOperation::new(self.engine.clone())
33 }
34
35 pub fn find_first(&self) -> FindFirstOperation<E, M> {
37 FindFirstOperation::new(self.engine.clone())
38 }
39
40 pub fn create(&self) -> CreateOperation<E, M> {
42 CreateOperation::new(self.engine.clone())
43 }
44
45 pub fn update(&self) -> UpdateOperation<E, M> {
47 UpdateOperation::new(self.engine.clone())
48 }
49
50 pub fn delete(&self) -> DeleteOperation<E, M> {
52 DeleteOperation::new(self.engine.clone())
53 }
54
55 pub fn upsert(&self) -> UpsertOperation<E, M> {
57 UpsertOperation::new(self.engine.clone())
58 }
59
60 pub fn count(&self) -> CountOperation<E, M> {
62 CountOperation::new(self.engine.clone())
63 }
64
65 pub async fn raw(&self, sql: &str, params: Vec<FilterValue>) -> crate::error::QueryResult<u64> {
67 self.engine.execute_raw(sql, params).await
68 }
69
70 pub fn find_by_id(&self, id: impl Into<FilterValue>) -> FindUniqueOperation<E, M> {
74 let pk = *M::PRIMARY_KEY.first().unwrap_or(&"id");
75 self.find_unique()
76 .r#where(Filter::Equals(pk.into(), id.into()))
77 }
78}
79
80impl<E: QueryEngine, M: Model> Clone for QueryBuilder<E, M> {
81 fn clone(&self) -> Self {
82 Self {
83 engine: self.engine.clone(),
84 _model: std::marker::PhantomData,
85 }
86 }
87}
88
89#[cfg(test)]
90mod tests {
91 use super::*;
92 use crate::error::QueryError;
93
94 struct TestModel;
95
96 impl Model for TestModel {
97 const MODEL_NAME: &'static str = "TestModel";
98 const TABLE_NAME: &'static str = "test_models";
99 const PRIMARY_KEY: &'static [&'static str] = &["id"];
100 const COLUMNS: &'static [&'static str] = &["id", "name", "email"];
101 }
102
103 impl crate::row::FromRow for TestModel {
104 fn from_row(_row: &impl crate::row::RowRef) -> Result<Self, crate::row::RowError> {
105 Ok(TestModel)
106 }
107 }
108
109 #[derive(Clone)]
110 struct MockEngine;
111
112 impl QueryEngine for MockEngine {
113 fn dialect(&self) -> &dyn crate::dialect::SqlDialect {
114 &crate::dialect::Postgres
115 }
116
117 fn query_many<T: Model + crate::row::FromRow + Send + 'static>(
118 &self,
119 _sql: &str,
120 _params: Vec<FilterValue>,
121 ) -> crate::traits::BoxFuture<'_, crate::error::QueryResult<Vec<T>>> {
122 Box::pin(async { Ok(Vec::new()) })
123 }
124
125 fn query_one<T: Model + crate::row::FromRow + Send + 'static>(
126 &self,
127 _sql: &str,
128 _params: Vec<FilterValue>,
129 ) -> crate::traits::BoxFuture<'_, crate::error::QueryResult<T>> {
130 Box::pin(async { Err(QueryError::not_found("test")) })
131 }
132
133 fn query_optional<T: Model + crate::row::FromRow + Send + 'static>(
134 &self,
135 _sql: &str,
136 _params: Vec<FilterValue>,
137 ) -> crate::traits::BoxFuture<'_, crate::error::QueryResult<Option<T>>> {
138 Box::pin(async { Ok(None) })
139 }
140
141 fn execute_insert<T: Model + crate::row::FromRow + Send + 'static>(
142 &self,
143 _sql: &str,
144 _params: Vec<FilterValue>,
145 ) -> crate::traits::BoxFuture<'_, crate::error::QueryResult<T>> {
146 Box::pin(async { Err(QueryError::not_found("test")) })
147 }
148
149 fn execute_update<T: Model + crate::row::FromRow + Send + 'static>(
150 &self,
151 _sql: &str,
152 _params: Vec<FilterValue>,
153 ) -> crate::traits::BoxFuture<'_, crate::error::QueryResult<Vec<T>>> {
154 Box::pin(async { Ok(Vec::new()) })
155 }
156
157 fn execute_delete(
158 &self,
159 _sql: &str,
160 _params: Vec<FilterValue>,
161 ) -> crate::traits::BoxFuture<'_, crate::error::QueryResult<u64>> {
162 Box::pin(async { Ok(0) })
163 }
164
165 fn execute_raw(
166 &self,
167 _sql: &str,
168 _params: Vec<FilterValue>,
169 ) -> crate::traits::BoxFuture<'_, crate::error::QueryResult<u64>> {
170 Box::pin(async { Ok(0) })
171 }
172
173 fn count(
174 &self,
175 _sql: &str,
176 _params: Vec<FilterValue>,
177 ) -> crate::traits::BoxFuture<'_, crate::error::QueryResult<u64>> {
178 Box::pin(async { Ok(0) })
179 }
180 }
181
182 #[test]
183 fn test_query_builder_find_many() {
184 let qb = QueryBuilder::<MockEngine, TestModel>::new(MockEngine);
185 let op = qb.find_many();
186 let (sql, _) = op.build_sql(&crate::dialect::Postgres);
187 assert!(sql.contains("SELECT * FROM test_models"));
188 }
189
190 #[test]
191 fn test_query_builder_find_by_id() {
192 let qb = QueryBuilder::<MockEngine, TestModel>::new(MockEngine);
193 let op = qb.find_by_id(1i32);
194 let (sql, params) = op.build_sql(&crate::dialect::Postgres);
195 assert!(sql.contains("WHERE"));
196 assert!(sql.contains(r#""id" = $1"#));
197 assert_eq!(params.len(), 1);
198 }
199}