1use serde::{Deserialize, Serialize};
8
9#[non_exhaustive]
11#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
12#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
13pub enum Operator {
14 And,
15 Or,
16 Not,
17}
18
19#[non_exhaustive]
30#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
31#[serde(untagged)]
32pub enum Filter<T> {
33 Operator(FilterOperator<T>),
35 Condition(T),
37}
38
39#[non_exhaustive]
41#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
42pub struct FilterOperator<T> {
43 pub operator: Operator,
45 pub conditions: Vec<Filter<T>>,
47}
48
49impl<T> FilterOperator<T> {
50 pub fn new(operator: Operator, conditions: Vec<Filter<T>>) -> Self {
52 Self {
53 operator,
54 conditions,
55 }
56 }
57}
58
59#[cfg(test)]
60mod tests {
61 use super::*;
62
63 #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
65 struct Cond {
66 #[serde(skip_serializing_if = "Option::is_none")]
67 pub has_keyword: Option<String>,
68 }
69
70 #[test]
74 fn filter_operator_or_roundtrip() {
75 let json =
76 r#"{"operator":"OR","conditions":[{"has_keyword":"music"},{"has_keyword":"video"}]}"#;
77 let f: Filter<Cond> = serde_json::from_str(json).expect("must parse");
78 match &f {
79 Filter::Operator(op) => {
80 assert_eq!(op.operator, Operator::Or);
81 assert_eq!(op.conditions.len(), 2);
82 }
83 other => panic!("expected Operator, got {other:?}"),
84 }
85 let back = serde_json::to_string(&f).expect("must serialize");
86 let f2: Filter<Cond> = serde_json::from_str(&back).expect("roundtrip");
87 assert_eq!(f, f2);
88 }
89
90 #[test]
93 fn filter_condition_deserialization() {
94 let json = r#"{"has_keyword":"$seen"}"#;
95 let f: Filter<Cond> = serde_json::from_str(json).expect("must parse");
96 match &f {
97 Filter::Condition(c) => assert_eq!(c.has_keyword.as_deref(), Some("$seen")),
98 other => panic!("expected Condition, got {other:?}"),
99 }
100 }
101
102 #[test]
104 fn operator_serialization() {
105 assert_eq!(serde_json::to_string(&Operator::And).unwrap(), r#""AND""#);
106 assert_eq!(serde_json::to_string(&Operator::Or).unwrap(), r#""OR""#);
107 assert_eq!(serde_json::to_string(&Operator::Not).unwrap(), r#""NOT""#);
108 }
109
110 #[test]
112 fn nested_filter_roundtrip() {
113 let filter = Filter::Operator(FilterOperator {
114 operator: Operator::And,
115 conditions: vec![
116 Filter::Operator(FilterOperator {
117 operator: Operator::Or,
118 conditions: vec![
119 Filter::Condition(Cond {
120 has_keyword: Some("a".to_owned()),
121 }),
122 Filter::Condition(Cond {
123 has_keyword: Some("b".to_owned()),
124 }),
125 ],
126 }),
127 Filter::Condition(Cond {
128 has_keyword: Some("c".to_owned()),
129 }),
130 ],
131 });
132 let json = serde_json::to_string(&filter).expect("serialize");
133 let back: Filter<Cond> = serde_json::from_str(&json).expect("deserialize");
134 assert_eq!(filter, back);
135 }
136}