1use super::common::{JsonValue, StringValue};
4use serde::Serialize;
5use std::borrow::Borrow;
6use std::convert::Into;
7
8pub enum Condition {
12 Equal(JsonValue),
13 NotEqual(JsonValue),
14 LessThan(f64),
15 GreaterThan(f64),
16 LessThanOrEqual(f64),
17 GreaterThatOrEqual(f64),
18 Prefix(StringValue),
19 Range(f64, f64),
20 Contains(StringValue),
21 NotContains(StringValue),
22}
23
24fn set_postfix(key: StringValue, postfix: &str) -> StringValue {
25 format!("{}?{}", key, postfix).into()
26}
27
28impl Condition {
29 fn gen_pair(self, key: StringValue) -> (StringValue, JsonValue) {
30 match self {
31 Self::Equal(val) => (key, val),
32 Self::NotEqual(val) => (set_postfix(key, "ne"), val),
33 Self::LessThan(val) => (set_postfix(key, "lt"), val.into()),
34 Self::GreaterThan(val) => (set_postfix(key, "gt"), val.into()),
35 Self::LessThanOrEqual(val) => (set_postfix(key, "lte"), val.into()),
36 Self::GreaterThatOrEqual(val) => (set_postfix(key, "gte"), val.into()),
37 Self::Prefix(val) => (set_postfix(key, "pfx"), val.into()),
38 Self::Range(val1, val2) => (set_postfix(key, "r"), serde_json::json!([val1, val2])),
39 Self::Contains(val) => (set_postfix(key, "contains"), val.into()),
40 Self::NotContains(val) => (set_postfix(key, "not_contains"), val.into()),
41 }
42 }
43}
44
45impl Condition {
47 pub fn equal<T>(value: T) -> serde_json::Result<Condition>
48 where
49 T: Serialize,
50 {
51 let json_val = serde_json::to_value(value)?;
52 Ok(Self::Equal(json_val))
53 }
54
55 pub fn not_equal<T>(value: T) -> serde_json::Result<Condition>
56 where
57 T: Serialize,
58 {
59 let json_val = serde_json::to_value(value)?;
60 Ok(Self::NotEqual(json_val))
61 }
62
63 pub fn less_than<T>(value: T) -> Condition
64 where
65 T: Into<f64>,
66 {
67 Self::LessThan(value.into())
68 }
69
70 pub fn greater_than<T>(value: T) -> Condition
71 where
72 T: Into<f64>,
73 {
74 Self::GreaterThan(value.into())
75 }
76
77 pub fn less_than_or_equal<T>(value: T) -> Condition
78 where
79 T: Into<f64>,
80 {
81 Self::LessThanOrEqual(value.into())
82 }
83
84 pub fn greater_than_or_equal<T>(value: T) -> Condition
85 where
86 T: Into<f64>,
87 {
88 Self::GreaterThatOrEqual(value.into())
89 }
90
91 pub fn prefix<T>(value: T) -> Condition
92 where
93 T: Into<StringValue>,
94 {
95 Self::Prefix(value.into())
96 }
97
98 pub fn range<T>(start: T, end: T) -> Condition
99 where
100 T: Into<f64>,
101 {
102 Self::Range(start.into(), end.into())
103 }
104
105 pub fn contains<T>(value: T) -> Condition
106 where
107 T: Into<StringValue>,
108 {
109 Self::Contains(value.into())
110 }
111
112 pub fn not_contains<T>(value: T) -> Condition
113 where
114 T: Into<StringValue>,
115 {
116 Self::NotContains(value.into())
117 }
118}
119
120impl From<Condition> for serde_json::Result<Condition> {
123 fn from(condition: Condition) -> serde_json::Result<Condition> {
124 Ok(condition)
125 }
126}
127
128pub struct Query {
130 conditions: Vec<Vec<(StringValue, serde_json::Result<Condition>)>>,
133}
134
135impl Query {
136 pub fn init() -> Self {
138 Self { conditions: vec![] }
139 }
140
141 pub fn on<K, V>(mut self, key: K, condition: V) -> Self
143 where
144 K: Into<StringValue>,
145 V: Into<serde_json::Result<Condition>>,
146 {
147 if let None = self.conditions.last() {
148 self.conditions.push(vec![]);
149 }
150 if let Some(and) = self.conditions.last_mut() {
151 and.push((key.into(), condition.into()));
152 }
153 self
154 }
155
156 pub fn either(mut self) -> Self {
158 if let Some(and) = self.conditions.last_mut() {
159 if and.len() > 0 {
160 self.conditions.push(vec![]);
161 }
162 }
163 self
164 }
165
166 pub(crate) fn render(self) -> serde_json::Result<JsonValue> {
167 let mut target = vec![];
168 for condition in self.conditions {
169 let mut target_obj = serde_json::json!({});
170 for and in condition {
171 let (key, val_result) = and;
172 let val = val_result?;
173 let (key, val) = val.gen_pair(key);
174 let key: &str = key.borrow();
175 target_obj[key] = val;
176 }
177 target.push(target_obj);
178 }
179 serde_json::to_value(target)
180 }
181}
182
183#[cfg(test)]
184mod tests {
185 use super::*;
186 use serde::Serialize;
187
188 #[test]
189 fn render_for_all_condition_types() {
190 let query = Query::init()
191 .on("name", Condition::equal("Anna"))
192 .on("surname", Condition::not_equal("Kowal"))
193 .on("count", Condition::less_than(10))
194 .on("likes", Condition::greater_than(10))
195 .on("watchers", Condition::greater_than_or_equal(78))
196 .on("customers", Condition::less_than_or_equal(4))
197 .on("homepage", Condition::prefix("https"))
198 .on("age", Condition::range(23, 78))
199 .on("title", Condition::not_contains("car"))
200 .on("description", Condition::contains("Tom"))
201 .render()
202 .unwrap();
203
204 let target_query = serde_json::json!([
205 {
206 "name": "Anna",
207 "surname?ne": "Kowal",
208 "count?lt": 10.,
209 "likes?gt": 10.,
210 "watchers?gte": 78.,
211 "customers?lte": 4.,
212 "homepage?pfx": "https",
213 "age?r": [23., 78.],
214 "title?not_contains": "car",
215 "description?contains": "Tom"
216 },
217 ]);
218
219 assert_eq!(query, target_query);
220 }
221
222 #[test]
223 fn render_with_either_statements() {
224 let query = Query::init()
225 .on("age", Condition::greater_than(50))
226 .either()
227 .on("hometown", Condition::equal("Greenville"))
228 .render()
229 .unwrap();
230
231 let target_query = serde_json::json!([
232 {
233 "age?gt": 50.,
234 },
235 {
236 "hometown": "Greenville",
237 }
238 ]);
239
240 assert_eq!(query, target_query);
241 }
242
243 #[test]
244 fn render_with_redundant_either_statements() {
245 let query = Query::init()
246 .either()
247 .on("age", Condition::equal(15))
248 .either()
249 .either()
250 .on("name", Condition::not_contains("om"))
251 .either()
252 .either()
253 .either();
254
255 assert_eq!(query.conditions.len(), 3);
256
257 let query = query.render().unwrap();
258
259 let target_query = serde_json::json!([
260 {
261 "age": 15,
262 },
263 {
264 "name?not_contains": "om",
265 },
266 {}
267 ]);
268
269 assert_eq!(query, target_query);
270 }
271
272 #[test]
273 fn render_with_complex_obects() {
274 #[derive(Serialize)]
275 struct PersonalData {
276 name: &'static str,
277 age: u8,
278 }
279
280 let query = Query::init()
281 .on(
282 "personal_data",
283 Condition::equal(PersonalData {
284 name: "Jan",
285 age: 43,
286 }),
287 )
288 .either()
289 .on("personal_data.name", Condition::equal("Janina"))
290 .on("personal_data.age", Condition::equal(51))
291 .render()
292 .unwrap();
293
294 let target_query = serde_json::json!([
295 {
296 "personal_data": {
297 "name": "Jan",
298 "age": 43
299 },
300 },
301 {
302 "personal_data.name": "Janina",
303 "personal_data.age": 51,
304
305 },
306 ]);
307
308 assert_eq!(query, target_query);
309 }
310}