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