io_jmap/rfc8620/
filter.rs1use alloc::vec::Vec;
5
6use serde::{Deserialize, Serialize};
7
8#[derive(Clone, Debug, Serialize, Deserialize)]
14#[serde(untagged)]
15pub enum JmapFilter<C> {
16 Operator(JmapFilterOperator<C>),
18 Condition(C),
20}
21
22impl<C> From<C> for JmapFilter<C> {
23 fn from(condition: C) -> Self {
24 JmapFilter::Condition(condition)
25 }
26}
27
28impl<C> JmapFilter<C> {
29 pub fn and(conditions: Vec<JmapFilter<C>>) -> Self {
31 JmapFilter::Operator(JmapFilterOperator {
32 operator: JmapFilterOperatorKind::And,
33 conditions,
34 })
35 }
36
37 pub fn or(conditions: Vec<JmapFilter<C>>) -> Self {
39 JmapFilter::Operator(JmapFilterOperator {
40 operator: JmapFilterOperatorKind::Or,
41 conditions,
42 })
43 }
44
45 pub fn not(conditions: Vec<JmapFilter<C>>) -> Self {
48 JmapFilter::Operator(JmapFilterOperator {
49 operator: JmapFilterOperatorKind::Not,
50 conditions,
51 })
52 }
53}
54
55#[derive(Clone, Debug, Serialize, Deserialize)]
57#[serde(rename_all = "camelCase")]
58pub struct JmapFilterOperator<C> {
59 pub operator: JmapFilterOperatorKind,
61 pub conditions: Vec<JmapFilter<C>>,
63}
64
65#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
67#[serde(rename_all = "UPPERCASE")]
68pub enum JmapFilterOperatorKind {
69 And,
71 Or,
73 Not,
75}
76
77#[cfg(test)]
78mod tests {
79 use alloc::{string::String, vec};
80
81 use serde::{Deserialize, Serialize};
82 use serde_json::json;
83
84 use crate::rfc8620::filter::*;
85
86 #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
87 struct Cond {
88 from: Option<String>,
89 }
90
91 #[test]
92 fn condition_serializes_flat() {
93 let f: JmapFilter<Cond> = JmapFilter::Condition(Cond {
94 from: Some("alice".into()),
95 });
96 assert_eq!(
97 serde_json::to_value(&f).unwrap(),
98 json!({ "from": "alice" })
99 );
100 }
101
102 #[test]
103 fn and_serializes_with_operator_key() {
104 let f: JmapFilter<Cond> = JmapFilter::and(vec![
105 JmapFilter::Condition(Cond {
106 from: Some("a".into()),
107 }),
108 JmapFilter::Condition(Cond {
109 from: Some("b".into()),
110 }),
111 ]);
112 assert_eq!(
113 serde_json::to_value(&f).unwrap(),
114 json!({
115 "operator": "AND",
116 "conditions": [
117 { "from": "a" },
118 { "from": "b" },
119 ],
120 }),
121 );
122 }
123
124 #[test]
125 fn not_wraps_a_single_subfilter() {
126 let f: JmapFilter<Cond> = JmapFilter::not(vec![JmapFilter::Condition(Cond {
127 from: Some("a".into()),
128 })]);
129 assert_eq!(
130 serde_json::to_value(&f).unwrap(),
131 json!({
132 "operator": "NOT",
133 "conditions": [{ "from": "a" }],
134 }),
135 );
136 }
137
138 #[test]
139 fn deserialize_discriminates_on_operator_key() {
140 let v = json!({ "from": "alice" });
141 let f: JmapFilter<Cond> = serde_json::from_value(v).unwrap();
142 assert!(matches!(f, JmapFilter::Condition(Cond { from: Some(_) })));
143
144 let v = json!({
145 "operator": "OR",
146 "conditions": [{ "from": "a" }, { "from": "b" }],
147 });
148 let f: JmapFilter<Cond> = serde_json::from_value(v).unwrap();
149 assert!(matches!(
150 f,
151 JmapFilter::Operator(JmapFilterOperator {
152 operator: JmapFilterOperatorKind::Or,
153 ..
154 })
155 ));
156 }
157}