1use std::collections::BTreeMap;
3use std::fmt;
4
5macro_rules! filter {
6 ($type:ident, $name:ident, $value:expr) => {
7 static $name: &str = $value;
8 pub struct $type;
9 };
10 (
11 $(#[$outer:meta])*
12 $type:ident, $name:ident, $value:expr, $($x:tt),*) => {
13 static $name: &str = $value;
14 $(#[$outer])*
15 pub struct $type;
16 $(
17 __impl_filter!($x, $type, $name);
18 )*
19
20 impl crate::filter::sealed::FilterPriv for $type {}
21 };
22}
23
24macro_rules! __impl_filter {
26 (Eq, $type:ident, $name:ident) => {
27 __impl_filter_eq!($type, $name);
28 };
29 (NotEq, $type:ident, $name:ident) => {
30 __impl_filter_ne!($type, $name);
31 };
32 (Like, $type:ident, $name:ident) => {
33 __impl_filter_like!($type, $name);
34 };
35 (In, $type:ident, $name:ident) => {
36 __impl_filter_in!($type, $name);
37 };
38 (Cmp, $type:ident, $name:ident) => {
39 __impl_filter_cmp!($type, $name);
40 };
41 (Bit, $type:ident, $name:ident) => {
42 __impl_filter_bit!($type, $name);
43 };
44 (OrderBy, $type:ident, $name:ident) => {
45 __impl_filter_order_by!($type, $name);
46 };
47}
48
49macro_rules! __impl_filter_eq {
50 ($type:ty, $name:expr) => {
51 impl crate::filter::Eq for $type {
52 fn eq<T, V>(value: V) -> crate::filter::Filter
53 where
54 T: std::fmt::Display,
55 V: Into<crate::filter::OneOrMany<T>>,
56 {
57 let op = crate::filter::Operator::Equals;
58 crate::filter::Filter::new($name, op, value)
59 }
60 }
61 };
62}
63
64macro_rules! __impl_filter_ne {
65 ($type:ty, $name:expr) => {
66 impl crate::filter::NotEq for $type {
67 fn ne<T, V>(value: V) -> crate::filter::Filter
68 where
69 T: std::fmt::Display,
70 V: Into<crate::filter::OneOrMany<T>>,
71 {
72 let op = crate::filter::Operator::Not;
73 crate::filter::Filter::new($name, op, value)
74 }
75 }
76 };
77}
78
79macro_rules! __impl_filter_like {
80 ($type:ty, $name:expr) => {
81 impl crate::filter::Like for $type {
82 fn like<T, V>(value: V) -> crate::filter::Filter
83 where
84 T: std::fmt::Display,
85 V: Into<crate::filter::OneOrMany<T>>,
86 {
87 let op = crate::filter::Operator::Like;
88 crate::filter::Filter::new($name, op, value)
89 }
90 }
91
92 impl crate::filter::NotLike for $type {
93 fn not_like<T, V>(value: V) -> crate::filter::Filter
94 where
95 T: std::fmt::Display,
96 V: Into<crate::filter::OneOrMany<T>>,
97 {
98 let op = crate::filter::Operator::NotLike;
99 crate::filter::Filter::new($name, op, value)
100 }
101 }
102 };
103}
104
105macro_rules! __impl_filter_in {
106 ($type:ty, $name:expr) => {
107 impl crate::filter::In for $type {
108 fn _in<T, V>(value: V) -> crate::filter::Filter
109 where
110 T: std::fmt::Display,
111 V: Into<crate::filter::OneOrMany<T>>,
112 {
113 let op = crate::filter::Operator::In;
114 crate::filter::Filter::new($name, op, value)
115 }
116 }
117
118 impl crate::filter::NotIn for $type {
119 fn not_in<T, V>(value: V) -> crate::filter::Filter
120 where
121 T: std::fmt::Display,
122 V: Into<crate::filter::OneOrMany<T>>,
123 {
124 let op = crate::filter::Operator::NotIn;
125 crate::filter::Filter::new($name, op, value)
126 }
127 }
128 };
129}
130
131macro_rules! __impl_filter_cmp {
132 ($type:ty, $name:expr) => {
133 impl crate::filter::Cmp for $type {
134 fn le<T, V>(value: V) -> crate::filter::Filter
135 where
136 T: std::fmt::Display,
137 V: Into<crate::filter::OneOrMany<T>>,
138 {
139 let op = crate::filter::Operator::Max;
140 crate::filter::Filter::new($name, op, value)
141 }
142
143 fn ge<T, V>(value: V) -> crate::filter::Filter
144 where
145 T: std::fmt::Display,
146 V: Into<crate::filter::OneOrMany<T>>,
147 {
148 let op = crate::filter::Operator::Min;
149 crate::filter::Filter::new($name, op, value)
150 }
151
152 fn gt<T, V>(value: V) -> crate::filter::Filter
153 where
154 T: std::fmt::Display,
155 V: Into<crate::filter::OneOrMany<T>>,
156 {
157 let op = crate::filter::Operator::GreaterThan;
158 crate::filter::Filter::new($name, op, value)
159 }
160
161 fn lt<T, V>(value: V) -> crate::filter::Filter
162 where
163 T: std::fmt::Display,
164 V: Into<crate::filter::OneOrMany<T>>,
165 {
166 let op = crate::filter::Operator::SmallerThan;
167 crate::filter::Filter::new($name, op, value)
168 }
169 }
170 };
171}
172
173macro_rules! __impl_filter_bit {
174 ($type:ty, $name:expr) => {
175 impl crate::filter::BitwiseAnd for $type {
176 fn bit_and<T, V>(value: V) -> crate::filter::Filter
177 where
178 T: std::fmt::Display,
179 V: Into<crate::filter::OneOrMany<T>>,
180 {
181 let op = crate::filter::Operator::BitwiseAnd;
182 crate::filter::Filter::new($name, op, value)
183 }
184 }
185 };
186}
187
188macro_rules! __impl_filter_order_by {
189 ($type:ty, $name:expr) => {
190 impl crate::filter::OrderBy for $type {
191 fn asc() -> crate::filter::Filter {
192 crate::filter::Filter::new_order_by_asc($name)
193 }
194
195 fn desc() -> crate::filter::Filter {
196 crate::filter::Filter::new_order_by_desc($name)
197 }
198 }
199 };
200}
201#[rustfmt::skip]
208pub mod prelude {
209 pub use super::BitwiseAnd;
210 pub use super::Cmp;
211 pub use super::OrderBy;
212 pub use super::{Eq, NotEq};
213 pub use super::{In, NotIn};
214 pub use super::{Like, NotLike};
215
216 pub use super::Filter;
217 pub use super::OneOrMany;
218
219 filter!(Fulltext, _Q, "_q", Eq);
220 filter!(Id, ID, "id", Eq, NotEq, In, Cmp, OrderBy);
221 filter!(Name, NAME, "name", Eq, NotEq, Like, In, OrderBy);
222 filter!(NameId, NAME_ID, "name_id", Eq, NotEq, Like, In, OrderBy);
223 filter!(ModId, MOD_ID, "mod_id", Eq, NotEq, In, Cmp, OrderBy);
224 filter!(Status, STATUS, "status", Eq, NotEq, In, Cmp, OrderBy);
225 filter!(DateAdded, DATE_ADDED, "date_added", Eq, NotEq, In, Cmp, OrderBy);
226 filter!(DateUpdated, DATE_UPDATED, "date_updated", Eq, NotEq, In, Cmp, OrderBy);
227 filter!(DateLive, DATE_LIVE, "date_live", Eq, NotEq, In, Cmp, OrderBy);
228 filter!(
229 SubmittedBy, SUBMITTED_BY, "submitted_by", Eq, NotEq, In, Cmp, OrderBy
231 );
232
233 pub fn with_limit(limit: usize) -> Filter {
241 Filter::with_limit(limit)
242 }
243
244 pub fn with_offset(offset: usize) -> Filter {
252 Filter::with_offset(offset)
253 }
254}
255
256pub(crate) mod sealed {
257 pub trait FilterPriv {}
258}
259
260pub trait Eq: sealed::FilterPriv {
261 fn eq<T: fmt::Display, V: Into<OneOrMany<T>>>(value: V) -> Filter;
263}
264
265pub trait NotEq: sealed::FilterPriv {
266 fn ne<T: fmt::Display, V: Into<OneOrMany<T>>>(value: V) -> Filter;
268}
269
270pub trait Like: sealed::FilterPriv {
271 fn like<T: fmt::Display, V: Into<OneOrMany<T>>>(value: V) -> Filter;
273}
274
275pub trait NotLike: sealed::FilterPriv {
276 fn not_like<T: fmt::Display, V: Into<OneOrMany<T>>>(value: V) -> Filter;
278}
279
280pub trait In: sealed::FilterPriv {
281 fn _in<T: fmt::Display, V: Into<OneOrMany<T>>>(value: V) -> Filter;
283}
284
285pub trait NotIn: sealed::FilterPriv {
286 fn not_in<T: fmt::Display, V: Into<OneOrMany<T>>>(value: V) -> Filter;
288}
289
290pub trait Cmp: sealed::FilterPriv {
291 fn le<T: fmt::Display, V: Into<OneOrMany<T>>>(value: V) -> Filter;
293
294 fn lt<T: fmt::Display, V: Into<OneOrMany<T>>>(value: V) -> Filter;
296
297 fn ge<T: fmt::Display, V: Into<OneOrMany<T>>>(value: V) -> Filter;
299
300 fn gt<T: fmt::Display, V: Into<OneOrMany<T>>>(value: V) -> Filter;
302}
303
304pub trait BitwiseAnd: sealed::FilterPriv {
305 fn bit_and<T: fmt::Display, V: Into<OneOrMany<T>>>(value: V) -> Filter;
307}
308
309pub trait OrderBy: sealed::FilterPriv {
310 fn asc() -> Filter;
312
313 fn desc() -> Filter;
315}
316
317pub fn custom_filter<S, T, V>(name: S, op: Operator, value: V) -> Filter
325where
326 S: Into<String>,
327 T: fmt::Display,
328 V: Into<OneOrMany<T>>,
329{
330 Filter::new(name, op, value)
331}
332
333pub fn custom_order_by_asc<S: Into<String>>(name: S) -> Filter {
341 Filter::new_order_by_asc(name)
342}
343
344pub fn custom_order_by_desc<S: Into<String>>(name: S) -> Filter {
352 Filter::new_order_by_desc(name)
353}
354
355#[derive(Clone, Default)]
356pub struct Filter {
357 filters: BTreeMap<(String, Operator), OneOrMany<String>>,
358 order_by: Option<Sorting>,
359 limit: Option<usize>,
360 offset: Option<usize>,
361}
362
363impl Filter {
364 pub(crate) fn new<S, T, V>(name: S, op: Operator, value: V) -> Filter
365 where
366 S: Into<String>,
367 T: fmt::Display,
368 V: Into<OneOrMany<T>>,
369 {
370 let mut filters = BTreeMap::new();
371 filters.insert((name.into(), op), value.into().to_string());
372 Filter {
373 filters,
374 ..Default::default()
375 }
376 }
377
378 pub(crate) fn new_order_by_asc<S>(name: S) -> Filter
379 where
380 S: Into<String>,
381 {
382 Filter {
383 order_by: Some(Sorting::Asc(name.into())),
384 ..Default::default()
385 }
386 }
387
388 pub(crate) fn new_order_by_desc<S>(name: S) -> Filter
389 where
390 S: Into<String>,
391 {
392 Filter {
393 order_by: Some(Sorting::Desc(name.into())),
394 ..Default::default()
395 }
396 }
397
398 pub(crate) fn with_limit(limit: usize) -> Filter {
399 Filter {
400 limit: Some(limit),
401 ..Default::default()
402 }
403 }
404
405 pub(crate) fn with_offset(offset: usize) -> Filter {
406 Filter {
407 offset: Some(offset),
408 ..Default::default()
409 }
410 }
411
412 #[must_use]
413 pub fn and(self, mut other: Filter) -> Filter {
414 let Filter { mut filters, .. } = self;
415 filters.append(&mut other.filters);
416 Filter {
417 filters,
418 order_by: other.order_by.or(self.order_by),
419 limit: other.limit.or(self.limit),
420 offset: other.offset.or(self.offset),
421 }
422 }
423
424 #[must_use]
425 pub fn order_by(self, other: Filter) -> Filter {
426 Filter {
427 order_by: other.order_by.or(self.order_by),
428 ..self
429 }
430 }
431
432 #[must_use]
433 pub fn limit(self, limit: usize) -> Filter {
434 Filter {
435 limit: Some(limit),
436 ..self
437 }
438 }
439
440 #[must_use]
441 pub fn offset(self, offset: usize) -> Filter {
442 Filter {
443 offset: Some(offset),
444 ..self
445 }
446 }
447}
448
449impl std::ops::Add for Filter {
450 type Output = Self;
451
452 fn add(self, other: Self) -> Self {
453 self.and(other)
454 }
455}
456
457impl fmt::Display for Filter {
458 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
459 match serde_json::to_string(&self) {
460 Ok(s) => f.write_str(&s),
461 Err(_) => Err(fmt::Error),
462 }
463 }
464}
465
466impl sealed::FilterPriv for Filter {}
467
468#[doc(hidden)]
469impl serde::ser::Serialize for Filter {
470 fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
471 where
472 S: serde::ser::Serializer,
473 {
474 use serde::ser::SerializeMap;
475
476 let len = self.filters.len()
477 + self.limit.as_ref().map(|_| 1).unwrap_or_default()
478 + self.offset.as_ref().map(|_| 1).unwrap_or_default()
479 + self.order_by.as_ref().map(|_| 1).unwrap_or_default();
480
481 let mut map = serializer.serialize_map(Some(len))?;
482 for ((name, op), value) in &self.filters {
483 map.serialize_key(&format!("{name}{op}"))?;
484 match value {
485 OneOrMany::One(v) => map.serialize_value(v)?,
486 OneOrMany::Many(v) => {
487 let mut iter = v.iter().peekable();
488 let mut value = String::new();
489
490 while let Some(s) = iter.next() {
491 value.push_str(s);
492
493 if iter.peek().is_some() {
494 value.push(',');
495 }
496 }
497
498 map.serialize_value(&value)?;
499 }
500 }
501 }
502 if let Some(ref limit) = self.limit {
503 map.serialize_entry("_limit", limit)?;
504 }
505 if let Some(ref offset) = self.offset {
506 map.serialize_entry("_offset", offset)?;
507 }
508 if let Some(ref order_by) = self.order_by {
509 map.serialize_entry("_sort", &order_by.to_string())?;
510 }
511 map.end()
512 }
513}
514
515#[derive(Clone)]
516enum Sorting {
517 Asc(String),
518 Desc(String),
519}
520
521impl fmt::Display for Sorting {
522 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
523 use std::fmt::Write;
524
525 match self {
526 Self::Asc(field) => f.write_str(field),
527 Self::Desc(field) => {
528 f.write_char('-')?;
529 f.write_str(field)
530 }
531 }
532 }
533}
534
535#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
539pub enum Operator {
540 Equals,
542 Not,
544 Like,
546 NotLike,
548 In,
550 NotIn,
552 Min,
554 Max,
556 SmallerThan,
558 GreaterThan,
560 BitwiseAnd,
562}
563
564impl fmt::Display for Operator {
565 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
566 match self {
567 Self::Equals => fmt.write_str(""),
568 Self::Not => fmt.write_str("-not"),
569 Self::Like => fmt.write_str("-lk"),
570 Self::NotLike => fmt.write_str("-not-lk"),
571 Self::In => fmt.write_str("-in"),
572 Self::NotIn => fmt.write_str("-not-in"),
573 Self::Min => fmt.write_str("-min"),
574 Self::Max => fmt.write_str("-max"),
575 Self::SmallerThan => fmt.write_str("-st"),
576 Self::GreaterThan => fmt.write_str("-gt"),
577 Self::BitwiseAnd => fmt.write_str("-bitwise-and"),
578 }
579 }
580}
581
582#[derive(Clone, Debug)]
584pub enum OneOrMany<T>
585where
586 T: fmt::Display,
587{
588 One(T),
589 Many(Vec<T>),
590}
591
592impl<T: fmt::Display> OneOrMany<T> {
593 fn to_string(&self) -> OneOrMany<String> {
594 match self {
595 Self::One(s) => OneOrMany::One(s.to_string()),
596 Self::Many(s) => OneOrMany::Many(s.iter().map(ToString::to_string).collect()),
597 }
598 }
599}
600
601impl<T: fmt::Display> From<T> for OneOrMany<T> {
602 fn from(from: T) -> OneOrMany<T> {
603 Self::One(from)
604 }
605}
606
607impl<T: fmt::Display> From<Vec<T>> for OneOrMany<T> {
608 fn from(from: Vec<T>) -> OneOrMany<T> {
609 Self::Many(from)
610 }
611}
612
613#[cfg(test)]
614mod test {
615 #[test]
616 #[allow(dead_code)]
617 fn filters() {
618 use super::prelude::*;
619
620 filter!(GameId, GAME_ID, "game_id", Eq, NotEq, Like, In, Cmp, OrderBy);
621 filter!(NameId, NAME_ID, "name_id", Eq, NotEq, Like, In, Cmp, OrderBy);
622 filter!(BitOption, BIT_OPTION, "bit_option", Bit);
623
624 assert_eq!(GAME_ID, "game_id");
625 assert_eq!(NAME_ID, "name_id");
626
627 let f = GameId::eq(1);
628 assert_eq!(f.to_string(), r#"{"game_id":"1"}"#);
629
630 let f = GameId::_in(vec![1, 2]).and(NameId::like("foobar*"));
631 assert_eq!(
632 f.to_string(),
633 r#"{"game_id-in":"1,2","name_id-lk":"foobar*"}"#
634 );
635
636 let f = GameId::eq(1).and(GameId::eq(2)).and(GameId::ne(3));
637 assert_eq!(f.to_string(), r#"{"game_id":"2","game_id-not":"3"}"#);
638
639 let f = GameId::eq(1).order_by(NameId::asc());
640 assert_eq!(f.to_string(), r#"{"game_id":"1","_sort":"name_id"}"#);
641
642 let f = NameId::like("foo*").and(NameId::not_like("bar*"));
643 assert_eq!(
644 f.to_string(),
645 r#"{"name_id-lk":"foo*","name_id-not-lk":"bar*"}"#
646 );
647
648 let f = NameId::gt(1).and(NameId::lt(2));
649 assert_eq!(f.to_string(), r#"{"name_id-st":"2","name_id-gt":"1"}"#);
650
651 let f = NameId::ge(1).and(NameId::le(2));
652 assert_eq!(f.to_string(), r#"{"name_id-min":"1","name_id-max":"2"}"#);
653
654 let f = BitOption::bit_and(1);
655 assert_eq!(f.to_string(), r#"{"bit_option-bitwise-and":"1"}"#);
656
657 let f = NameId::desc();
658 assert_eq!(f.to_string(), r#"{"_sort":"-name_id"}"#);
659
660 let f = with_limit(10).and(with_limit(20));
661 assert_eq!(f.to_string(), r#"{"_limit":20}"#);
662
663 let f = with_offset(10).and(with_offset(20));
664 assert_eq!(f.to_string(), r#"{"_offset":20}"#);
665 }
666
667 #[test]
668 fn custom_filters() {
669 use super::prelude::*;
670 use super::*;
671
672 filter!(GameId, GAME_ID, "game_id", Eq);
673
674 let f = GameId::eq(1).and(custom_filter("foo", Operator::Equals, "bar"));
675 assert_eq!(f.to_string(), r#"{"foo":"bar","game_id":"1"}"#);
676
677 let f = custom_order_by_asc("foo");
678 assert_eq!(f.to_string(), r#"{"_sort":"foo"}"#);
679 }
680
681 #[test]
682 fn std_ops_add() {
683 use super::prelude::*;
684
685 let f = Id::eq(1) + Id::eq(2);
686 assert_eq!(f.to_string(), r#"{"id":"2"}"#);
687
688 let f = Id::eq(1) + NameId::eq("foo");
689 assert_eq!(f.to_string(), r#"{"id":"1","name_id":"foo"}"#);
690 }
691}
692
693