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