io_jmap/rfc8621/email_submission/
cancel.rs1use core::fmt;
58
59use alloc::{collections::BTreeMap, string::String, vec, vec::Vec};
60
61use log::trace;
62use secrecy::SecretString;
63use serde::{Deserialize, Serialize};
64use thiserror::Error;
65
66use crate::{
67 coroutine::*,
68 jmap_try,
69 rfc8620::{CORE_CAPABILITY, JmapBatch, JmapMethodError, JmapSession, send::*},
70 rfc8621::{
71 MAIL_CAPABILITY,
72 email_submission::{
73 JmapEmailSubmission, JmapEmailSubmissionSetItemError, JmapEmailSubmissionUpdate,
74 JmapUndoStatus, SUBMISSION_CAPABILITY,
75 },
76 },
77};
78
79#[derive(Debug, Error)]
81pub enum JmapEmailSubmissionCancelError {
82 #[error("JMAP EmailSubmission/set (cancel) failed: missing response in method_responses")]
83 MissingResponse,
84 #[error("JMAP EmailSubmission/set (cancel) failed: {0}")]
85 Send(#[from] JmapSendError),
86 #[error("JMAP EmailSubmission/set (cancel) failed: serialize args: {0}")]
87 SerializeArgs(#[source] serde_json::Error),
88 #[error("JMAP EmailSubmission/set (cancel) failed: parse response: {0}")]
89 ParseResponse(#[source] serde_json::Error),
90 #[error("JMAP EmailSubmission/set (cancel) failed: {0}")]
91 Method(#[from] JmapMethodError),
92}
93
94#[derive(Clone, Debug)]
96pub struct JmapEmailSubmissionCancelOutput {
97 pub new_state: String,
98 pub updated: BTreeMap<String, Option<JmapEmailSubmission>>,
99 pub not_updated: BTreeMap<String, JmapEmailSubmissionSetItemError>,
100 pub keep_alive: bool,
101}
102
103pub struct JmapEmailSubmissionCancel {
105 state: State,
106}
107
108impl JmapEmailSubmissionCancel {
109 pub fn new(
111 session: &JmapSession,
112 http_auth: &SecretString,
113 ids: Vec<String>,
114 ) -> Result<Self, JmapEmailSubmissionCancelError> {
115 let account_id = session
116 .primary_accounts
117 .get(MAIL_CAPABILITY)
118 .cloned()
119 .unwrap_or_default();
120 let api_url = &session.api_url;
121
122 let update = ids
123 .into_iter()
124 .map(|id| {
125 (
126 id,
127 JmapEmailSubmissionUpdate {
128 undo_status: Some(JmapUndoStatus::Canceled),
129 },
130 )
131 })
132 .collect();
133
134 let args = serde_json::to_value(CancelEmailSubmissionsArgs { account_id, update })
135 .map_err(JmapEmailSubmissionCancelError::SerializeArgs)?;
136
137 let mut batch = JmapBatch::new();
138 batch.add("EmailSubmission/set", args);
139 let request = batch.into_request(vec![
140 CORE_CAPABILITY.into(),
141 MAIL_CAPABILITY.into(),
142 SUBMISSION_CAPABILITY.into(),
143 ]);
144
145 Ok(Self {
146 state: State::Send(JmapSend::new(http_auth, api_url, request)?),
147 })
148 }
149}
150
151impl JmapCoroutine for JmapEmailSubmissionCancel {
152 type Yield = JmapYield;
153 type Return = Result<JmapEmailSubmissionCancelOutput, JmapEmailSubmissionCancelError>;
154
155 fn resume(&mut self, arg: Option<&[u8]>) -> JmapCoroutineState<Self::Yield, Self::Return> {
156 trace!("JmapEmailSubmission/cancel: {}", self.state);
157 match &mut self.state {
158 State::Send(send) => {
159 let JmapSendOutput {
160 response,
161 keep_alive,
162 } = jmap_try!(send, arg);
163
164 let Some((name, args, _)) = response.method_responses.into_iter().next() else {
165 return JmapCoroutineState::Complete(Err(
166 JmapEmailSubmissionCancelError::MissingResponse,
167 ));
168 };
169
170 if name == "error" {
171 let err = serde_json::from_value::<JmapMethodError>(args)
172 .unwrap_or(JmapMethodError::Unknown);
173 return JmapCoroutineState::Complete(Err(err.into()));
174 }
175
176 match serde_json::from_value::<EmailSubmissionCancelResponse>(args) {
177 Ok(r) => JmapCoroutineState::Complete(Ok(JmapEmailSubmissionCancelOutput {
178 new_state: r.new_state,
179 updated: r.updated.unwrap_or_default(),
180 not_updated: r.not_updated.unwrap_or_default(),
181 keep_alive,
182 })),
183 Err(err) => JmapCoroutineState::Complete(Err(
184 JmapEmailSubmissionCancelError::ParseResponse(err),
185 )),
186 }
187 }
188 }
189 }
190}
191
192enum State {
193 Send(JmapSend),
194}
195
196impl fmt::Display for State {
197 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
198 match self {
199 Self::Send(_) => f.write_str("send"),
200 }
201 }
202}
203
204#[derive(Serialize)]
205#[serde(rename_all = "camelCase")]
206struct CancelEmailSubmissionsArgs {
207 account_id: String,
208 update: BTreeMap<String, JmapEmailSubmissionUpdate>,
209}
210
211#[derive(Deserialize)]
212#[serde(rename_all = "camelCase")]
213struct EmailSubmissionCancelResponse {
214 new_state: String,
215 #[serde(default)]
216 updated: Option<BTreeMap<String, Option<JmapEmailSubmission>>>,
217 #[serde(default)]
218 not_updated: Option<BTreeMap<String, JmapEmailSubmissionSetItemError>>,
219}