Skip to main content

jmap_client/core/
set.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 crate::Error;
13use ahash::AHashMap;
14use chrono::{DateTime, Utc};
15use serde::{Deserialize, Serialize};
16use std::fmt::{self, Display, Formatter};
17
18use super::{request::ResultReference, Object, RequestParams};
19
20pub trait SetObject: Object {
21    type SetArguments: Default;
22
23    fn new(create_id: Option<usize>) -> Self;
24    fn create_id(&self) -> Option<String>;
25    fn set_id(&mut self, _id: Option<String>) {}
26}
27
28#[derive(Debug, Clone, Serialize)]
29pub struct SetRequest<O: SetObject> {
30    #[serde(rename = "accountId")]
31    #[serde(skip_serializing_if = "Option::is_none")]
32    account_id: Option<String>,
33
34    #[serde(rename = "ifInState")]
35    #[serde(skip_serializing_if = "Option::is_none")]
36    if_in_state: Option<String>,
37
38    #[serde(skip_serializing_if = "Option::is_none")]
39    create: Option<AHashMap<String, O>>,
40
41    #[serde(skip_serializing_if = "Option::is_none")]
42    update: Option<AHashMap<String, O>>,
43
44    #[serde(skip_serializing_if = "Option::is_none")]
45    destroy: Option<Vec<String>>,
46
47    #[serde(rename = "#destroy")]
48    #[serde(skip_deserializing)]
49    #[serde(skip_serializing_if = "Option::is_none")]
50    destroy_ref: Option<ResultReference>,
51
52    #[serde(flatten)]
53    arguments: O::SetArguments,
54}
55
56#[derive(Debug, Clone, Deserialize)]
57pub struct SetResponse<O: SetObject> {
58    #[serde(rename = "accountId")]
59    account_id: Option<String>,
60
61    #[serde(rename = "oldState")]
62    old_state: Option<String>,
63
64    #[serde(rename = "newState")]
65    new_state: Option<String>,
66
67    #[serde(rename = "created")]
68    created: Option<AHashMap<String, O>>,
69
70    #[serde(rename = "updated")]
71    updated: Option<AHashMap<String, Option<O>>>,
72
73    #[serde(rename = "destroyed")]
74    destroyed: Option<Vec<String>>,
75
76    #[serde(rename = "notCreated")]
77    not_created: Option<AHashMap<String, SetError<O::Property>>>,
78
79    #[serde(rename = "notUpdated")]
80    not_updated: Option<AHashMap<String, SetError<O::Property>>>,
81
82    #[serde(rename = "notDestroyed")]
83    not_destroyed: Option<AHashMap<String, SetError<O::Property>>>,
84}
85
86#[derive(Debug, Clone, Deserialize)]
87pub struct SetError<U>
88where
89    U: Display,
90{
91    #[serde(rename = "type")]
92    pub type_: SetErrorType,
93    description: Option<String>,
94    properties: Option<Vec<U>>,
95}
96
97#[derive(Debug, Clone, Deserialize, Eq, PartialEq)]
98pub enum SetErrorType {
99    #[serde(rename = "forbidden")]
100    Forbidden,
101    #[serde(rename = "overQuota")]
102    OverQuota,
103    #[serde(rename = "tooLarge")]
104    TooLarge,
105    #[serde(rename = "rateLimit")]
106    RateLimit,
107    #[serde(rename = "notFound")]
108    NotFound,
109    #[serde(rename = "invalidPatch")]
110    InvalidPatch,
111    #[serde(rename = "willDestroy")]
112    WillDestroy,
113    #[serde(rename = "invalidProperties")]
114    InvalidProperties,
115    #[serde(rename = "singleton")]
116    Singleton,
117    #[serde(rename = "mailboxHasChild")]
118    MailboxHasChild,
119    #[serde(rename = "mailboxHasEmail")]
120    MailboxHasEmail,
121    #[serde(rename = "blobNotFound")]
122    BlobNotFound,
123    #[serde(rename = "tooManyKeywords")]
124    TooManyKeywords,
125    #[serde(rename = "tooManyMailboxes")]
126    TooManyMailboxes,
127    #[serde(rename = "forbiddenFrom")]
128    ForbiddenFrom,
129    #[serde(rename = "invalidEmail")]
130    InvalidEmail,
131    #[serde(rename = "tooManyRecipients")]
132    TooManyRecipients,
133    #[serde(rename = "noRecipients")]
134    NoRecipients,
135    #[serde(rename = "invalidRecipients")]
136    InvalidRecipients,
137    #[serde(rename = "forbiddenMailFrom")]
138    ForbiddenMailFrom,
139    #[serde(rename = "forbiddenToSend")]
140    ForbiddenToSend,
141    #[serde(rename = "cannotUnsend")]
142    CannotUnsend,
143    #[serde(rename = "alreadyExists")]
144    AlreadyExists,
145    #[serde(rename = "invalidScript")]
146    InvalidScript,
147    #[serde(rename = "scriptIsActive")]
148    ScriptIsActive,
149}
150
151impl<O: SetObject> SetRequest<O> {
152    pub fn new(params: RequestParams) -> Self {
153        Self {
154            account_id: if O::requires_account_id() {
155                params.account_id.into()
156            } else {
157                None
158            },
159            if_in_state: None,
160            create: None,
161            update: None,
162            destroy: None,
163            destroy_ref: None,
164            arguments: Default::default(),
165        }
166    }
167
168    pub fn account_id(&mut self, account_id: impl Into<String>) -> &mut Self {
169        if O::requires_account_id() {
170            self.account_id = Some(account_id.into());
171        }
172        self
173    }
174
175    pub fn if_in_state(&mut self, if_in_state: impl Into<String>) -> &mut Self {
176        self.if_in_state = Some(if_in_state.into());
177        self
178    }
179
180    pub fn create(&mut self) -> &mut O {
181        let create_id = self.create.as_ref().map_or(0, |c| c.len());
182        let create_id_str = format!("c{}", create_id);
183        self.create
184            .get_or_insert_with(AHashMap::new)
185            .insert(create_id_str.clone(), O::new(create_id.into()));
186        self.create
187            .as_mut()
188            .unwrap()
189            .get_mut(&create_id_str)
190            .unwrap()
191    }
192
193    pub fn create_with_id(&mut self, create_id: impl Into<String>) -> &mut O {
194        let create_id = create_id.into();
195        self.create
196            .get_or_insert_with(AHashMap::new)
197            .insert(create_id.clone(), O::new(0.into()));
198        self.create.as_mut().unwrap().get_mut(&create_id).unwrap()
199    }
200
201    pub fn create_item(&mut self, item: O) -> String {
202        let create_id = self.create.as_ref().map_or(0, |c| c.len());
203        let create_id_str = format!("c{}", create_id);
204        self.create
205            .get_or_insert_with(AHashMap::new)
206            .insert(create_id_str.clone(), item);
207        create_id_str
208    }
209
210    pub fn update(&mut self, id: impl Into<String>) -> &mut O {
211        let id: String = id.into();
212        self.update
213            .get_or_insert_with(AHashMap::new)
214            .insert(id.clone(), O::new(None));
215        self.update.as_mut().unwrap().get_mut(&id).unwrap()
216    }
217
218    pub fn update_item(&mut self, id: impl Into<String>, item: O) {
219        self.update
220            .get_or_insert_with(AHashMap::new)
221            .insert(id.into(), item);
222    }
223
224    pub fn destroy<U, V>(&mut self, ids: U) -> &mut Self
225    where
226        U: IntoIterator<Item = V>,
227        V: Into<String>,
228    {
229        self.destroy
230            .get_or_insert_with(Vec::new)
231            .extend(ids.into_iter().map(|id| id.into()));
232        self.destroy_ref = None;
233        self
234    }
235
236    pub fn destroy_ref(&mut self, reference: ResultReference) -> &mut Self {
237        self.destroy_ref = reference.into();
238        self.destroy = None;
239        self
240    }
241
242    pub fn arguments(&mut self) -> &mut O::SetArguments {
243        &mut self.arguments
244    }
245}
246
247impl<O: SetObject> SetResponse<O> {
248    pub fn account_id(&self) -> Option<&str> {
249        self.account_id.as_deref()
250    }
251
252    pub fn old_state(&self) -> Option<&str> {
253        self.old_state.as_deref()
254    }
255
256    pub fn new_state(&self) -> &str {
257        self.new_state.as_deref().unwrap_or("")
258    }
259
260    pub fn take_new_state(&mut self) -> String {
261        self.new_state.take().unwrap_or_default()
262    }
263
264    pub fn created(&mut self, id: &str) -> crate::Result<O> {
265        if let Some(result) = self.created.as_mut().and_then(|r| r.remove(id)) {
266            Ok(result)
267        } else if let Some(error) = self.not_created.as_mut().and_then(|r| r.remove(id)) {
268            Err(error.to_string_error().into())
269        } else {
270            Err(Error::Internal(format!("Id {} not found.", id)))
271        }
272    }
273
274    pub fn updated(&mut self, id: &str) -> crate::Result<Option<O>> {
275        if let Some(result) = self.updated.as_mut().and_then(|r| r.remove(id)) {
276            Ok(result)
277        } else if let Some(error) = self.not_updated.as_mut().and_then(|r| r.remove(id)) {
278            Err(error.to_string_error().into())
279        } else {
280            Err(Error::Internal(format!("Id {} not found.", id)))
281        }
282    }
283
284    pub fn destroyed(&mut self, id: &str) -> crate::Result<()> {
285        if self
286            .destroyed
287            .as_ref()
288            .is_some_and(|r| r.iter().any(|i| i == id))
289        {
290            Ok(())
291        } else if let Some(error) = self.not_destroyed.as_mut().and_then(|r| r.remove(id)) {
292            Err(error.to_string_error().into())
293        } else {
294            Err(Error::Internal(format!("Id {} not found.", id)))
295        }
296    }
297
298    pub fn created_ids(&self) -> Option<impl Iterator<Item = &String>> {
299        self.created.as_ref().map(|map| map.keys())
300    }
301
302    pub fn updated_ids(&self) -> Option<impl Iterator<Item = &String>> {
303        self.updated.as_ref().map(|map| map.keys())
304    }
305
306    pub fn take_updated_ids(&mut self) -> Option<Vec<String>> {
307        self.updated
308            .take()
309            .map(|map| map.into_iter().map(|(k, _)| k).collect())
310    }
311
312    pub fn destroyed_ids(&self) -> Option<impl Iterator<Item = &String>> {
313        self.destroyed.as_ref().map(|list| list.iter())
314    }
315
316    pub fn take_destroyed_ids(&mut self) -> Option<Vec<String>> {
317        self.destroyed.take()
318    }
319
320    pub fn not_created_ids(&self) -> Option<impl Iterator<Item = &String>> {
321        self.not_created.as_ref().map(|map| map.keys())
322    }
323
324    pub fn not_updated_ids(&self) -> Option<impl Iterator<Item = &String>> {
325        self.not_updated.as_ref().map(|map| map.keys())
326    }
327
328    pub fn not_destroyed_ids(&self) -> Option<impl Iterator<Item = &String>> {
329        self.not_destroyed.as_ref().map(|map| map.keys())
330    }
331
332    pub fn has_updated(&self) -> bool {
333        self.updated.as_ref().is_some_and(|m| !m.is_empty())
334    }
335
336    pub fn has_created(&self) -> bool {
337        self.created.as_ref().is_some_and(|m| !m.is_empty())
338    }
339
340    pub fn has_destroyed(&self) -> bool {
341        self.destroyed.as_ref().is_some_and(|m| !m.is_empty())
342    }
343
344    pub fn unwrap_update_errors(&self) -> crate::Result<()> {
345        if let Some(errors) = &self.not_updated {
346            if let Some(err) = errors.values().next() {
347                return Err(err.to_string_error().into());
348            }
349        }
350        Ok(())
351    }
352
353    pub fn unwrap_create_errors(&self) -> crate::Result<()> {
354        if let Some(errors) = &self.not_created {
355            if let Some(err) = errors.values().next() {
356                return Err(err.to_string_error().into());
357            }
358        }
359        Ok(())
360    }
361}
362
363impl<U: Display> SetError<U> {
364    pub fn error(&self) -> &SetErrorType {
365        &self.type_
366    }
367
368    pub fn description(&self) -> Option<&str> {
369        self.description.as_deref()
370    }
371
372    pub fn properties(&self) -> Option<&[U]> {
373        self.properties.as_deref()
374    }
375
376    pub fn to_string_error(&self) -> SetError<String> {
377        SetError {
378            type_: self.type_.clone(),
379            description: self.description.as_ref().map(|s| s.to_string()),
380            properties: self
381                .properties
382                .as_ref()
383                .map(|s| s.iter().map(|s| s.to_string()).collect()),
384        }
385    }
386}
387
388impl<U: Display> Display for SetError<U> {
389    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
390        self.type_.fmt(f)?;
391        if let Some(description) = &self.description {
392            write!(f, ": {}", description)?;
393        }
394        if let Some(properties) = &self.properties {
395            write!(
396                f,
397                " (properties: {})",
398                properties
399                    .iter()
400                    .map(|v| v.to_string())
401                    .collect::<Vec<String>>()
402                    .join(", ")
403            )?;
404        }
405        Ok(())
406    }
407}
408
409impl Display for SetErrorType {
410    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
411        match self {
412            SetErrorType::Forbidden => write!(f, "forbidden"),
413            SetErrorType::OverQuota => write!(f, "overQuota"),
414            SetErrorType::TooLarge => write!(f, "tooLarge"),
415            SetErrorType::RateLimit => write!(f, "rateLimit"),
416            SetErrorType::NotFound => write!(f, "notFound"),
417            SetErrorType::InvalidPatch => write!(f, "invalidPatch"),
418            SetErrorType::WillDestroy => write!(f, "willDestroy"),
419            SetErrorType::InvalidProperties => write!(f, "invalidProperties"),
420            SetErrorType::Singleton => write!(f, "singleton"),
421            SetErrorType::MailboxHasChild => write!(f, "mailboxHasChild"),
422            SetErrorType::MailboxHasEmail => write!(f, "mailboxHasEmail"),
423            SetErrorType::BlobNotFound => write!(f, "blobNotFound"),
424            SetErrorType::TooManyKeywords => write!(f, "tooManyKeywords"),
425            SetErrorType::TooManyMailboxes => write!(f, "tooManyMailboxes"),
426            SetErrorType::ForbiddenFrom => write!(f, "forbiddenFrom"),
427            SetErrorType::InvalidEmail => write!(f, "invalidEmail"),
428            SetErrorType::TooManyRecipients => write!(f, "tooManyRecipients"),
429            SetErrorType::NoRecipients => write!(f, "noRecipients"),
430            SetErrorType::InvalidRecipients => write!(f, "invalidRecipients"),
431            SetErrorType::ForbiddenMailFrom => write!(f, "forbiddenMailFrom"),
432            SetErrorType::ForbiddenToSend => write!(f, "forbiddenToSend"),
433            SetErrorType::CannotUnsend => write!(f, "cannotUnsend"),
434            SetErrorType::AlreadyExists => write!(f, "alreadyExists"),
435            SetErrorType::InvalidScript => write!(f, "invalidScript"),
436            SetErrorType::ScriptIsActive => write!(f, "scriptIsActive"),
437        }
438    }
439}
440
441pub fn from_timestamp(timestamp: i64) -> DateTime<Utc> {
442    DateTime::from_timestamp(timestamp, 0).unwrap_or_default()
443}
444
445pub fn string_not_set(string: &Option<String>) -> bool {
446    matches!(string, Some(string) if string.is_empty())
447}
448
449pub fn date_not_set(date: &Option<DateTime<Utc>>) -> bool {
450    matches!(date, Some(date) if date.timestamp() == 0)
451}
452
453pub fn list_not_set<O>(list: &Option<Vec<O>>) -> bool {
454    matches!(list, Some(list) if list.is_empty() )
455}
456
457pub fn map_not_set<K, V>(list: &Option<AHashMap<K, V>>) -> bool {
458    matches!(list, Some(list) if list.is_empty() )
459}