ic_dbms_api/dbms/query/
builder.rs1use std::marker::PhantomData;
2
3use crate::dbms::query::{Filter, OrderDirection, Query};
4use crate::dbms::table::TableSchema;
5
6#[derive(Debug, Clone)]
8pub struct QueryBuilder<T>
9where
10 T: TableSchema,
11{
12 query: Query<T>,
13 _marker: PhantomData<T>,
14}
15
16impl<T> Default for QueryBuilder<T>
17where
18 T: TableSchema,
19{
20 fn default() -> Self {
21 Self {
22 query: Query::default(),
23 _marker: PhantomData,
24 }
25 }
26}
27
28impl<T> QueryBuilder<T>
29where
30 T: TableSchema,
31{
32 pub fn build(self) -> Query<T> {
34 self.query
35 }
36
37 pub fn field(mut self, field: &str) -> Self {
39 let field = field.to_string();
40 match &mut self.query.columns {
41 crate::dbms::query::Select::All => {
42 self.query.columns = crate::dbms::query::Select::Columns(vec![field]);
43 }
44 crate::dbms::query::Select::Columns(cols) if !cols.contains(&field) => {
45 cols.push(field);
46 }
47 _ => {}
48 }
49 self
50 }
51
52 pub fn fields<I>(mut self, fields: I) -> Self
54 where
55 I: IntoIterator<Item = &'static str>,
56 {
57 for field in fields {
58 self = self.field(field);
59 }
60 self
61 }
62
63 pub fn all(mut self) -> Self {
65 self.query.columns = crate::dbms::query::Select::All;
66 self
67 }
68
69 pub fn with(mut self, table_relation: &str) -> Self {
71 let table_relation = table_relation.to_string();
72 if !self.query.eager_relations.contains(&table_relation) {
73 self.query.eager_relations.push(table_relation);
74 }
75 self
76 }
77
78 pub fn order_by_asc(mut self, field: &str) -> Self {
80 self.query
81 .order_by
82 .push((field.to_string(), OrderDirection::Ascending));
83 self
84 }
85
86 pub fn order_by_desc(mut self, field: &str) -> Self {
88 self.query
89 .order_by
90 .push((field.to_string(), OrderDirection::Descending));
91 self
92 }
93
94 pub fn limit(mut self, limit: usize) -> Self {
96 self.query.limit = Some(limit);
97 self
98 }
99
100 pub fn offset(mut self, offset: usize) -> Self {
102 self.query.offset = Some(offset);
103 self
104 }
105
106 pub fn filter(mut self, filter: Option<Filter>) -> Self {
108 self.query.filter = filter;
109 self
110 }
111
112 pub fn and_where(mut self, filter: Filter) -> Self {
114 self.query.filter = match self.query.filter {
115 Some(existing_filter) => Some(existing_filter.and(filter)),
116 None => Some(filter),
117 };
118 self
119 }
120
121 pub fn or_where(mut self, filter: Filter) -> Self {
123 self.query.filter = match self.query.filter {
124 Some(existing_filter) => Some(existing_filter.or(filter)),
125 None => Some(filter),
126 };
127 self
128 }
129}
130
131#[cfg(test)]
132mod tests {
133
134 use super::*;
135 use crate::dbms::value::Value;
136 use crate::tests::User;
137
138 #[test]
139 fn test_default_query_builder() {
140 let query_builder = QueryBuilder::<User>::default();
141 let query = query_builder.build();
142 assert!(matches!(query.columns, crate::dbms::query::Select::All));
143 assert!(query.eager_relations.is_empty());
144 assert!(query.filter.is_none());
145 assert!(query.order_by.is_empty());
146 assert!(query.limit.is_none());
147 assert!(query.offset.is_none());
148 }
149
150 #[test]
151 fn test_should_add_field_to_query_builder() {
152 let query_builder = QueryBuilder::<User>::default().field("id").field("name");
153
154 let query = query_builder.build();
155 assert_eq!(query.columns(), vec!["id", "name"]);
156 }
157
158 #[test]
159 fn test_should_set_fields() {
160 let query_builder = QueryBuilder::<User>::default().fields(["id", "email"]);
161
162 let query = query_builder.build();
163 assert_eq!(query.columns(), vec!["id", "email"]);
164 }
165
166 #[test]
167 fn test_should_set_all_fields() {
168 let query_builder = QueryBuilder::<User>::default().field("id").all();
169
170 let query = query_builder.build();
171 assert!(matches!(query.columns, crate::dbms::query::Select::All));
172 }
173
174 #[test]
175 fn test_should_add_eager_relation() {
176 let query_builder = QueryBuilder::<User>::default().with("posts");
177 let query = query_builder.build();
178 assert_eq!(query.eager_relations, vec!["posts"]);
179 }
180
181 #[test]
182 fn test_should_not_duplicate_eager_relation() {
183 let query_builder = QueryBuilder::<User>::default().with("posts").with("posts");
184 let query = query_builder.build();
185 assert_eq!(query.eager_relations, vec!["posts"]);
186 }
187
188 #[test]
189 fn test_should_add_order_by_clauses() {
190 let query_builder = QueryBuilder::<User>::default()
191 .order_by_asc("name")
192 .order_by_desc("created_at");
193 let query = query_builder.build();
194 assert_eq!(
195 query.order_by,
196 vec![
197 ("name".to_string(), OrderDirection::Ascending),
198 ("created_at".to_string(), OrderDirection::Descending)
199 ]
200 );
201 }
202
203 #[test]
204 fn test_should_set_limit_and_offset() {
205 let query_builder = QueryBuilder::<User>::default().limit(10).offset(5);
206 let query = query_builder.build();
207 assert_eq!(query.limit, Some(10));
208 assert_eq!(query.offset, Some(5));
209 }
210
211 #[test]
212 fn test_should_create_filters() {
213 let query = QueryBuilder::<User>::default()
214 .all()
215 .and_where(Filter::eq("id", Value::Uint32(1u32.into())))
216 .or_where(Filter::like("name", "John%"))
217 .build();
218
219 let filter = query.filter.expect("should have filter");
220 if let Filter::Or(left, right) = filter {
221 assert!(matches!(*left, Filter::Eq(id, Value::Uint32(_)) if id == "id"));
222 assert!(matches!(*right, Filter::Like(name, _) if name == "name"));
223 } else {
224 panic!("Expected OR filter at the top level");
225 }
226 }
227}