1use std::ops::{Add, BitAnd, BitOr};
2
3#[derive(Debug)]
4pub struct QueryBuilder {
5 field: String,
6 action: String,
7 value: String,
8}
9
10impl QueryBuilder {
11 pub fn get_sql(&self) -> String {
12 let field = if self.field.is_empty() || self.field.starts_with('(') {
13 self.field.clone()
14 } else {
15 format!("`{}`", self.field)
16 };
17 let action = if self.action.is_empty() {
18 self.action.clone()
19 } else {
20 format!("{} ", self.action)
21 };
22 format!("{} {}{}", field, action, self.value)
23 }
24}
25
26#[derive(Debug)]
27pub struct QueryBuilderGroup {
28 group: Vec<QueryBuilder>,
29}
30
31impl QueryBuilderGroup {
32 pub fn get_sql(&self) -> String {
33 self.group
34 .iter()
35 .map(|builder| builder.get_sql())
36 .collect::<Vec<String>>()
37 .join(" AND ")
38 }
39
40 pub fn get_query(&self) -> String {
41 format!("WHERE {}", self.get_sql())
42 }
43}
44
45impl Add for QueryBuilderGroup {
46 type Output = QueryBuilderGroup;
47
48 fn add(self, other: QueryBuilderGroup) -> QueryBuilderGroup {
49 let QueryBuilderGroup { mut group } = self;
50 group.extend(other.group);
51 QueryBuilderGroup { group }
52 }
53}
54
55impl BitAnd for QueryBuilderGroup {
56 type Output = QueryBuilderGroup;
57
58 fn bitand(self, other: QueryBuilderGroup) -> QueryBuilderGroup {
59 let builder = QueryBuilder {
60 field: format!("({})", self.get_sql()),
61 action: "AND".to_string(),
62 value: format!("({})", other.get_sql()),
63 };
64 QueryBuilderGroup {
65 group: vec![builder],
66 }
67 }
68}
69
70impl BitOr for QueryBuilderGroup {
71 type Output = QueryBuilderGroup;
72
73 fn bitor(self, other: QueryBuilderGroup) -> QueryBuilderGroup {
74 let builder = QueryBuilder {
75 field: format!("({})", self.get_sql()),
76 action: "OR".to_string(),
77 value: format!("({})", other.get_sql()),
78 };
79 QueryBuilderGroup {
80 group: vec![builder],
81 }
82 }
83}
84
85pub trait Query {
86 fn get_field() -> String;
87 #[inline]
88 fn parse<T>(value: T) -> String
89 where
90 T: ToString,
91 {
92 match value.to_string().parse::<f64>().map(|_| value.to_string()) {
93 Ok(value) => value,
94 Err(_) => format!("'{}'", value.to_string()),
95 }
96 }
97 fn query_eq<T>(value: T) -> QueryBuilderGroup
98 where
99 T: ToString,
100 {
101 QueryBuilderGroup {
102 group: vec![QueryBuilder {
103 field: Self::get_field(),
104 action: "=".to_string(),
105 value: Self::parse(value),
106 }],
107 }
108 }
109 fn query_ne<T>(value: T) -> QueryBuilderGroup
110 where
111 T: ToString,
112 {
113 QueryBuilderGroup {
114 group: vec![QueryBuilder {
115 field: Self::get_field(),
116 action: "!=".to_string(),
117 value: Self::parse(value),
118 }],
119 }
120 }
121 fn query_gt<T>(value: T) -> QueryBuilderGroup
122 where
123 T: ToString,
124 {
125 QueryBuilderGroup {
126 group: vec![QueryBuilder {
127 field: Self::get_field(),
128 action: ">".to_string(),
129 value: Self::parse(value),
130 }],
131 }
132 }
133 fn gte<T>(value: T) -> QueryBuilderGroup
134 where
135 T: ToString,
136 {
137 QueryBuilderGroup {
138 group: vec![QueryBuilder {
139 field: Self::get_field(),
140 action: ">=".to_string(),
141 value: Self::parse(value),
142 }],
143 }
144 }
145 fn query_lt<T>(value: T) -> QueryBuilderGroup
146 where
147 T: ToString,
148 {
149 QueryBuilderGroup {
150 group: vec![QueryBuilder {
151 field: Self::get_field(),
152 action: "<".to_string(),
153 value: Self::parse(value),
154 }],
155 }
156 }
157 fn lte<T>(value: T) -> QueryBuilderGroup
158 where
159 T: ToString,
160 {
161 QueryBuilderGroup {
162 group: vec![QueryBuilder {
163 field: Self::get_field(),
164 action: "<=".to_string(),
165 value: Self::parse(value),
166 }],
167 }
168 }
169 fn like<T>(value: T) -> QueryBuilderGroup
170 where
171 T: ToString,
172 {
173 QueryBuilderGroup {
174 group: vec![QueryBuilder {
175 field: Self::get_field(),
176 action: "LIKE".to_string(),
177 value: format!("'%{}%'", value.to_string()),
178 }],
179 }
180 }
181 fn starts_with<T>(value: T) -> QueryBuilderGroup
182 where
183 T: ToString,
184 {
185 QueryBuilderGroup {
186 group: vec![QueryBuilder {
187 field: Self::get_field(),
188 action: "LIKE".to_string(),
189 value: format!("'{}%'", value.to_string()),
190 }],
191 }
192 }
193 fn ends_with<T>(value: T) -> QueryBuilderGroup
194 where
195 T: ToString,
196 {
197 QueryBuilderGroup {
198 group: vec![QueryBuilder {
199 field: Self::get_field(),
200 action: "LIKE".to_string(),
201 value: format!("'%{}'", value.to_string()),
202 }],
203 }
204 }
205
206 fn between<T>(value: (T, T)) -> QueryBuilderGroup
207 where
208 T: ToString,
209 {
210 QueryBuilderGroup {
211 group: vec![QueryBuilder {
212 field: Self::get_field(),
213 action: "BETWEEN".to_string(),
214 value: format!("{} AND {}", Self::parse(value.0), Self::parse(value.1)),
215 }],
216 }
217 }
218 fn r#in<T>(value: Vec<T>) -> QueryBuilderGroup
219 where
220 T: ToString,
221 {
222 QueryBuilderGroup {
223 group: vec![QueryBuilder {
224 field: Self::get_field(),
225 action: "IN".to_string(),
226 value: format!(
227 "({})",
228 value
229 .iter()
230 .map(|v| format!("'{}'", v.to_string()))
231 .collect::<Vec<String>>()
232 .join(", ")
233 ),
234 }],
235 }
236 }
237 fn is_null() -> QueryBuilderGroup {
238 QueryBuilderGroup {
239 group: vec![QueryBuilder {
240 field: Self::get_field(),
241 action: "IS".to_string(),
242 value: "NULL".to_string(),
243 }],
244 }
245 }
246 fn not_null() -> QueryBuilderGroup {
247 QueryBuilderGroup {
248 group: vec![QueryBuilder {
249 field: Self::get_field(),
250 action: "IS NOT".to_string(),
251 value: "NULL".to_string(),
252 }],
253 }
254 }
255 fn raw<T>(value: T) -> QueryBuilderGroup
256 where
257 T: ToString,
258 {
259 QueryBuilderGroup {
260 group: vec![QueryBuilder {
261 field: "".to_string(),
262 action: "".to_string(),
263 value: value.to_string(),
264 }],
265 }
266 }
267}
268
269#[cfg(test)]
270mod tests {
271 use super::*;
272
273 type User = String;
274 impl Query for User {
275 fn get_field() -> String {
276 "user".to_string()
277 }
278 }
279
280 type Age = u16;
281 impl Query for Age {
282 fn get_field() -> String {
283 "age".to_string()
284 }
285 }
286
287 #[test]
288 fn test_query_builder() {
289 let query = User::query_eq("zhangsan") & Age::query_gt(18)
290 | User::query_eq("lisi") & Age::query_lt(30);
291 assert_eq!(
292 query.get_sql(),
293 "((`user` = 'zhangsan') AND (`age` > 18)) OR ((`user` = 'lisi') AND (`age` < 30))"
294 );
295 let query = (User::query_eq("zhangsan") + Age::query_gt(18))
296 | User::query_eq("lisi") & Age::query_lt(30);
297 assert_eq!(
298 query.get_sql(),
299 "(`user` = 'zhangsan' AND `age` > 18) OR ((`user` = 'lisi') AND (`age` < 30))"
300 );
301 let query = User::query_eq("zhangsan") + Age::query_gt(18) + Age::query_lt(30);
302 assert_eq!(
303 query.get_sql(),
304 "`user` = 'zhangsan' AND `age` > 18 AND `age` < 30"
305 );
306 let query = User::query_eq("zhangsan") & Age::gte(18) & Age::lte(30);
307 assert_eq!(
308 query.get_sql(),
309 "((`user` = 'zhangsan') AND (`age` >= 18)) AND (`age` <= 30)"
310 );
311 let query = User::like("zhangsan") + Age::between((18, 30));
312 assert_eq!(
313 query.get_sql(),
314 "`user` LIKE '%zhangsan%' AND `age` BETWEEN 18 AND 30"
315 );
316 let query = User::r#in(vec!["zhangsan", "lisi"]) + Age::query_ne(18);
317 assert_eq!(
318 query.get_sql(),
319 "`user` IN ('zhangsan', 'lisi') AND `age` != 18"
320 );
321 let query = User::is_null() + Age::not_null();
322 assert_eq!(query.get_sql(), "`user` IS NULL AND `age` IS NOT NULL");
323 let query = User::starts_with("zhang") + User::ends_with("san");
324 assert_eq!(
325 query.get_sql(),
326 "`user` LIKE 'zhang%' AND `user` LIKE '%san'"
327 );
328 }
329}