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