Skip to main content

io_jmap/rfc8621/vacation_response/
get.rs

1//! JMAP `VacationResponse/get` coroutine (RFC 8621 ยง8.2): wraps the generic
2//! [`JmapGet`] for the singleton `JmapVacationResponse` object.
3//!
4//! # Example
5//!
6//! ```rust,no_run
7//! use std::{
8//!     io::{Read, Write},
9//!     net::TcpStream,
10//! };
11//!
12//! use io_jmap::{
13//!     coroutine::{JmapCoroutine, JmapCoroutineState, JmapYield},
14//!     rfc8620::session::JmapSession,
15//!     rfc8621::vacation_response::get::JmapVacationResponseGet,
16//! };
17//! use secrecy::SecretString;
18//!
19//! // Ready stream needed (TCP-connected, TLS-negociated)
20//! let mut stream = TcpStream::connect("api.example.com:443").unwrap();
21//! let mut buf = [0u8; 4096];
22//!
23//! let session: JmapSession = serde_json::from_str(r#"{
24//!     "username": "",
25//!     "accounts": {},
26//!     "primaryAccounts": {"urn:ietf:params:jmap:mail": "a1"},
27//!     "capabilities": {},
28//!     "apiUrl": "https://api.example.com/jmap/",
29//!     "downloadUrl": "",
30//!     "uploadUrl": "",
31//!     "eventSourceUrl": "",
32//!     "state": ""
33//! }"#).unwrap();
34//! let auth = SecretString::from("Bearer xyz");
35//! let mut coroutine = JmapVacationResponseGet::new(&session, &auth).unwrap();
36//! let mut arg = None;
37//!
38//! let out = loop {
39//!     match coroutine.resume(arg.take()) {
40//!         JmapCoroutineState::Yielded(JmapYield::WantsWrite(bytes)) => {
41//!             stream.write_all(&bytes).unwrap();
42//!         }
43//!         JmapCoroutineState::Yielded(JmapYield::WantsRead) => {
44//!             let n = stream.read(&mut buf).unwrap();
45//!             arg = Some(&buf[..n]);
46//!         }
47//!         JmapCoroutineState::Complete(Ok(out)) => break out,
48//!         JmapCoroutineState::Complete(Err(err)) => panic!("{err}"),
49//!     }
50//! };
51//!
52//! println!("vacation enabled: {:?}", out.vacation_response);
53//! ```
54
55use alloc::{
56    string::{String, ToString},
57    vec,
58    vec::Vec,
59};
60
61use secrecy::SecretString;
62use serde::Serialize;
63use thiserror::Error;
64
65use crate::{
66    coroutine::*,
67    jmap_try,
68    rfc8620::{JMAP_CORE_CAPABILITY, get::*, request::JmapBatch, send::*, session::JmapSession},
69    rfc8621::{
70        JMAP_MAIL_CAPABILITY,
71        vacation_response::{JMAP_VACATION_RESPONSE_CAPABILITY, JmapVacationResponse},
72    },
73};
74
75/// Failure causes during a JMAP `VacationResponse/get` flow.
76#[derive(Debug, Error)]
77pub enum JmapVacationResponseGetError {
78    /// The inner send coroutine failed.
79    #[error("JMAP VacationResponse/get failed: {0}")]
80    Send(#[from] JmapSendError),
81    /// The method arguments could not be serialized.
82    #[error("JMAP VacationResponse/get failed: serialize args: {0}")]
83    SerializeArgs(#[source] serde_json::Error),
84    /// The inner generic get coroutine failed.
85    #[error("JMAP VacationResponse/get failed: {0}")]
86    Get(#[from] JmapGetError),
87}
88
89/// Successful terminal output of [`JmapVacationResponseGet`].
90#[derive(Clone, Debug)]
91pub struct JmapVacationResponseGetOutput {
92    /// The vacation response singleton, when the server returned it.
93    pub vacation_response: Option<JmapVacationResponse>,
94    /// The new server state after the call.
95    pub new_state: String,
96    /// Whether the server indicated the connection can be reused.
97    pub keep_alive: bool,
98}
99
100/// I/O-free coroutine for the JMAP `VacationResponse/get` method.
101pub struct JmapVacationResponseGet {
102    state: State,
103}
104
105impl JmapVacationResponseGet {
106    /// Prepares the method call request and builds the coroutine.
107    pub fn new(
108        session: &JmapSession,
109        http_auth: &SecretString,
110    ) -> Result<Self, JmapVacationResponseGetError> {
111        let account_id = session
112            .primary_accounts
113            .get(JMAP_VACATION_RESPONSE_CAPABILITY)
114            .or_else(|| session.primary_accounts.get(JMAP_MAIL_CAPABILITY))
115            .cloned()
116            .unwrap_or_default();
117        let api_url = &session.api_url;
118
119        let args = serde_json::to_value(VacationResponseGetArgs {
120            account_id,
121            ids: vec!["singleton".to_string()],
122        })
123        .map_err(JmapVacationResponseGetError::SerializeArgs)?;
124
125        let mut using = vec![JMAP_CORE_CAPABILITY.into(), JMAP_MAIL_CAPABILITY.into()];
126        if session
127            .capabilities
128            .contains_key(JMAP_VACATION_RESPONSE_CAPABILITY)
129        {
130            using.push(JMAP_VACATION_RESPONSE_CAPABILITY.into());
131        }
132
133        let mut batch = JmapBatch::new();
134        batch.add("VacationResponse/get", args);
135        let request = batch.into_request(using);
136
137        let send = JmapSend::new(http_auth, api_url, request)?;
138        Ok(Self {
139            state: State::Get(JmapGet::from_send(send)),
140        })
141    }
142}
143
144impl JmapCoroutine for JmapVacationResponseGet {
145    type Yield = JmapYield;
146    type Return = Result<JmapVacationResponseGetOutput, JmapVacationResponseGetError>;
147
148    fn resume(&mut self, arg: Option<&[u8]>) -> JmapCoroutineState<Self::Yield, Self::Return> {
149        match &mut self.state {
150            State::Get(get) => {
151                let JmapGetOutput {
152                    list,
153                    state,
154                    keep_alive,
155                    ..
156                } = jmap_try!(get, arg);
157                JmapCoroutineState::Complete(Ok(JmapVacationResponseGetOutput {
158                    vacation_response: list.into_iter().next(),
159                    new_state: state,
160                    keep_alive,
161                }))
162            }
163        }
164    }
165}
166
167enum State {
168    Get(JmapGet<JmapVacationResponse>),
169}
170
171#[derive(Serialize)]
172#[serde(rename_all = "camelCase")]
173struct VacationResponseGetArgs {
174    account_id: String,
175    ids: Vec<String>,
176}