jmap_client/email/
query.rs

1/*
2 * Copyright Stalwart Labs LLC See the COPYING
3 * file at the top-level directory of this distribution.
4 *
5 * Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 * https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 * <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
8 * option. This file may not be copied, modified, or distributed
9 * except according to those terms.
10 */
11
12use chrono::{DateTime, Utc};
13use serde::Serialize;
14
15use crate::{
16    core::{
17        query::{self, QueryObject},
18        set::from_timestamp,
19    },
20    Set,
21};
22
23use super::{Email, QueryArguments};
24
25#[derive(Serialize, Clone, Debug)]
26#[serde(untagged)]
27pub enum Filter {
28    InMailbox {
29        #[serde(rename = "inMailbox")]
30        value: String,
31    },
32    InMailboxOtherThan {
33        #[serde(rename = "inMailboxOtherThan")]
34        value: Vec<String>,
35    },
36    Before {
37        #[serde(rename = "before")]
38        value: DateTime<Utc>,
39    },
40    After {
41        #[serde(rename = "after")]
42        value: DateTime<Utc>,
43    },
44    MinSize {
45        #[serde(rename = "minSize")]
46        value: u32,
47    },
48    MaxSize {
49        #[serde(rename = "maxSize")]
50        value: u32,
51    },
52    AllInThreadHaveKeyword {
53        #[serde(rename = "allInThreadHaveKeyword")]
54        value: String,
55    },
56    SomeInThreadHaveKeyword {
57        #[serde(rename = "someInThreadHaveKeyword")]
58        value: String,
59    },
60    NoneInThreadHaveKeyword {
61        #[serde(rename = "noneInThreadHaveKeyword")]
62        value: String,
63    },
64    HasKeyword {
65        #[serde(rename = "hasKeyword")]
66        value: String,
67    },
68    NotKeyword {
69        #[serde(rename = "notKeyword")]
70        value: String,
71    },
72    HasAttachment {
73        #[serde(rename = "hasAttachment")]
74        value: bool,
75    },
76    Text {
77        #[serde(rename = "text")]
78        value: String,
79    },
80    From {
81        #[serde(rename = "from")]
82        value: String,
83    },
84    To {
85        #[serde(rename = "to")]
86        value: String,
87    },
88    Cc {
89        #[serde(rename = "cc")]
90        value: String,
91    },
92    Bcc {
93        #[serde(rename = "bcc")]
94        value: String,
95    },
96    Subject {
97        #[serde(rename = "subject")]
98        value: String,
99    },
100    Body {
101        #[serde(rename = "body")]
102        value: String,
103    },
104    Header {
105        #[serde(rename = "header")]
106        value: Vec<String>,
107    },
108
109    // Non-standard
110    Id {
111        #[serde(rename = "id")]
112        value: Vec<String>,
113    },
114    SentBefore {
115        #[serde(rename = "sentBefore")]
116        value: DateTime<Utc>,
117    },
118    SentAfter {
119        #[serde(rename = "sentAfter")]
120        value: DateTime<Utc>,
121    },
122    InThread {
123        #[serde(rename = "inThread")]
124        value: String,
125    },
126}
127
128#[derive(Serialize, Debug, Clone)]
129#[serde(tag = "property")]
130pub enum Comparator {
131    #[serde(rename = "receivedAt")]
132    ReceivedAt,
133    #[serde(rename = "size")]
134    Size,
135    #[serde(rename = "from")]
136    From,
137    #[serde(rename = "to")]
138    To,
139    #[serde(rename = "subject")]
140    Subject,
141    #[serde(rename = "sentAt")]
142    SentAt,
143    #[serde(rename = "hasKeyword")]
144    HasKeyword { keyword: String },
145    #[serde(rename = "allInThreadHaveKeyword")]
146    AllInThreadHaveKeyword { keyword: String },
147    #[serde(rename = "someInThreadHaveKeyword")]
148    SomeInThreadHaveKeyword { keyword: String },
149
150    // Non-standard
151    #[serde(rename = "cc")]
152    Cc,
153}
154
155impl Filter {
156    pub fn in_mailbox(value: impl Into<String>) -> Self {
157        Filter::InMailbox {
158            value: value.into(),
159        }
160    }
161
162    pub fn in_mailbox_other_than<U, V>(value: U) -> Self
163    where
164        U: IntoIterator<Item = V>,
165        V: Into<String>,
166    {
167        Filter::InMailboxOtherThan {
168            value: value.into_iter().map(|v| v.into()).collect(),
169        }
170    }
171
172    pub fn before(value: i64) -> Self {
173        Filter::Before {
174            value: from_timestamp(value),
175        }
176    }
177
178    pub fn after(value: i64) -> Self {
179        Filter::After {
180            value: from_timestamp(value),
181        }
182    }
183
184    pub fn min_size(value: u32) -> Self {
185        Filter::MinSize { value }
186    }
187
188    pub fn max_size(value: u32) -> Self {
189        Filter::MaxSize { value }
190    }
191
192    pub fn all_in_thread_have_keyword(value: impl Into<String>) -> Self {
193        Filter::AllInThreadHaveKeyword {
194            value: value.into(),
195        }
196    }
197
198    pub fn some_in_thread_have_keyword(value: impl Into<String>) -> Self {
199        Filter::SomeInThreadHaveKeyword {
200            value: value.into(),
201        }
202    }
203
204    pub fn none_in_thread_have_keyword(value: impl Into<String>) -> Self {
205        Filter::NoneInThreadHaveKeyword {
206            value: value.into(),
207        }
208    }
209
210    pub fn has_keyword(value: impl Into<String>) -> Self {
211        Filter::HasKeyword {
212            value: value.into(),
213        }
214    }
215
216    pub fn not_keyword(value: impl Into<String>) -> Self {
217        Filter::NotKeyword {
218            value: value.into(),
219        }
220    }
221
222    pub fn has_attachment(value: bool) -> Self {
223        Filter::HasAttachment { value }
224    }
225
226    pub fn text(value: impl Into<String>) -> Self {
227        Filter::Text {
228            value: value.into(),
229        }
230    }
231
232    pub fn from(value: impl Into<String>) -> Self {
233        Filter::From {
234            value: value.into(),
235        }
236    }
237
238    pub fn to(value: impl Into<String>) -> Self {
239        Filter::To {
240            value: value.into(),
241        }
242    }
243
244    pub fn cc(value: impl Into<String>) -> Self {
245        Filter::Cc {
246            value: value.into(),
247        }
248    }
249
250    pub fn bcc(value: impl Into<String>) -> Self {
251        Filter::Bcc {
252            value: value.into(),
253        }
254    }
255
256    pub fn subject(value: impl Into<String>) -> Self {
257        Filter::Subject {
258            value: value.into(),
259        }
260    }
261
262    pub fn body(value: impl Into<String>) -> Self {
263        Filter::Body {
264            value: value.into(),
265        }
266    }
267
268    pub fn header(header: impl Into<String>, v: Option<impl Into<String>>) -> Self {
269        let mut value = Vec::with_capacity(2);
270        value.push(header.into());
271        if let Some(v) = v {
272            value.push(v.into());
273        }
274        Filter::Header { value }
275    }
276
277    // Stalwart JMAP specific
278    pub fn id<U, V>(value: U) -> Self
279    where
280        U: IntoIterator<Item = V>,
281        V: Into<String>,
282    {
283        Filter::Id {
284            value: value.into_iter().map(|v| v.into()).collect(),
285        }
286    }
287
288    pub fn sent_before(value: i64) -> Self {
289        Filter::SentBefore {
290            value: from_timestamp(value),
291        }
292    }
293
294    pub fn sent_after(value: i64) -> Self {
295        Filter::SentAfter {
296            value: from_timestamp(value),
297        }
298    }
299
300    pub fn in_thread(value: impl Into<String>) -> Self {
301        Filter::InThread {
302            value: value.into(),
303        }
304    }
305}
306
307impl Comparator {
308    pub fn received_at() -> query::Comparator<Comparator> {
309        query::Comparator::new(Comparator::ReceivedAt)
310    }
311
312    pub fn size() -> query::Comparator<Comparator> {
313        query::Comparator::new(Comparator::Size)
314    }
315
316    pub fn from() -> query::Comparator<Comparator> {
317        query::Comparator::new(Comparator::From)
318    }
319
320    pub fn to() -> query::Comparator<Comparator> {
321        query::Comparator::new(Comparator::To)
322    }
323
324    pub fn cc() -> query::Comparator<Comparator> {
325        query::Comparator::new(Comparator::Cc)
326    }
327
328    pub fn subject() -> query::Comparator<Comparator> {
329        query::Comparator::new(Comparator::Subject)
330    }
331
332    pub fn sent_at() -> query::Comparator<Comparator> {
333        query::Comparator::new(Comparator::SentAt)
334    }
335
336    pub fn has_keyword(keyword: impl Into<String>) -> query::Comparator<Comparator> {
337        query::Comparator::new(Comparator::HasKeyword {
338            keyword: keyword.into(),
339        })
340    }
341
342    pub fn all_in_thread_have_keyword(keyword: impl Into<String>) -> query::Comparator<Comparator> {
343        query::Comparator::new(Comparator::AllInThreadHaveKeyword {
344            keyword: keyword.into(),
345        })
346    }
347
348    pub fn some_in_thread_have_keyword(
349        keyword: impl Into<String>,
350    ) -> query::Comparator<Comparator> {
351        query::Comparator::new(Comparator::SomeInThreadHaveKeyword {
352            keyword: keyword.into(),
353        })
354    }
355}
356
357impl QueryObject for Email<Set> {
358    type QueryArguments = QueryArguments;
359
360    type Filter = Filter;
361
362    type Sort = Comparator;
363}