jmap_mail_client/methods/
email.rs1use std::collections::HashMap;
12
13use jmap_types::{Id, PatchObject, State};
14
15use super::{
16 ChangesResponse, EmailCopyParams, EmailGetParams, EmailImportInput, EmailImportResponse,
17 EmailParseParams, EmailParseResponse, GetResponse, QueryChangesResponse, QueryResponse,
18 SetResponse,
19};
20
21impl super::SessionClient {
22 pub async fn email_get(
28 &self,
29 ids: Option<&[Id]>,
30 properties: Option<&[&str]>,
31 params: Option<EmailGetParams>,
32 ) -> Result<GetResponse<jmap_mail_types::Email>, jmap_base_client::ClientError> {
33 let (api_url, account_id) = self.session_parts()?;
34 let mut args = serde_json::json!({ "accountId": account_id });
41 if let Some(id_slice) = ids {
42 args["ids"] = serde_json::to_value(id_slice).expect("Id slice Serialize is infallible");
43 }
44 if let Some(props) = properties {
45 args["properties"] = serde_json::Value::Array(
46 props.iter().copied().map(serde_json::Value::from).collect(),
47 );
48 }
49 if let Some(p) = params {
50 let pv = serde_json::to_value(p).map_err(|e| {
51 jmap_base_client::ClientError::InvalidArgument(format!(
52 "email_get: failed to serialize params: {e}"
53 ))
54 })?;
55 if let serde_json::Value::Object(map) = pv {
56 for (k, v) in map {
57 args[k] = v;
58 }
59 }
60 }
61 let req = super::build_request("Email/get", args, super::USING_MAIL);
62 let resp = self.call_internal(api_url, &req).await?;
63 jmap_base_client::extract_response(&resp, super::CALL_ID)
64 }
65
66 pub async fn email_changes(
71 &self,
72 since_state: &State,
73 max_changes: Option<u64>,
74 ) -> Result<ChangesResponse, jmap_base_client::ClientError> {
75 if since_state.as_ref().is_empty() {
77 return Err(jmap_base_client::ClientError::InvalidArgument(
78 "email_changes: since_state may not be empty".into(),
79 ));
80 }
81 let (api_url, account_id) = self.session_parts()?;
82 let mut args = serde_json::json!({
83 "accountId": account_id,
84 "sinceState": since_state,
85 });
86 if let Some(mc) = max_changes {
87 args["maxChanges"] = mc.into();
88 }
89 let req = super::build_request("Email/changes", args, super::USING_MAIL);
90 let resp = self.call_internal(api_url, &req).await?;
91 jmap_base_client::extract_response(&resp, super::CALL_ID)
92 }
93
94 pub async fn email_set(
105 &self,
106 create: Option<serde_json::Value>,
107 update: Option<HashMap<Id, PatchObject>>,
108 destroy: Option<Vec<Id>>,
109 if_in_state: Option<&State>,
110 ) -> Result<SetResponse<jmap_mail_types::Email>, jmap_base_client::ClientError> {
111 let (api_url, account_id) = self.session_parts()?;
112 let mut args = serde_json::json!({
113 "accountId": account_id,
114 });
115 if let Some(s) = if_in_state {
116 args["ifInState"] = s.as_ref().into();
117 }
118 if let Some(c) = create {
119 args["create"] = c;
120 }
121 if let Some(u) = update {
122 args["update"] = serde_json::to_value(&u).map_err(|e| {
123 jmap_base_client::ClientError::InvalidArgument(format!(
124 "email_set: serializing update map failed: {e}"
125 ))
126 })?;
127 }
128 if let Some(d) = destroy {
129 args["destroy"] = serde_json::to_value(&d).expect("Id Vec Serialize is infallible");
130 }
131 let req = super::build_request("Email/set", args, super::USING_MAIL);
132 let resp = self.call_internal(api_url, &req).await?;
133 jmap_base_client::extract_response(&resp, super::CALL_ID)
134 }
135
136 pub async fn email_query(
142 &self,
143 filter: Option<serde_json::Value>,
144 sort: Option<serde_json::Value>,
145 position: Option<u64>,
146 limit: Option<u64>,
147 collapse_threads: Option<bool>,
148 ) -> Result<QueryResponse, jmap_base_client::ClientError> {
149 let (api_url, account_id) = self.session_parts()?;
150 let mut args = serde_json::json!({
151 "accountId": account_id,
152 });
153 if let Some(f) = filter {
154 args["filter"] = f;
155 }
156 if let Some(s) = sort {
157 args["sort"] = s;
158 }
159 if let Some(p) = position {
160 args["position"] = p.into();
161 }
162 if let Some(l) = limit {
163 args["limit"] = l.into();
164 }
165 if let Some(ct) = collapse_threads {
166 args["collapseThreads"] = ct.into();
167 }
168 let req = super::build_request("Email/query", args, super::USING_MAIL);
169 let resp = self.call_internal(api_url, &req).await?;
170 jmap_base_client::extract_response(&resp, super::CALL_ID)
171 }
172
173 pub async fn email_query_changes(
176 &self,
177 since_query_state: &State,
178 max_changes: Option<u64>,
179 collapse_threads: Option<bool>,
180 ) -> Result<QueryChangesResponse, jmap_base_client::ClientError> {
181 if since_query_state.as_ref().is_empty() {
183 return Err(jmap_base_client::ClientError::InvalidArgument(
184 "email_query_changes: since_query_state may not be empty".into(),
185 ));
186 }
187 let (api_url, account_id) = self.session_parts()?;
188 let mut args = serde_json::json!({
189 "accountId": account_id,
190 "sinceQueryState": since_query_state,
191 });
192 if let Some(mc) = max_changes {
193 args["maxChanges"] = mc.into();
194 }
195 if let Some(ct) = collapse_threads {
196 args["collapseThreads"] = ct.into();
197 }
198 let req = super::build_request("Email/queryChanges", args, super::USING_MAIL);
199 let resp = self.call_internal(api_url, &req).await?;
200 jmap_base_client::extract_response(&resp, super::CALL_ID)
201 }
202
203 pub async fn email_copy(
209 &self,
210 params: EmailCopyParams,
211 create: serde_json::Value,
212 ) -> Result<SetResponse<jmap_mail_types::Email>, jmap_base_client::ClientError> {
213 let (api_url, account_id) = self.session_parts()?;
214 let mut args = serde_json::json!({
215 "accountId": account_id,
216 "fromAccountId": params.from_account_id,
217 "create": create,
218 });
219 if let Some(v) = params.on_success_destroy_original {
220 args["onSuccessDestroyOriginal"] = v.into();
221 }
222 if let Some(v) = params.destroy_from_if_in_state {
223 args["destroyFromIfInState"] = v.as_ref().into();
224 }
225 let req = super::build_request("Email/copy", args, super::USING_MAIL);
226 let resp = self.call_internal(api_url, &req).await?;
227 jmap_base_client::extract_response(&resp, super::CALL_ID)
228 }
229
230 pub async fn email_import(
251 &self,
252 emails: &HashMap<String, EmailImportInput<'_>>,
253 if_in_state: Option<&State>,
254 ) -> Result<EmailImportResponse, jmap_base_client::ClientError> {
255 if emails.is_empty() {
256 return Err(jmap_base_client::ClientError::InvalidArgument(
257 "email_import: emails map may not be empty".into(),
258 ));
259 }
260 for (key, input) in emails {
261 if input.mailbox_ids.is_empty() {
262 return Err(jmap_base_client::ClientError::InvalidArgument(format!(
263 "email_import: mailboxIds for creation id '{key}' may not be empty (RFC 8621 §4.8)"
264 )));
265 }
266 }
267 let (api_url, account_id) = self.session_parts()?;
268 let emails_value = serde_json::to_value(emails).map_err(|e| {
269 jmap_base_client::ClientError::InvalidArgument(format!(
270 "email_import: serializing emails map failed: {e}"
271 ))
272 })?;
273 let mut args = serde_json::json!({
274 "accountId": account_id,
275 "emails": emails_value,
276 });
277 if let Some(s) = if_in_state {
278 args["ifInState"] = s.as_ref().into();
279 }
280 let req = super::build_request("Email/import", args, super::USING_MAIL);
281 let resp = self.call_internal(api_url, &req).await?;
282 jmap_base_client::extract_response(&resp, super::CALL_ID)
283 }
284
285 pub async fn email_parse(
300 &self,
301 blob_ids: &[Id],
302 params: Option<EmailParseParams>,
303 ) -> Result<EmailParseResponse, jmap_base_client::ClientError> {
304 if blob_ids.is_empty() {
305 return Err(jmap_base_client::ClientError::InvalidArgument(
306 "email_parse: blob_ids may not be empty".into(),
307 ));
308 }
309 let (api_url, account_id) = self.session_parts()?;
310 let mut args = serde_json::json!({
311 "accountId": account_id,
312 "blobIds": blob_ids,
313 });
314 if let Some(p) = params {
315 let pv = serde_json::to_value(&p).map_err(|e| {
316 jmap_base_client::ClientError::InvalidArgument(format!(
317 "email_parse: failed to serialize params: {e}"
318 ))
319 })?;
320 if let serde_json::Value::Object(map) = pv {
321 for (k, v) in map {
322 args[k] = v;
323 }
324 }
325 }
326 let req = super::build_request("Email/parse", args, super::USING_MAIL);
327 let resp = self.call_internal(api_url, &req).await?;
328 jmap_base_client::extract_response(&resp, super::CALL_ID)
329 }
330}
331
332#[cfg(test)]
337mod tests {
338 use serde_json::json;
339
340 #[test]
366 fn email_get_response_deserializes() {
367 let json = json!({
368 "accountId": "acc1",
369 "state": "s10",
370 "list": [
371 {
372 "id": "e1",
373 "blobId": "b1",
374 "threadId": "t1",
375 "mailboxIds": { "mb1": true },
376 "keywords": { "$seen": true },
377 "size": 1024,
378 "receivedAt": "2024-01-01T00:00:00Z"
379 }
380 ],
381 "notFound": []
382 });
383 use super::super::GetResponse;
384 let resp: GetResponse<jmap_mail_types::Email> =
385 serde_json::from_value(json).expect("must deserialize Email GetResponse");
386 assert_eq!(resp.list.len(), 1);
387 assert_eq!(resp.list[0].id.as_ref(), "e1");
388 }
389}