rs_puff/
filter.rs

1use serde::ser::{SerializeSeq, Serializer};
2use serde::{Deserialize, Serialize};
3
4#[derive(Debug, Clone, PartialEq, Deserialize)]
5pub struct ContainsAllTokensParams {
6    #[serde(skip_serializing_if = "Option::is_none")]
7    pub last_as_prefix: Option<bool>,
8}
9
10#[derive(Debug, Clone, PartialEq)]
11pub enum Filter {
12    // Comparison ops: ["attr", "Op", value]
13    Eq { attr: String, value: serde_json::Value },
14    NotEq { attr: String, value: serde_json::Value },
15    Lt { attr: String, value: serde_json::Value },
16    Lte { attr: String, value: serde_json::Value },
17    Gt { attr: String, value: serde_json::Value },
18    Gte { attr: String, value: serde_json::Value },
19
20    // Array element comparison
21    AnyLt { attr: String, value: serde_json::Value },
22    AnyLte { attr: String, value: serde_json::Value },
23    AnyGt { attr: String, value: serde_json::Value },
24    AnyGte { attr: String, value: serde_json::Value },
25
26    // Set ops: ["attr", "Op", [values]]
27    In { attr: String, values: Vec<serde_json::Value> },
28    NotIn { attr: String, values: Vec<serde_json::Value> },
29    Contains { attr: String, value: serde_json::Value },
30    NotContains { attr: String, value: serde_json::Value },
31    ContainsAny { attr: String, values: Vec<serde_json::Value> },
32    NotContainsAny { attr: String, values: Vec<serde_json::Value> },
33
34    // String ops
35    Glob { attr: String, pattern: String },
36    NotGlob { attr: String, pattern: String },
37    IGlob { attr: String, pattern: String },
38    NotIGlob { attr: String, pattern: String },
39    Regex { attr: String, pattern: String },
40
41    // FTS ops
42    ContainsAllTokens { attr: String, value: String, params: Option<ContainsAllTokensParams> },
43    ContainsTokenSequence { attr: String, value: String },
44
45    // Logical ops
46    And(Vec<Filter>),
47    Or(Vec<Filter>),
48    Not(Box<Filter>),
49}
50
51impl Filter {
52    pub fn eq(attr: impl Into<String>, value: impl Into<serde_json::Value>) -> Self {
53        Filter::Eq { attr: attr.into(), value: value.into() }
54    }
55
56    pub fn not_eq(attr: impl Into<String>, value: impl Into<serde_json::Value>) -> Self {
57        Filter::NotEq { attr: attr.into(), value: value.into() }
58    }
59
60    pub fn lt(attr: impl Into<String>, value: impl Into<serde_json::Value>) -> Self {
61        Filter::Lt { attr: attr.into(), value: value.into() }
62    }
63
64    pub fn lte(attr: impl Into<String>, value: impl Into<serde_json::Value>) -> Self {
65        Filter::Lte { attr: attr.into(), value: value.into() }
66    }
67
68    pub fn gt(attr: impl Into<String>, value: impl Into<serde_json::Value>) -> Self {
69        Filter::Gt { attr: attr.into(), value: value.into() }
70    }
71
72    pub fn gte(attr: impl Into<String>, value: impl Into<serde_json::Value>) -> Self {
73        Filter::Gte { attr: attr.into(), value: value.into() }
74    }
75
76    pub fn r#in(attr: impl Into<String>, values: Vec<serde_json::Value>) -> Self {
77        Filter::In { attr: attr.into(), values }
78    }
79
80    pub fn not_in(attr: impl Into<String>, values: Vec<serde_json::Value>) -> Self {
81        Filter::NotIn { attr: attr.into(), values }
82    }
83
84    pub fn contains(attr: impl Into<String>, value: impl Into<serde_json::Value>) -> Self {
85        Filter::Contains { attr: attr.into(), value: value.into() }
86    }
87
88    pub fn contains_any(attr: impl Into<String>, values: Vec<serde_json::Value>) -> Self {
89        Filter::ContainsAny { attr: attr.into(), values }
90    }
91
92    pub fn glob(attr: impl Into<String>, pattern: impl Into<String>) -> Self {
93        Filter::Glob { attr: attr.into(), pattern: pattern.into() }
94    }
95
96    pub fn iglob(attr: impl Into<String>, pattern: impl Into<String>) -> Self {
97        Filter::IGlob { attr: attr.into(), pattern: pattern.into() }
98    }
99
100    pub fn regex(attr: impl Into<String>, pattern: impl Into<String>) -> Self {
101        Filter::Regex { attr: attr.into(), pattern: pattern.into() }
102    }
103
104    pub fn and(filters: Vec<Filter>) -> Self {
105        Filter::And(filters)
106    }
107
108    pub fn or(filters: Vec<Filter>) -> Self {
109        Filter::Or(filters)
110    }
111
112    pub fn not(filter: Filter) -> Self {
113        Filter::Not(Box::new(filter))
114    }
115
116    pub fn contains_all_tokens(attr: impl Into<String>, value: impl Into<String>) -> Self {
117        Filter::ContainsAllTokens {
118            attr: attr.into(),
119            value: value.into(),
120            params: None,
121        }
122    }
123
124    pub fn contains_all_tokens_with_params(
125        attr: impl Into<String>,
126        value: impl Into<String>,
127        params: ContainsAllTokensParams,
128    ) -> Self {
129        Filter::ContainsAllTokens {
130            attr: attr.into(),
131            value: value.into(),
132            params: Some(params),
133        }
134    }
135}
136
137impl Serialize for Filter {
138    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
139    where
140        S: Serializer,
141    {
142        match self {
143            // Binary ops: ["attr", "Op", value]
144            Filter::Eq { attr, value } => {
145                let mut seq = serializer.serialize_seq(Some(3))?;
146                seq.serialize_element(attr)?;
147                seq.serialize_element("Eq")?;
148                seq.serialize_element(value)?;
149                seq.end()
150            }
151            Filter::NotEq { attr, value } => {
152                let mut seq = serializer.serialize_seq(Some(3))?;
153                seq.serialize_element(attr)?;
154                seq.serialize_element("NotEq")?;
155                seq.serialize_element(value)?;
156                seq.end()
157            }
158            Filter::Lt { attr, value } => {
159                let mut seq = serializer.serialize_seq(Some(3))?;
160                seq.serialize_element(attr)?;
161                seq.serialize_element("Lt")?;
162                seq.serialize_element(value)?;
163                seq.end()
164            }
165            Filter::Lte { attr, value } => {
166                let mut seq = serializer.serialize_seq(Some(3))?;
167                seq.serialize_element(attr)?;
168                seq.serialize_element("Lte")?;
169                seq.serialize_element(value)?;
170                seq.end()
171            }
172            Filter::Gt { attr, value } => {
173                let mut seq = serializer.serialize_seq(Some(3))?;
174                seq.serialize_element(attr)?;
175                seq.serialize_element("Gt")?;
176                seq.serialize_element(value)?;
177                seq.end()
178            }
179            Filter::Gte { attr, value } => {
180                let mut seq = serializer.serialize_seq(Some(3))?;
181                seq.serialize_element(attr)?;
182                seq.serialize_element("Gte")?;
183                seq.serialize_element(value)?;
184                seq.end()
185            }
186            Filter::AnyLt { attr, value } => {
187                let mut seq = serializer.serialize_seq(Some(3))?;
188                seq.serialize_element(attr)?;
189                seq.serialize_element("AnyLt")?;
190                seq.serialize_element(value)?;
191                seq.end()
192            }
193            Filter::AnyLte { attr, value } => {
194                let mut seq = serializer.serialize_seq(Some(3))?;
195                seq.serialize_element(attr)?;
196                seq.serialize_element("AnyLte")?;
197                seq.serialize_element(value)?;
198                seq.end()
199            }
200            Filter::AnyGt { attr, value } => {
201                let mut seq = serializer.serialize_seq(Some(3))?;
202                seq.serialize_element(attr)?;
203                seq.serialize_element("AnyGt")?;
204                seq.serialize_element(value)?;
205                seq.end()
206            }
207            Filter::AnyGte { attr, value } => {
208                let mut seq = serializer.serialize_seq(Some(3))?;
209                seq.serialize_element(attr)?;
210                seq.serialize_element("AnyGte")?;
211                seq.serialize_element(value)?;
212                seq.end()
213            }
214            Filter::In { attr, values } => {
215                let mut seq = serializer.serialize_seq(Some(3))?;
216                seq.serialize_element(attr)?;
217                seq.serialize_element("In")?;
218                seq.serialize_element(values)?;
219                seq.end()
220            }
221            Filter::NotIn { attr, values } => {
222                let mut seq = serializer.serialize_seq(Some(3))?;
223                seq.serialize_element(attr)?;
224                seq.serialize_element("NotIn")?;
225                seq.serialize_element(values)?;
226                seq.end()
227            }
228            Filter::Contains { attr, value } => {
229                let mut seq = serializer.serialize_seq(Some(3))?;
230                seq.serialize_element(attr)?;
231                seq.serialize_element("Contains")?;
232                seq.serialize_element(value)?;
233                seq.end()
234            }
235            Filter::NotContains { attr, value } => {
236                let mut seq = serializer.serialize_seq(Some(3))?;
237                seq.serialize_element(attr)?;
238                seq.serialize_element("NotContains")?;
239                seq.serialize_element(value)?;
240                seq.end()
241            }
242            Filter::ContainsAny { attr, values } => {
243                let mut seq = serializer.serialize_seq(Some(3))?;
244                seq.serialize_element(attr)?;
245                seq.serialize_element("ContainsAny")?;
246                seq.serialize_element(values)?;
247                seq.end()
248            }
249            Filter::NotContainsAny { attr, values } => {
250                let mut seq = serializer.serialize_seq(Some(3))?;
251                seq.serialize_element(attr)?;
252                seq.serialize_element("NotContainsAny")?;
253                seq.serialize_element(values)?;
254                seq.end()
255            }
256            Filter::Glob { attr, pattern } => {
257                let mut seq = serializer.serialize_seq(Some(3))?;
258                seq.serialize_element(attr)?;
259                seq.serialize_element("Glob")?;
260                seq.serialize_element(pattern)?;
261                seq.end()
262            }
263            Filter::NotGlob { attr, pattern } => {
264                let mut seq = serializer.serialize_seq(Some(3))?;
265                seq.serialize_element(attr)?;
266                seq.serialize_element("NotGlob")?;
267                seq.serialize_element(pattern)?;
268                seq.end()
269            }
270            Filter::IGlob { attr, pattern } => {
271                let mut seq = serializer.serialize_seq(Some(3))?;
272                seq.serialize_element(attr)?;
273                seq.serialize_element("IGlob")?;
274                seq.serialize_element(pattern)?;
275                seq.end()
276            }
277            Filter::NotIGlob { attr, pattern } => {
278                let mut seq = serializer.serialize_seq(Some(3))?;
279                seq.serialize_element(attr)?;
280                seq.serialize_element("NotIGlob")?;
281                seq.serialize_element(pattern)?;
282                seq.end()
283            }
284            Filter::Regex { attr, pattern } => {
285                let mut seq = serializer.serialize_seq(Some(3))?;
286                seq.serialize_element(attr)?;
287                seq.serialize_element("Regex")?;
288                seq.serialize_element(pattern)?;
289                seq.end()
290            }
291            Filter::ContainsAllTokens { attr, value, params } => {
292                if let Some(p) = params {
293                    let mut seq = serializer.serialize_seq(Some(4))?;
294                    seq.serialize_element(attr)?;
295                    seq.serialize_element("ContainsAllTokens")?;
296                    seq.serialize_element(value)?;
297                    seq.serialize_element(p)?;
298                    seq.end()
299                } else {
300                    let mut seq = serializer.serialize_seq(Some(3))?;
301                    seq.serialize_element(attr)?;
302                    seq.serialize_element("ContainsAllTokens")?;
303                    seq.serialize_element(value)?;
304                    seq.end()
305                }
306            }
307            Filter::ContainsTokenSequence { attr, value } => {
308                let mut seq = serializer.serialize_seq(Some(3))?;
309                seq.serialize_element(attr)?;
310                seq.serialize_element("ContainsTokenSequence")?;
311                seq.serialize_element(value)?;
312                seq.end()
313            }
314            // Logical ops
315            Filter::And(filters) => {
316                let mut seq = serializer.serialize_seq(Some(2))?;
317                seq.serialize_element("And")?;
318                seq.serialize_element(filters)?;
319                seq.end()
320            }
321            Filter::Or(filters) => {
322                let mut seq = serializer.serialize_seq(Some(2))?;
323                seq.serialize_element("Or")?;
324                seq.serialize_element(filters)?;
325                seq.end()
326            }
327            Filter::Not(filter) => {
328                let mut seq = serializer.serialize_seq(Some(2))?;
329                seq.serialize_element("Not")?;
330                seq.serialize_element(filter)?;
331                seq.end()
332            }
333        }
334    }
335}
336
337impl Serialize for ContainsAllTokensParams {
338    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
339    where
340        S: Serializer,
341    {
342        use serde::ser::SerializeMap;
343        let mut map = serializer.serialize_map(None)?;
344        if let Some(v) = self.last_as_prefix {
345            map.serialize_entry("last_as_prefix", &v)?;
346        }
347        map.end()
348    }
349}
350
351#[cfg(test)]
352mod tests {
353    use super::*;
354
355    #[test]
356    fn test_eq_serialization() {
357        let f = Filter::eq("name", "foo");
358        let json = serde_json::to_string(&f).unwrap();
359        assert_eq!(json, r#"["name","Eq","foo"]"#);
360    }
361
362    #[test]
363    fn test_not_eq_serialization() {
364        let f = Filter::not_eq("status", "deleted");
365        let json = serde_json::to_string(&f).unwrap();
366        assert_eq!(json, r#"["status","NotEq","deleted"]"#);
367    }
368
369    #[test]
370    fn test_comparison_ops() {
371        assert_eq!(
372            serde_json::to_string(&Filter::lt("age", 30)).unwrap(),
373            r#"["age","Lt",30]"#
374        );
375        assert_eq!(
376            serde_json::to_string(&Filter::lte("age", 30)).unwrap(),
377            r#"["age","Lte",30]"#
378        );
379        assert_eq!(
380            serde_json::to_string(&Filter::gt("score", 0.5)).unwrap(),
381            r#"["score","Gt",0.5]"#
382        );
383        assert_eq!(
384            serde_json::to_string(&Filter::gte("score", 0.5)).unwrap(),
385            r#"["score","Gte",0.5]"#
386        );
387    }
388
389    #[test]
390    fn test_and_serialization() {
391        let f = Filter::and(vec![
392            Filter::eq("name", "foo"),
393            Filter::gt("age", 18),
394        ]);
395        let json = serde_json::to_string(&f).unwrap();
396        assert_eq!(json, r#"["And",[["name","Eq","foo"],["age","Gt",18]]]"#);
397    }
398
399    #[test]
400    fn test_or_serialization() {
401        let f = Filter::or(vec![
402            Filter::eq("role", "admin"),
403            Filter::eq("role", "mod"),
404        ]);
405        let json = serde_json::to_string(&f).unwrap();
406        assert_eq!(json, r#"["Or",[["role","Eq","admin"],["role","Eq","mod"]]]"#);
407    }
408
409    #[test]
410    fn test_not_serialization() {
411        let f = Filter::not(Filter::eq("deleted", true));
412        let json = serde_json::to_string(&f).unwrap();
413        assert_eq!(json, r#"["Not",["deleted","Eq",true]]"#);
414    }
415
416    #[test]
417    fn test_in_serialization() {
418        let f = Filter::r#in("status", vec!["active".into(), "pending".into()]);
419        let json = serde_json::to_string(&f).unwrap();
420        assert_eq!(json, r#"["status","In",["active","pending"]]"#);
421    }
422
423    #[test]
424    fn test_not_in_serialization() {
425        let f = Filter::not_in("status", vec!["deleted".into()]);
426        let json = serde_json::to_string(&f).unwrap();
427        assert_eq!(json, r#"["status","NotIn",["deleted"]]"#);
428    }
429
430    #[test]
431    fn test_contains_serialization() {
432        let f = Filter::contains("tags", "rust");
433        let json = serde_json::to_string(&f).unwrap();
434        assert_eq!(json, r#"["tags","Contains","rust"]"#);
435    }
436
437    #[test]
438    fn test_contains_any_serialization() {
439        let f = Filter::contains_any("tags", vec!["rust".into(), "go".into()]);
440        let json = serde_json::to_string(&f).unwrap();
441        assert_eq!(json, r#"["tags","ContainsAny",["rust","go"]]"#);
442    }
443
444    #[test]
445    fn test_glob_serialization() {
446        let f = Filter::glob("name", "a*");
447        let json = serde_json::to_string(&f).unwrap();
448        assert_eq!(json, r#"["name","Glob","a*"]"#);
449    }
450
451    #[test]
452    fn test_iglob_serialization() {
453        let f = Filter::iglob("name", "A*");
454        let json = serde_json::to_string(&f).unwrap();
455        assert_eq!(json, r#"["name","IGlob","A*"]"#);
456    }
457
458    #[test]
459    fn test_regex_serialization() {
460        let f = Filter::regex("email", r".*@.*\.com");
461        let json = serde_json::to_string(&f).unwrap();
462        assert_eq!(json, r#"["email","Regex",".*@.*\\.com"]"#);
463    }
464
465    #[test]
466    fn test_nested_logical_ops() {
467        let f = Filter::and(vec![
468            Filter::or(vec![
469                Filter::eq("a", 1),
470                Filter::eq("b", 2),
471            ]),
472            Filter::not(Filter::eq("c", 3)),
473        ]);
474        let json = serde_json::to_string(&f).unwrap();
475        assert_eq!(
476            json,
477            r#"["And",[["Or",[["a","Eq",1],["b","Eq",2]]],["Not",["c","Eq",3]]]]"#
478        );
479    }
480
481    #[test]
482    fn test_numeric_values() {
483        let f = Filter::eq("count", 42);
484        let json = serde_json::to_string(&f).unwrap();
485        assert_eq!(json, r#"["count","Eq",42]"#);
486
487        let f = Filter::eq("price", 19.99);
488        let json = serde_json::to_string(&f).unwrap();
489        assert_eq!(json, r#"["price","Eq",19.99]"#);
490    }
491
492    #[test]
493    fn test_null_value() {
494        let f = Filter::Eq { attr: "field".into(), value: serde_json::Value::Null };
495        let json = serde_json::to_string(&f).unwrap();
496        assert_eq!(json, r#"["field","Eq",null]"#);
497    }
498}