jmap_client/email/
import.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::{
13    core::{
14        request::ResultReference,
15        set::{from_timestamp, SetError},
16        RequestParams,
17    },
18    Error,
19};
20use ahash::AHashMap;
21use chrono::{DateTime, Utc};
22use serde::{Deserialize, Serialize};
23
24use super::{Email, Property};
25
26#[derive(Debug, Clone, Serialize)]
27pub struct EmailImportRequest {
28    #[serde(rename = "accountId")]
29    account_id: String,
30
31    #[serde(rename = "ifInState")]
32    #[serde(skip_serializing_if = "Option::is_none")]
33    if_in_state: Option<String>,
34
35    emails: AHashMap<String, EmailImport>,
36}
37
38#[derive(Debug, Clone, Serialize)]
39pub struct EmailImport {
40    #[serde(skip)]
41    create_id: usize,
42
43    #[serde(rename = "blobId")]
44    blob_id: String,
45
46    #[serde(rename = "mailboxIds")]
47    #[serde(skip_serializing_if = "Option::is_none")]
48    mailbox_ids: Option<AHashMap<String, bool>>,
49
50    #[serde(rename = "#mailboxIds")]
51    #[serde(skip_deserializing)]
52    #[serde(skip_serializing_if = "Option::is_none")]
53    mailbox_ids_ref: Option<ResultReference>,
54
55    #[serde(rename = "keywords")]
56    keywords: AHashMap<String, bool>,
57
58    #[serde(rename = "receivedAt")]
59    #[serde(skip_serializing_if = "Option::is_none")]
60    received_at: Option<DateTime<Utc>>,
61}
62
63#[derive(Debug, Clone, Deserialize)]
64pub struct EmailImportResponse {
65    #[serde(rename = "accountId")]
66    account_id: String,
67
68    #[serde(rename = "oldState")]
69    old_state: Option<String>,
70
71    #[serde(rename = "newState")]
72    new_state: String,
73
74    #[serde(rename = "created")]
75    created: Option<AHashMap<String, Email>>,
76
77    #[serde(rename = "notCreated")]
78    not_created: Option<AHashMap<String, SetError<Property>>>,
79}
80
81impl EmailImportRequest {
82    pub fn new(params: RequestParams) -> Self {
83        EmailImportRequest {
84            account_id: params.account_id,
85            if_in_state: None,
86            emails: AHashMap::new(),
87        }
88    }
89
90    pub fn account_id(&mut self, account_id: impl Into<String>) -> &mut Self {
91        self.account_id = account_id.into();
92        self
93    }
94
95    pub fn if_in_state(&mut self, if_in_state: impl Into<String>) -> &mut Self {
96        self.if_in_state = Some(if_in_state.into());
97        self
98    }
99
100    pub fn email(&mut self, blob_id: impl Into<String>) -> &mut EmailImport {
101        let create_id = self.emails.len();
102        let create_id_str = format!("i{}", create_id);
103        self.emails.insert(
104            create_id_str.clone(),
105            EmailImport::new(blob_id.into(), create_id),
106        );
107        self.emails.get_mut(&create_id_str).unwrap()
108    }
109}
110
111impl EmailImport {
112    fn new(blob_id: String, create_id: usize) -> Self {
113        EmailImport {
114            create_id,
115            blob_id,
116            mailbox_ids: None,
117            mailbox_ids_ref: None,
118            keywords: AHashMap::new(),
119            received_at: None,
120        }
121    }
122
123    pub fn mailbox_ids<T, U>(&mut self, mailbox_ids: T) -> &mut Self
124    where
125        T: IntoIterator<Item = U>,
126        U: Into<String>,
127    {
128        self.mailbox_ids = Some(mailbox_ids.into_iter().map(|s| (s.into(), true)).collect());
129        self.mailbox_ids_ref = None;
130        self
131    }
132
133    pub fn mailbox_ids_ref(&mut self, reference: ResultReference) -> &mut Self {
134        self.mailbox_ids_ref = reference.into();
135        self.mailbox_ids = None;
136        self
137    }
138
139    pub fn keywords<T, U>(&mut self, keywords: T) -> &mut Self
140    where
141        T: IntoIterator<Item = U>,
142        U: Into<String>,
143    {
144        self.keywords = keywords.into_iter().map(|s| (s.into(), true)).collect();
145        self
146    }
147
148    pub fn received_at(&mut self, received_at: i64) -> &mut Self {
149        self.received_at = Some(from_timestamp(received_at));
150        self
151    }
152
153    pub fn create_id(&self) -> String {
154        format!("i{}", self.create_id)
155    }
156}
157
158impl EmailImportResponse {
159    pub fn account_id(&self) -> &str {
160        &self.account_id
161    }
162
163    pub fn old_state(&self) -> Option<&str> {
164        self.old_state.as_deref()
165    }
166
167    pub fn new_state(&self) -> &str {
168        &self.new_state
169    }
170
171    pub fn take_new_state(&mut self) -> String {
172        std::mem::take(&mut self.new_state)
173    }
174
175    pub fn created(&mut self, id: &str) -> crate::Result<Email> {
176        if let Some(result) = self.created.as_mut().and_then(|r| r.remove(id)) {
177            Ok(result)
178        } else if let Some(error) = self.not_created.as_mut().and_then(|r| r.remove(id)) {
179            Err(error.to_string_error().into())
180        } else {
181            Err(Error::Internal(format!("Id {} not found.", id)))
182        }
183    }
184
185    pub fn created_ids(&self) -> Option<impl Iterator<Item = &String>> {
186        self.created.as_ref().map(|map| map.keys())
187    }
188
189    pub fn not_created_ids(&self) -> Option<impl Iterator<Item = &String>> {
190        self.not_created.as_ref().map(|map| map.keys())
191    }
192}