io_jmap/calendars/calendar_event/changes.rs
1//! JMAP `CalendarEvent/changes` coroutine
2//! (draft-ietf-jmap-calendars-27): wraps the generic [`JmapChanges`]
3//! with the JMAP-Calendars capability set.
4//!
5//! # Example
6//!
7//! ```rust,no_run
8//! use std::{
9//! io::{Read, Write},
10//! net::TcpStream,
11//! };
12//!
13//! use io_jmap::{
14//! calendars::calendar_event::changes::{
15//! JmapCalendarEventChanges, JmapCalendarEventChangesOptions,
16//! },
17//! coroutine::{JmapCoroutine, JmapCoroutineState, JmapYield},
18//! rfc8620::session::JmapSession,
19//! };
20//! use secrecy::SecretString;
21//!
22//! // Ready stream needed (TCP-connected, TLS-negociated)
23//! let mut stream = TcpStream::connect("api.example.com:443").unwrap();
24//! let mut buf = [0u8; 4096];
25//!
26//! let session: JmapSession = serde_json::from_str(r#"{
27//! "username": "",
28//! "accounts": {},
29//! "primaryAccounts": {"urn:ietf:params:jmap:calendars": "a1"},
30//! "capabilities": {},
31//! "apiUrl": "https://api.example.com/jmap/",
32//! "downloadUrl": "",
33//! "uploadUrl": "",
34//! "eventSourceUrl": "",
35//! "state": ""
36//! }"#).unwrap();
37//! let auth = SecretString::from("Bearer xyz");
38//! let mut coroutine = JmapCalendarEventChanges::new(
39//! &session,
40//! &auth,
41//! "s1",
42//! JmapCalendarEventChangesOptions::default(),
43//! )
44//! .unwrap();
45//! let mut arg = None;
46//!
47//! let out = loop {
48//! match coroutine.resume(arg.take()) {
49//! JmapCoroutineState::Yielded(JmapYield::WantsWrite(bytes)) => {
50//! stream.write_all(&bytes).unwrap();
51//! }
52//! JmapCoroutineState::Yielded(JmapYield::WantsRead) => {
53//! let n = stream.read(&mut buf).unwrap();
54//! arg = Some(&buf[..n]);
55//! }
56//! JmapCoroutineState::Complete(Ok(out)) => break out,
57//! JmapCoroutineState::Complete(Err(err)) => panic!("{err}"),
58//! }
59//! };
60//!
61//! println!("new state {}", out.new_state);
62//! ```
63
64use alloc::{string::String, vec};
65
66use secrecy::SecretString;
67use thiserror::Error;
68
69use crate::{
70 calendars::JMAP_CALENDARS_CAPABILITY,
71 coroutine::*,
72 jmap_try,
73 rfc8620::{JMAP_CORE_CAPABILITY, changes::*, session::JmapSession},
74};
75
76/// Failure causes during a JMAP `CalendarEvent/changes` flow.
77#[derive(Debug, Error)]
78pub enum JmapCalendarEventChangesError {
79 /// The inner generic changes coroutine failed.
80 #[error("JMAP CalendarEvent/changes failed: {0}")]
81 Changes(#[from] JmapChangesError),
82}
83
84/// Options for [`JmapCalendarEventChanges::new`].
85#[derive(Clone, Debug, Default)]
86pub struct JmapCalendarEventChangesOptions {
87 /// Server-side cap on the number of changes returned.
88 pub max_changes: Option<u64>,
89}
90
91/// I/O-free coroutine for the JMAP `CalendarEvent/changes` method.
92pub struct JmapCalendarEventChanges {
93 state: State,
94}
95
96impl JmapCalendarEventChanges {
97 /// Prepares the method call request and builds the coroutine.
98 pub fn new(
99 session: &JmapSession,
100 http_auth: &SecretString,
101 since_state: impl Into<String>,
102 opts: JmapCalendarEventChangesOptions,
103 ) -> Result<Self, JmapCalendarEventChangesError> {
104 let account_id = session
105 .primary_accounts
106 .get(JMAP_CALENDARS_CAPABILITY)
107 .cloned()
108 .unwrap_or_default();
109 let api_url = &session.api_url;
110
111 Ok(Self {
112 state: State::Changes(JmapChanges::new(
113 account_id,
114 http_auth,
115 api_url,
116 "CalendarEvent/changes",
117 vec![
118 JMAP_CORE_CAPABILITY.into(),
119 JMAP_CALENDARS_CAPABILITY.into(),
120 ],
121 since_state,
122 JmapChangesOptions {
123 max_changes: opts.max_changes,
124 },
125 )?),
126 })
127 }
128}
129
130impl JmapCoroutine for JmapCalendarEventChanges {
131 type Yield = JmapYield;
132 type Return = Result<JmapChangesOutput, JmapCalendarEventChangesError>;
133
134 fn resume(&mut self, arg: Option<&[u8]>) -> JmapCoroutineState<Self::Yield, Self::Return> {
135 match &mut self.state {
136 State::Changes(changes) => {
137 let out = jmap_try!(changes, arg);
138 JmapCoroutineState::Complete(Ok(out))
139 }
140 }
141 }
142}
143
144enum State {
145 Changes(JmapChanges),
146}