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> 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 #[derive(Clone)]
104 struct MockEngine;
105
106 impl QueryEngine for MockEngine {
107 fn query_many<T: Model + Send + 'static>(
108 &self,
109 _sql: &str,
110 _params: Vec<FilterValue>,
111 ) -> crate::traits::BoxFuture<'_, crate::error::QueryResult<Vec<T>>> {
112 Box::pin(async { Ok(Vec::new()) })
113 }
114
115 fn query_one<T: Model + Send + 'static>(
116 &self,
117 _sql: &str,
118 _params: Vec<FilterValue>,
119 ) -> crate::traits::BoxFuture<'_, crate::error::QueryResult<T>> {
120 Box::pin(async { Err(QueryError::not_found("test")) })
121 }
122
123 fn query_optional<T: Model + Send + 'static>(
124 &self,
125 _sql: &str,
126 _params: Vec<FilterValue>,
127 ) -> crate::traits::BoxFuture<'_, crate::error::QueryResult<Option<T>>> {
128 Box::pin(async { Ok(None) })
129 }
130
131 fn execute_insert<T: Model + Send + 'static>(
132 &self,
133 _sql: &str,
134 _params: Vec<FilterValue>,
135 ) -> crate::traits::BoxFuture<'_, crate::error::QueryResult<T>> {
136 Box::pin(async { Err(QueryError::not_found("test")) })
137 }
138
139 fn execute_update<T: Model + Send + 'static>(
140 &self,
141 _sql: &str,
142 _params: Vec<FilterValue>,
143 ) -> crate::traits::BoxFuture<'_, crate::error::QueryResult<Vec<T>>> {
144 Box::pin(async { Ok(Vec::new()) })
145 }
146
147 fn execute_delete(
148 &self,
149 _sql: &str,
150 _params: Vec<FilterValue>,
151 ) -> crate::traits::BoxFuture<'_, crate::error::QueryResult<u64>> {
152 Box::pin(async { Ok(0) })
153 }
154
155 fn execute_raw(
156 &self,
157 _sql: &str,
158 _params: Vec<FilterValue>,
159 ) -> crate::traits::BoxFuture<'_, crate::error::QueryResult<u64>> {
160 Box::pin(async { Ok(0) })
161 }
162
163 fn count(
164 &self,
165 _sql: &str,
166 _params: Vec<FilterValue>,
167 ) -> crate::traits::BoxFuture<'_, crate::error::QueryResult<u64>> {
168 Box::pin(async { Ok(0) })
169 }
170 }
171
172 #[test]
173 fn test_query_builder_find_many() {
174 let qb = QueryBuilder::<MockEngine, TestModel>::new(MockEngine);
175 let op = qb.find_many();
176 let (sql, _) = op.build_sql();
177 assert!(sql.contains("SELECT * FROM test_models"));
178 }
179
180 #[test]
181 fn test_query_builder_find_by_id() {
182 let qb = QueryBuilder::<MockEngine, TestModel>::new(MockEngine);
183 let op = qb.find_by_id(1i32);
184 let (sql, params) = op.build_sql();
185 assert!(sql.contains("WHERE"));
186 assert!(sql.contains("id = $1"));
187 assert_eq!(params.len(), 1);
188 }
189}