jmap_client/core/
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 serde::{Deserialize, Serialize};
13
14use crate::Method;
15
16use super::{request::ResultReference, Object, RequestParams};
17
18pub trait QueryObject: Object {
19    type QueryArguments: Default + Serialize;
20    type Filter: Serialize;
21    type Sort: Serialize;
22}
23
24#[derive(Debug, Clone, Serialize)]
25pub struct QueryRequest<O: QueryObject> {
26    #[serde(skip)]
27    method: (Method, usize),
28
29    #[serde(rename = "accountId")]
30    account_id: String,
31
32    #[serde(rename = "filter")]
33    #[serde(skip_serializing_if = "Option::is_none")]
34    filter: Option<Filter<O::Filter>>,
35
36    #[serde(rename = "sort")]
37    #[serde(skip_serializing_if = "Option::is_none")]
38    sort: Option<Vec<Comparator<O::Sort>>>,
39
40    #[serde(rename = "position")]
41    #[serde(skip_serializing_if = "Option::is_none")]
42    position: Option<i32>,
43
44    #[serde(rename = "anchor")]
45    #[serde(skip_serializing_if = "Option::is_none")]
46    anchor: Option<String>,
47
48    #[serde(rename = "anchorOffset")]
49    #[serde(skip_serializing_if = "Option::is_none")]
50    anchor_offset: Option<i32>,
51
52    #[serde(rename = "limit")]
53    #[serde(skip_serializing_if = "Option::is_none")]
54    limit: Option<usize>,
55
56    #[serde(rename = "calculateTotal")]
57    #[serde(skip_serializing_if = "Option::is_none")]
58    calculate_total: Option<bool>,
59
60    #[serde(flatten)]
61    arguments: O::QueryArguments,
62}
63
64#[derive(Debug, Clone, Serialize)]
65#[serde(untagged)]
66pub enum Filter<T> {
67    FilterOperator(FilterOperator<T>),
68    FilterCondition(T),
69}
70
71#[derive(Debug, Clone, Serialize)]
72pub struct FilterOperator<T> {
73    operator: Operator,
74    conditions: Vec<Filter<T>>,
75}
76
77#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
78pub enum Operator {
79    #[serde(rename = "AND")]
80    And,
81    #[serde(rename = "OR")]
82    Or,
83    #[serde(rename = "NOT")]
84    Not,
85}
86
87#[derive(Debug, Clone, Serialize)]
88pub struct Comparator<A> {
89    #[serde(rename = "isAscending")]
90    is_ascending: bool,
91
92    #[serde(skip_serializing_if = "Option::is_none")]
93    collation: Option<String>,
94
95    #[serde(flatten)]
96    arguments: A,
97}
98
99#[derive(Debug, Clone, Deserialize)]
100pub struct QueryResponse {
101    #[serde(rename = "accountId")]
102    account_id: String,
103
104    #[serde(rename = "queryState")]
105    query_state: String,
106
107    #[serde(rename = "canCalculateChanges")]
108    can_calculate_changes: Option<bool>,
109
110    #[serde(rename = "position")]
111    position: i32,
112
113    #[serde(rename = "ids")]
114    ids: Vec<String>,
115
116    #[serde(rename = "total")]
117    total: Option<usize>,
118
119    #[serde(rename = "limit")]
120    limit: Option<usize>,
121}
122
123impl<O: QueryObject> QueryRequest<O> {
124    pub fn new(params: RequestParams) -> Self {
125        QueryRequest {
126            account_id: params.account_id,
127            method: (params.method, params.call_id),
128            filter: None,
129            sort: None,
130            position: None,
131            anchor: None,
132            anchor_offset: None,
133            limit: None,
134            calculate_total: None,
135            arguments: O::QueryArguments::default(),
136        }
137    }
138
139    pub fn account_id(&mut self, account_id: impl Into<String>) -> &mut Self {
140        self.account_id = account_id.into();
141        self
142    }
143
144    pub fn filter(&mut self, filter: impl Into<Filter<O::Filter>>) -> &mut Self {
145        self.filter = Some(filter.into());
146        self
147    }
148
149    pub fn sort(&mut self, sort: impl IntoIterator<Item = Comparator<O::Sort>>) -> &mut Self {
150        self.sort = Some(sort.into_iter().collect());
151        self
152    }
153
154    pub fn position(&mut self, position: i32) -> &mut Self {
155        self.position = position.into();
156        self
157    }
158
159    pub fn anchor(&mut self, anchor: impl Into<String>) -> &mut Self {
160        self.anchor = Some(anchor.into());
161        self
162    }
163
164    pub fn anchor_offset(&mut self, anchor_offset: i32) -> &mut Self {
165        self.anchor_offset = anchor_offset.into();
166        self
167    }
168
169    pub fn limit(&mut self, limit: usize) -> &mut Self {
170        self.limit = Some(limit);
171        self
172    }
173
174    pub fn calculate_total(&mut self, calculate_total: bool) -> &mut Self {
175        self.calculate_total = Some(calculate_total);
176        self
177    }
178
179    pub fn arguments(&mut self) -> &mut O::QueryArguments {
180        &mut self.arguments
181    }
182
183    pub fn result_reference(&self) -> ResultReference {
184        ResultReference::new(self.method.0, self.method.1, "/ids")
185    }
186}
187
188impl QueryResponse {
189    pub fn account_id(&self) -> &str {
190        &self.account_id
191    }
192
193    pub fn ids(&self) -> &[String] {
194        &self.ids
195    }
196
197    pub fn id(&self, pos: usize) -> &str {
198        self.ids[pos].as_str()
199    }
200
201    pub fn take_ids(&mut self) -> Vec<String> {
202        std::mem::take(&mut self.ids)
203    }
204
205    pub fn total(&self) -> Option<usize> {
206        self.total
207    }
208
209    pub fn limit(&self) -> Option<usize> {
210        self.limit
211    }
212
213    pub fn position(&self) -> i32 {
214        self.position
215    }
216
217    pub fn take_query_state(&mut self) -> String {
218        std::mem::take(&mut self.query_state)
219    }
220
221    pub fn query_state(&self) -> &str {
222        &self.query_state
223    }
224
225    pub fn can_calculate_changes(&self) -> bool {
226        self.can_calculate_changes.unwrap_or(false)
227    }
228}
229
230impl<A> Comparator<A> {
231    pub fn new(arguments: A) -> Self {
232        Comparator {
233            is_ascending: true,
234            collation: None,
235            arguments,
236        }
237    }
238
239    pub fn descending(mut self) -> Self {
240        self.is_ascending = false;
241        self
242    }
243
244    pub fn ascending(mut self) -> Self {
245        self.is_ascending = true;
246        self
247    }
248
249    pub fn is_ascending(mut self, is_ascending: bool) -> Self {
250        self.is_ascending = is_ascending;
251        self
252    }
253
254    pub fn collation(mut self, collation: String) -> Self {
255        self.collation = Some(collation);
256        self
257    }
258}
259
260impl<T> From<FilterOperator<T>> for Filter<T> {
261    fn from(filter: FilterOperator<T>) -> Self {
262        Filter::FilterOperator(filter)
263    }
264}
265
266impl<T> From<T> for Filter<T> {
267    fn from(filter: T) -> Self {
268        Filter::FilterCondition(filter)
269    }
270}
271
272impl<T> Filter<T> {
273    pub fn operator(operator: Operator, conditions: Vec<Filter<T>>) -> Self {
274        Filter::FilterOperator(FilterOperator {
275            operator,
276            conditions,
277        })
278    }
279
280    pub fn and<U, V>(conditions: U) -> Self
281    where
282        U: IntoIterator<Item = V>,
283        V: Into<Filter<T>>,
284    {
285        Filter::FilterOperator(FilterOperator {
286            operator: Operator::And,
287            conditions: conditions.into_iter().map(|t| t.into()).collect(),
288        })
289    }
290
291    pub fn or<U, V>(conditions: U) -> Self
292    where
293        U: IntoIterator<Item = V>,
294        V: Into<Filter<T>>,
295    {
296        Filter::FilterOperator(FilterOperator {
297            operator: Operator::Or,
298            conditions: conditions.into_iter().map(|t| t.into()).collect(),
299        })
300    }
301
302    pub fn not<U, V>(conditions: U) -> Self
303    where
304        U: IntoIterator<Item = V>,
305        V: Into<Filter<T>>,
306    {
307        Filter::FilterOperator(FilterOperator {
308            operator: Operator::Not,
309            conditions: conditions.into_iter().map(|t| t.into()).collect(),
310        })
311    }
312}