1use std::collections::BTreeSet;
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: BTreeSet<FilterEntry>,
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 = BTreeSet::new();
371 filters.insert(FilterEntry::new(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 map_filters = |f: &FilterEntry| {
477 let value = match f.value {
478 OneOrMany::One(ref v) => v.to_string(),
479 OneOrMany::Many(ref v) => v
480 .iter()
481 .map(ToString::to_string)
482 .collect::<Vec<_>>()
483 .join(","),
484 };
485 (format!("{}{}", f.name, f.op), value)
486 };
487
488 let len = self.filters.len()
489 + self.limit.as_ref().map(|_| 1).unwrap_or_default()
490 + self.offset.as_ref().map(|_| 1).unwrap_or_default()
491 + self.order_by.as_ref().map(|_| 1).unwrap_or_default();
492
493 let mut map = serializer.serialize_map(Some(len))?;
494 for (k, v) in self.filters.iter().map(map_filters) {
495 map.serialize_entry(&k, &v)?;
496 }
497 if let Some(ref limit) = self.limit {
498 map.serialize_entry("_limit", limit)?;
499 }
500 if let Some(ref offset) = self.offset {
501 map.serialize_entry("_offset", offset)?;
502 }
503 if let Some(ref order_by) = self.order_by {
504 map.serialize_entry("_sort", &order_by.to_string())?;
505 }
506 map.end()
507 }
508}
509
510#[derive(Clone)]
511struct FilterEntry {
512 name: String,
513 op: Operator,
514 value: OneOrMany<String>,
515}
516
517impl FilterEntry {
518 fn new(name: String, op: Operator, value: OneOrMany<String>) -> FilterEntry {
519 FilterEntry { name, op, value }
520 }
521}
522
523impl std::cmp::Eq for FilterEntry {}
525
526impl PartialEq for FilterEntry {
527 fn eq(&self, other: &FilterEntry) -> bool {
528 matches!(self.cmp(other), std::cmp::Ordering::Equal)
529 }
530}
531
532impl Ord for FilterEntry {
533 fn cmp(&self, other: &FilterEntry) -> std::cmp::Ordering {
534 self.name.cmp(&other.name).then(self.op.cmp(&other.op))
535 }
536}
537
538impl PartialOrd for FilterEntry {
539 fn partial_cmp(&self, other: &FilterEntry) -> Option<std::cmp::Ordering> {
540 Some(self.cmp(other))
541 }
542}
543#[derive(Clone)]
546enum Sorting {
547 Asc(String),
548 Desc(String),
549}
550
551impl fmt::Display for Sorting {
552 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
553 use std::fmt::Write;
554
555 match self {
556 Self::Asc(field) => f.write_str(field),
557 Self::Desc(field) => {
558 f.write_char('-')?;
559 f.write_str(field)
560 }
561 }
562 }
563}
564
565#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
569pub enum Operator {
570 Equals,
572 Not,
574 Like,
576 NotLike,
578 In,
580 NotIn,
582 Min,
584 Max,
586 SmallerThan,
588 GreaterThan,
590 BitwiseAnd,
592}
593
594impl fmt::Display for Operator {
595 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
596 match self {
597 Self::Equals => fmt.write_str(""),
598 Self::Not => fmt.write_str("-not"),
599 Self::Like => fmt.write_str("-lk"),
600 Self::NotLike => fmt.write_str("-not-lk"),
601 Self::In => fmt.write_str("-in"),
602 Self::NotIn => fmt.write_str("-not-in"),
603 Self::Min => fmt.write_str("-min"),
604 Self::Max => fmt.write_str("-max"),
605 Self::SmallerThan => fmt.write_str("-st"),
606 Self::GreaterThan => fmt.write_str("-gt"),
607 Self::BitwiseAnd => fmt.write_str("-bitwise-and"),
608 }
609 }
610}
611
612#[derive(Clone, Debug)]
614pub enum OneOrMany<T>
615where
616 T: fmt::Display,
617{
618 One(T),
619 Many(Vec<T>),
620}
621
622impl<T: fmt::Display> OneOrMany<T> {
623 fn to_string(&self) -> OneOrMany<String> {
624 match self {
625 Self::One(s) => OneOrMany::One(s.to_string()),
626 Self::Many(s) => OneOrMany::Many(s.iter().map(ToString::to_string).collect()),
627 }
628 }
629}
630
631impl<T: fmt::Display> From<T> for OneOrMany<T> {
632 fn from(from: T) -> OneOrMany<T> {
633 Self::One(from)
634 }
635}
636
637impl<T: fmt::Display> From<Vec<T>> for OneOrMany<T> {
638 fn from(from: Vec<T>) -> OneOrMany<T> {
639 Self::Many(from)
640 }
641}
642
643#[cfg(test)]
644mod test {
645 #[test]
646 #[allow(dead_code)]
647 fn filters() {
648 use super::prelude::*;
649
650 filter!(GameId, GAME_ID, "game_id", Eq, NotEq, Like, In, Cmp, OrderBy);
651 filter!(NameId, NAME_ID, "name_id", Eq, NotEq, Like, In, Cmp, OrderBy);
652 filter!(BitOption, BIT_OPTION, "bit_option", Bit);
653
654 assert_eq!(GAME_ID, "game_id");
655 assert_eq!(NAME_ID, "name_id");
656
657 let f = GameId::eq(1);
658 assert_eq!(f.to_string(), r#"{"game_id":"1"}"#);
659
660 let f = GameId::_in(vec![1, 2]).and(NameId::like("foobar*"));
661 assert_eq!(
662 f.to_string(),
663 r#"{"game_id-in":"1,2","name_id-lk":"foobar*"}"#
664 );
665
666 let f = GameId::eq(1).and(GameId::eq(2)).and(GameId::ne(3));
667 assert_eq!(f.to_string(), r#"{"game_id":"2","game_id-not":"3"}"#);
668
669 let f = GameId::eq(1).order_by(NameId::asc());
670 assert_eq!(f.to_string(), r#"{"game_id":"1","_sort":"name_id"}"#);
671
672 let f = NameId::like("foo*").and(NameId::not_like("bar*"));
673 assert_eq!(
674 f.to_string(),
675 r#"{"name_id-lk":"foo*","name_id-not-lk":"bar*"}"#
676 );
677
678 let f = NameId::gt(1).and(NameId::lt(2));
679 assert_eq!(f.to_string(), r#"{"name_id-st":"2","name_id-gt":"1"}"#);
680
681 let f = NameId::ge(1).and(NameId::le(2));
682 assert_eq!(f.to_string(), r#"{"name_id-min":"1","name_id-max":"2"}"#);
683
684 let f = BitOption::bit_and(1);
685 assert_eq!(f.to_string(), r#"{"bit_option-bitwise-and":"1"}"#);
686
687 let f = NameId::desc();
688 assert_eq!(f.to_string(), r#"{"_sort":"-name_id"}"#);
689
690 let f = with_limit(10).and(with_limit(20));
691 assert_eq!(f.to_string(), r#"{"_limit":20}"#);
692
693 let f = with_offset(10).and(with_offset(20));
694 assert_eq!(f.to_string(), r#"{"_offset":20}"#);
695 }
696
697 #[test]
698 fn custom_filters() {
699 use super::prelude::*;
700 use super::*;
701
702 filter!(GameId, GAME_ID, "game_id", Eq);
703
704 let f = GameId::eq(1).and(custom_filter("foo", Operator::Equals, "bar"));
705 assert_eq!(f.to_string(), r#"{"foo":"bar","game_id":"1"}"#);
706
707 let f = custom_order_by_asc("foo");
708 assert_eq!(f.to_string(), r#"{"_sort":"foo"}"#);
709 }
710
711 #[test]
712 fn std_ops_add() {
713 use super::prelude::*;
714
715 let f = Id::eq(1) + Id::eq(2);
716 assert_eq!(f.to_string(), r#"{"id":"2"}"#);
717
718 let f = Id::eq(1) + NameId::eq("foo");
719 assert_eq!(f.to_string(), r#"{"id":"1","name_id":"foo"}"#);
720 }
721}
722
723