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: &'static str) -> Self {
39 match &mut self.query.columns {
40 crate::dbms::query::Select::All => {
41 self.query.columns = crate::dbms::query::Select::Columns(vec![field]);
42 }
43 crate::dbms::query::Select::Columns(cols) if !cols.contains(&field) => {
44 cols.push(field);
45 }
46 _ => {}
47 }
48 self
49 }
50
51 pub fn fields<I>(mut self, fields: I) -> Self
53 where
54 I: IntoIterator<Item = &'static str>,
55 {
56 for field in fields {
57 self = self.field(field);
58 }
59 self
60 }
61
62 pub fn all(mut self) -> Self {
64 self.query.columns = crate::dbms::query::Select::All;
65 self
66 }
67
68 pub fn with(mut self, table_relation: &'static str) -> Self {
70 if !self.query.eager_relations.contains(&table_relation) {
71 self.query.eager_relations.push(table_relation);
72 }
73 self
74 }
75
76 pub fn order_by_asc(mut self, field: &'static str) -> Self {
78 self.query.order_by.push((field, OrderDirection::Ascending));
79 self
80 }
81
82 pub fn order_by_desc(mut self, field: &'static str) -> Self {
84 self.query
85 .order_by
86 .push((field, OrderDirection::Descending));
87 self
88 }
89
90 pub fn limit(mut self, limit: usize) -> Self {
92 self.query.limit = Some(limit);
93 self
94 }
95
96 pub fn offset(mut self, offset: usize) -> Self {
98 self.query.offset = Some(offset);
99 self
100 }
101
102 pub fn filter(mut self, filter: Option<Filter>) -> Self {
104 self.query.filter = filter;
105 self
106 }
107
108 pub fn and_where(mut self, filter: Filter) -> Self {
110 self.query.filter = match self.query.filter {
111 Some(existing_filter) => Some(existing_filter.and(filter)),
112 None => Some(filter),
113 };
114 self
115 }
116
117 pub fn or_where(mut self, filter: Filter) -> Self {
119 self.query.filter = match self.query.filter {
120 Some(existing_filter) => Some(existing_filter.or(filter)),
121 None => Some(filter),
122 };
123 self
124 }
125}
126
127#[cfg(test)]
128mod tests {
129
130 use super::*;
131 use crate::dbms::value::Value;
132 use crate::tests::User;
133
134 #[test]
135 fn test_default_query_builder() {
136 let query_builder = QueryBuilder::<User>::default();
137 let query = query_builder.build();
138 assert!(matches!(query.columns, crate::dbms::query::Select::All));
139 assert!(query.eager_relations.is_empty());
140 assert!(query.filter.is_none());
141 assert!(query.order_by.is_empty());
142 assert!(query.limit.is_none());
143 assert!(query.offset.is_none());
144 }
145
146 #[test]
147 fn test_should_add_field_to_query_builder() {
148 let query_builder = QueryBuilder::<User>::default().field("id").field("name");
149
150 let query = query_builder.build();
151 assert_eq!(query.columns(), vec!["id", "name"]);
152 }
153
154 #[test]
155 fn test_should_set_fields() {
156 let query_builder = QueryBuilder::<User>::default().fields(["id", "email"]);
157
158 let query = query_builder.build();
159 assert_eq!(query.columns(), vec!["id", "email"]);
160 }
161
162 #[test]
163 fn test_should_set_all_fields() {
164 let query_builder = QueryBuilder::<User>::default().field("id").all();
165
166 let query = query_builder.build();
167 assert!(matches!(query.columns, crate::dbms::query::Select::All));
168 }
169
170 #[test]
171 fn test_should_add_eager_relation() {
172 let query_builder = QueryBuilder::<User>::default().with("posts");
173 let query = query_builder.build();
174 assert_eq!(query.eager_relations, vec!["posts"]);
175 }
176
177 #[test]
178 fn test_should_not_duplicate_eager_relation() {
179 let query_builder = QueryBuilder::<User>::default().with("posts").with("posts");
180 let query = query_builder.build();
181 assert_eq!(query.eager_relations, vec!["posts"]);
182 }
183
184 #[test]
185 fn test_should_add_order_by_clauses() {
186 let query_builder = QueryBuilder::<User>::default()
187 .order_by_asc("name")
188 .order_by_desc("created_at");
189 let query = query_builder.build();
190 assert_eq!(
191 query.order_by,
192 vec![
193 ("name", OrderDirection::Ascending),
194 ("created_at", OrderDirection::Descending)
195 ]
196 );
197 }
198
199 #[test]
200 fn test_should_set_limit_and_offset() {
201 let query_builder = QueryBuilder::<User>::default().limit(10).offset(5);
202 let query = query_builder.build();
203 assert_eq!(query.limit, Some(10));
204 assert_eq!(query.offset, Some(5));
205 }
206
207 #[test]
208 fn test_should_create_filters() {
209 let query = QueryBuilder::<User>::default()
210 .all()
211 .and_where(Filter::eq("id", Value::Uint32(1u32.into())))
212 .or_where(Filter::like("name", "John%"))
213 .build();
214
215 let filter = query.filter.expect("should have filter");
216 if let Filter::Or(left, right) = filter {
217 assert!(matches!(*left, Filter::Eq("id", Value::Uint32(_))));
218 assert!(matches!(*right, Filter::Like("name", _)));
219 } else {
220 panic!("Expected OR filter at the top level");
221 }
222 }
223}