1use crate::status::Status;
2use serde::{Deserialize, Serialize};
3use serde_json::Value;
4use strum::VariantNames;
5use strum_macros::EnumVariantNames;
6
7#[derive(Clone, Debug, Serialize, Deserialize, EnumVariantNames)]
8#[serde(rename_all = "snake_case")]
9#[strum(serialize_all = "snake_case")]
10#[serde(tag = "operator", content = "value")]
11pub enum Constraint {
12 StringEquals(String),
13 StringNotEquals(String),
14 StringContains(String),
15 StringContainsAll(Vec<String>),
16 StringContainsAny(Vec<String>),
17 StringDoesNotContain(String),
18 StringDoesNotContainAny(Vec<String>),
19 StringIn(Vec<String>),
20 StringNotIn(Vec<String>),
21 StringIsSubset(Vec<String>),
22 StringIsSubstring(String),
23 StringHasSubstring(String),
24 IntEquals(i64),
25 IntNotEquals(i64),
26 IntContains(i64),
27 IntContainsAll(Vec<i64>),
28 IntContainsAny(Vec<i64>),
29 IntDoesNotContain(i64),
30 IntDoesNotContainAny(Vec<i64>),
31 IntIn(Vec<i64>),
32 IntNotIn(Vec<i64>),
33 IntInRange(i64, i64),
34 IntNotInRange(i64, i64),
35 IntLessThan(i64),
36 IntLessThanInclusive(i64),
37 IntGreaterThan(i64),
38 IntGreaterThanInclusive(i64),
39 FloatEquals(f64),
40 FloatNotEquals(f64),
41 FloatContains(f64),
42 FloatDoesNotContain(f64),
43 FloatIn(Vec<f64>),
44 FloatNotIn(Vec<f64>),
45 FloatInRange(f64, f64),
46 FloatNotInRange(f64, f64),
47 FloatLessThan(f64),
48 FloatLessThanInclusive(f64),
49 FloatGreaterThan(f64),
50 FloatGreaterThanInclusive(f64),
51 BoolEquals(bool),
52}
53
54impl Constraint {
55 fn value_as_str_array(v: &Value) -> Option<Vec<&str>> {
56 v.as_array()
57 .map(|x| x.iter().filter_map(|y| y.as_str()).collect::<Vec<_>>())
58 }
59
60 fn value_as_i64_array(v: &Value) -> Option<Vec<i64>> {
61 v.as_array()
62 .map(|x| x.iter().filter_map(|y| y.as_i64()).collect::<Vec<_>>())
63 }
64
65 fn value_as_f64_array(v: &Value) -> Option<Vec<f64>> {
66 v.as_array()
67 .map(|x| x.iter().filter_map(|y| y.as_f64()).collect::<Vec<_>>())
68 }
69
70 pub fn check_value(&self, v: &Value) -> Status {
71 match *self {
72 Constraint::StringEquals(ref s) => match v.as_str() {
73 None => Status::NotMet,
74 Some(v) => {
75 if v == s {
76 Status::Met
77 } else {
78 Status::NotMet
79 }
80 }
81 },
82 Constraint::StringNotEquals(ref s) => match v.as_str() {
83 None => Status::NotMet,
84 Some(v) => {
85 if v != s {
86 Status::Met
87 } else {
88 Status::NotMet
89 }
90 }
91 },
92 Constraint::StringContains(ref s) => {
93 match Self::value_as_str_array(v) {
94 None => Status::NotMet,
95 Some(v) => {
96 if v.contains(&s.as_str()) {
97 Status::Met
98 } else {
99 Status::NotMet
100 }
101 }
102 }
103 }
104 Constraint::StringContainsAll(ref s) => {
105 match Self::value_as_str_array(v) {
106 None => Status::NotMet,
107 Some(v) => {
108 if s.iter().all(|y| v.contains(&y.as_str())) {
109 Status::Met
110 } else {
111 Status::NotMet
112 }
113 }
114 }
115 }
116 Constraint::StringContainsAny(ref s) => {
117 match Self::value_as_str_array(v) {
118 None => Status::NotMet,
119 Some(v) => {
120 if s.iter().any(|y| v.contains(&y.as_str())) {
121 Status::Met
122 } else {
123 Status::NotMet
124 }
125 }
126 }
127 }
128 Constraint::StringDoesNotContain(ref s) => {
129 match Self::value_as_str_array(v) {
130 None => Status::NotMet,
131 Some(v) => {
132 if !v.contains(&s.as_str()) {
133 Status::Met
134 } else {
135 Status::NotMet
136 }
137 }
138 }
139 }
140 Constraint::StringDoesNotContainAny(ref s) => {
141 match Self::value_as_str_array(v) {
142 None => Status::NotMet,
143 Some(v) => {
144 if s.iter().all(|y| !v.contains(&y.as_str())) {
145 Status::Met
146 } else {
147 Status::NotMet
148 }
149 }
150 }
151 }
152 Constraint::StringIn(ref ss) => match v.as_str() {
153 None => Status::NotMet,
154 Some(v) => {
155 if ss.iter().any(|s| s == v) {
156 Status::Met
157 } else {
158 Status::NotMet
159 }
160 }
161 },
162 Constraint::StringNotIn(ref ss) => match v.as_str() {
163 None => Status::NotMet,
164 Some(v) => {
165 if ss.iter().all(|s| s != v) {
166 Status::Met
167 } else {
168 Status::NotMet
169 }
170 }
171 },
172 Constraint::StringIsSubset(ref s) => {
173 match Self::value_as_str_array(v) {
174 None => Status::NotMet,
175 Some(v) => {
176 if v.into_iter().any(|y| !s.contains(&y.into())) {
177 Status::NotMet
178 } else {
179 Status::Met
180 }
181 }
182 }
183 }
184 Constraint::StringIsSubstring(ref s) => match v.as_str() {
185 None => Status::NotMet,
186 Some(v) => {
187 if s.contains(v) {
188 Status::Met
189 } else {
190 Status::NotMet
191 }
192 }
193 },
194 Constraint::StringHasSubstring(ref s) => match v.as_str() {
195 None => Status::NotMet,
196 Some(v) => {
197 if v.contains(s) {
198 Status::Met
199 } else {
200 Status::NotMet
201 }
202 }
203 },
204 Constraint::IntEquals(num) => match v.as_i64() {
205 None => Status::NotMet,
206 Some(v) => {
207 if v == num {
208 Status::Met
209 } else {
210 Status::NotMet
211 }
212 }
213 },
214 Constraint::IntNotEquals(num) => match v.as_i64() {
215 None => Status::NotMet,
216 Some(v) => {
217 if v != num {
218 Status::Met
219 } else {
220 Status::NotMet
221 }
222 }
223 },
224 Constraint::IntContains(num) => match Self::value_as_i64_array(v) {
225 None => Status::NotMet,
226 Some(v) => {
227 if v.contains(&num) {
228 Status::Met
229 } else {
230 Status::NotMet
231 }
232 }
233 },
234 Constraint::IntContainsAll(ref nums) => {
235 match Self::value_as_i64_array(v) {
236 None => Status::NotMet,
237 Some(v) => {
238 if nums.iter().all(|num| v.contains(&num)) {
239 Status::Met
240 } else {
241 Status::NotMet
242 }
243 }
244 }
245 }
246 Constraint::IntContainsAny(ref nums) => {
247 match Self::value_as_i64_array(v) {
248 None => Status::NotMet,
249 Some(v) => {
250 if nums.iter().any(|num| v.contains(&num)) {
251 Status::Met
252 } else {
253 Status::NotMet
254 }
255 }
256 }
257 }
258 Constraint::IntDoesNotContain(num) => {
259 match Self::value_as_i64_array(v) {
260 None => Status::NotMet,
261 Some(v) => {
262 if !v.contains(&num) {
263 Status::Met
264 } else {
265 Status::NotMet
266 }
267 }
268 }
269 }
270 Constraint::IntDoesNotContainAny(ref nums) => {
271 match Self::value_as_i64_array(v) {
272 None => Status::NotMet,
273 Some(v) => {
274 if nums.iter().all(|num| !v.contains(&num)) {
275 Status::Met
276 } else {
277 Status::NotMet
278 }
279 }
280 }
281 }
282 Constraint::IntIn(ref nums) => match v.as_i64() {
283 None => Status::NotMet,
284 Some(v) => {
285 if nums.iter().any(|&num| num == v) {
286 Status::Met
287 } else {
288 Status::NotMet
289 }
290 }
291 },
292 Constraint::IntNotIn(ref nums) => match v.as_i64() {
293 None => Status::NotMet,
294 Some(v) => {
295 if nums.iter().all(|&num| num != v) {
296 Status::Met
297 } else {
298 Status::NotMet
299 }
300 }
301 },
302 Constraint::IntInRange(start, end) => match v.as_i64() {
303 None => Status::NotMet,
304 Some(v) => {
305 if start <= v && v <= end {
306 Status::Met
307 } else {
308 Status::NotMet
309 }
310 }
311 },
312 Constraint::IntNotInRange(start, end) => match v.as_i64() {
313 None => Status::NotMet,
314 Some(v) => {
315 if start <= v && v <= end {
316 Status::NotMet
317 } else {
318 Status::Met
319 }
320 }
321 },
322 Constraint::IntLessThan(num) => match v.as_i64() {
323 None => Status::NotMet,
324 Some(v) => {
325 if v < num {
326 Status::Met
327 } else {
328 Status::NotMet
329 }
330 }
331 },
332 Constraint::IntLessThanInclusive(num) => match v.as_i64() {
333 None => Status::NotMet,
334 Some(v) => {
335 if v <= num {
336 Status::Met
337 } else {
338 Status::NotMet
339 }
340 }
341 },
342 Constraint::IntGreaterThan(num) => match v.as_i64() {
343 None => Status::NotMet,
344 Some(v) => {
345 if v > num {
346 Status::Met
347 } else {
348 Status::NotMet
349 }
350 }
351 },
352 Constraint::IntGreaterThanInclusive(num) => match v.as_i64() {
353 None => Status::NotMet,
354 Some(v) => {
355 if v >= num {
356 Status::Met
357 } else {
358 Status::NotMet
359 }
360 }
361 },
362 Constraint::FloatEquals(num) => match v.as_f64() {
363 None => Status::NotMet,
364 Some(v) => {
365 if (v - num).abs() < f64::EPSILON {
366 Status::Met
367 } else {
368 Status::NotMet
369 }
370 }
371 },
372 Constraint::FloatNotEquals(num) => match v.as_f64() {
373 None => Status::NotMet,
374 Some(v) => {
375 if (v - num).abs() > f64::EPSILON {
376 Status::Met
377 } else {
378 Status::NotMet
379 }
380 }
381 },
382 Constraint::FloatContains(num) => {
383 match Self::value_as_f64_array(v) {
384 None => Status::NotMet,
385 Some(v) => {
386 if v.contains(&num) {
387 Status::Met
388 } else {
389 Status::NotMet
390 }
391 }
392 }
393 }
394 Constraint::FloatDoesNotContain(num) => {
395 match Self::value_as_f64_array(v) {
396 None => Status::NotMet,
397 Some(v) => {
398 if !v.contains(&num) {
399 Status::Met
400 } else {
401 Status::NotMet
402 }
403 }
404 }
405 }
406 Constraint::FloatIn(ref nums) => match v.as_f64() {
407 None => Status::NotMet,
408 Some(v) => {
409 if nums.iter().any(|&num| (v - num).abs() < f64::EPSILON) {
410 Status::Met
411 } else {
412 Status::NotMet
413 }
414 }
415 },
416 Constraint::FloatNotIn(ref nums) => match v.as_f64() {
417 None => Status::NotMet,
418 Some(v) => {
419 if nums.iter().all(|&num| (v - num).abs() > f64::EPSILON) {
420 Status::Met
421 } else {
422 Status::NotMet
423 }
424 }
425 },
426 Constraint::FloatInRange(start, end) => match v.as_f64() {
427 None => Status::NotMet,
428 Some(v) => {
429 if start <= v && v <= end {
430 Status::Met
431 } else {
432 Status::NotMet
433 }
434 }
435 },
436 Constraint::FloatNotInRange(start, end) => match v.as_f64() {
437 None => Status::NotMet,
438 Some(v) => {
439 if start <= v && v <= end {
440 Status::NotMet
441 } else {
442 Status::Met
443 }
444 }
445 },
446 Constraint::FloatLessThan(num) => match v.as_f64() {
447 None => Status::NotMet,
448 Some(v) => {
449 if v < num {
450 Status::Met
451 } else {
452 Status::NotMet
453 }
454 }
455 },
456 Constraint::FloatLessThanInclusive(num) => match v.as_f64() {
457 None => Status::NotMet,
458 Some(v) => {
459 if v <= num {
460 Status::Met
461 } else {
462 Status::NotMet
463 }
464 }
465 },
466 Constraint::FloatGreaterThan(num) => match v.as_f64() {
467 None => Status::NotMet,
468 Some(v) => {
469 if v > num {
470 Status::Met
471 } else {
472 Status::NotMet
473 }
474 }
475 },
476 Constraint::FloatGreaterThanInclusive(num) => match v.as_f64() {
477 None => Status::NotMet,
478 Some(v) => {
479 if v >= num {
480 Status::Met
481 } else {
482 Status::NotMet
483 }
484 }
485 },
486 Constraint::BoolEquals(b) => match v.as_bool() {
487 None => Status::NotMet,
488 Some(v) => {
489 if v == b {
490 Status::Met
491 } else {
492 Status::NotMet
493 }
494 }
495 },
496 }
497 }
498
499 pub fn operators() -> &'static [&'static str] {
500 Constraint::VARIANTS
501 }
502}
503
504#[cfg(test)]
505mod tests {
506 use super::Constraint;
507
508 #[test]
509 fn available_operators() {
510 assert_eq!(Constraint::operators().len(), 40);
511 }
512}