1#[cfg(any(
29 feature = "rustls-aws",
30 feature = "rustls-ring",
31 feature = "native-tls"
32))]
33use core::time::Duration;
34use core::{any::Any, fmt};
35
36#[cfg(any(
37 feature = "rustls-aws",
38 feature = "rustls-ring",
39 feature = "native-tls"
40))]
41use alloc::string::ToString;
42use alloc::{boxed::Box, collections::BTreeMap, string::String, vec, vec::Vec};
43
44use std::io::{self, Read, Write};
45
46#[cfg(any(
47 feature = "rustls-aws",
48 feature = "rustls-ring",
49 feature = "native-tls"
50))]
51use pimalaya_stream::{
52 stream::{Stream, TcpConnectOptions, TlsConnectOptions},
53 tls::Tls,
54};
55use secrecy::SecretString;
56use thiserror::Error;
57use url::Url;
58
59use crate::{
60 calendars::{
61 calendar::{changes::*, get::*},
62 calendar_event::{changes::*, get::*, query::*},
63 },
64 coroutine::*,
65 rfc8620::{
66 blob_download::*,
67 blob_upload::*,
68 changes::JmapChangesOutput,
69 coroutine::JmapRedirectYield,
70 push_subscription::{get::*, set::*},
71 request::{JmapRequest, JmapResponse},
72 send::*,
73 session::JmapSession,
74 session_get::*,
75 },
76 rfc8621::{
77 email::{changes::*, copy::*, get::*, import::*, parse::*, query::*, set::*},
78 email_submission::{cancel::*, get::*, query::*, set::*},
79 identity::{get::*, set::*},
80 mailbox::{changes::*, get::*, query::*, set::*},
81 thread::{changes::*, get::*},
82 vacation_response::{get::*, set::*, *},
83 },
84 rfc9610::{
85 address_book::{changes::*, get::*, set::*},
86 contact_card::{changes::*, copy::*, get::*, query::*, set::*},
87 },
88};
89
90#[derive(Debug, Error)]
92pub enum JmapClientStdError {
93 #[error(transparent)]
95 Send(#[from] JmapSendError),
96 #[error(transparent)]
98 SessionGet(#[from] JmapSessionGetError),
99 #[error(transparent)]
101 BlobUpload(#[from] JmapBlobUploadError),
102 #[error(transparent)]
104 BlobDownload(#[from] JmapBlobDownloadError),
105 #[error(transparent)]
107 PushSubscriptionGet(#[from] JmapPushSubscriptionGetError),
108 #[error(transparent)]
110 PushSubscriptionSet(#[from] JmapPushSubscriptionSetError),
111 #[error(transparent)]
113 MailboxGet(#[from] JmapMailboxGetError),
114 #[error(transparent)]
116 MailboxQuery(#[from] JmapMailboxQueryError),
117 #[error(transparent)]
119 MailboxSet(#[from] JmapMailboxSetError),
120 #[error(transparent)]
122 MailboxChanges(#[from] JmapMailboxChangesError),
123 #[error(transparent)]
125 EmailGet(#[from] JmapEmailGetError),
126 #[error(transparent)]
128 EmailQuery(#[from] JmapEmailQueryError),
129 #[error(transparent)]
131 EmailSet(#[from] JmapEmailSetError),
132 #[error(transparent)]
134 EmailChanges(#[from] JmapEmailChangesError),
135 #[error(transparent)]
137 EmailCopy(#[from] JmapEmailCopyError),
138 #[error(transparent)]
140 EmailImport(#[from] JmapEmailImportError),
141 #[error(transparent)]
143 EmailParse(#[from] JmapEmailParseError),
144 #[error(transparent)]
146 ThreadGet(#[from] JmapThreadGetError),
147 #[error(transparent)]
149 ThreadChanges(#[from] JmapThreadChangesError),
150 #[error(transparent)]
152 IdentityGet(#[from] JmapIdentityGetError),
153 #[error(transparent)]
155 IdentitySet(#[from] JmapIdentitySetError),
156 #[error(transparent)]
158 EmailSubmissionGet(#[from] JmapEmailSubmissionGetError),
159 #[error(transparent)]
161 EmailSubmissionQuery(#[from] JmapEmailSubmissionQueryError),
162 #[error(transparent)]
164 EmailSubmissionSet(#[from] JmapEmailSubmissionSetError),
165 #[error(transparent)]
167 EmailSubmissionCancel(#[from] JmapEmailSubmissionCancelError),
168 #[error(transparent)]
170 VacationResponseGet(#[from] JmapVacationResponseGetError),
171 #[error(transparent)]
173 VacationResponseSet(#[from] JmapVacationResponseSetError),
174 #[error(transparent)]
176 AddressBookGet(#[from] JmapAddressBookGetError),
177 #[error(transparent)]
179 AddressBookSet(#[from] JmapAddressBookSetError),
180 #[error(transparent)]
182 AddressBookChanges(#[from] JmapAddressBookChangesError),
183 #[error(transparent)]
185 ContactCardGet(#[from] JmapContactCardGetError),
186 #[error(transparent)]
188 ContactCardQuery(#[from] JmapContactCardQueryError),
189 #[error(transparent)]
191 ContactCardSet(#[from] JmapContactCardSetError),
192 #[error(transparent)]
194 ContactCardChanges(#[from] JmapContactCardChangesError),
195 #[error(transparent)]
197 ContactCardCopy(#[from] JmapContactCardCopyError),
198 #[error(transparent)]
200 CalendarGet(#[from] JmapCalendarGetError),
201 #[error(transparent)]
203 CalendarChanges(#[from] JmapCalendarChangesError),
204 #[error(transparent)]
206 CalendarEventGet(#[from] JmapCalendarEventGetError),
207 #[error(transparent)]
209 CalendarEventQuery(#[from] JmapCalendarEventQueryError),
210 #[error(transparent)]
212 CalendarEventChanges(#[from] JmapCalendarEventChangesError),
213 #[error(transparent)]
215 Io(#[from] io::Error),
216 #[cfg(any(
218 feature = "rustls-aws",
219 feature = "rustls-ring",
220 feature = "native-tls"
221 ))]
222 #[error(transparent)]
223 Tls(#[from] anyhow::Error),
224 #[cfg(any(
226 feature = "rustls-aws",
227 feature = "rustls-ring",
228 feature = "native-tls"
229 ))]
230 #[error("JMAP URL `{0}` has no host")]
231 UrlMissingHost(String),
232 #[cfg(any(
234 feature = "rustls-aws",
235 feature = "rustls-ring",
236 feature = "native-tls"
237 ))]
238 #[error(
239 "JMAP URL `{url}` has unsupported scheme `{scheme}` (expected `http`, `https`, `jmap` or `jmaps`)"
240 )]
241 UrlUnsupportedScheme {
242 url: String,
244 scheme: String,
246 },
247 #[error("JMAP server redirected to `{0}` during a non-redirectable operation")]
250 UnexpectedRedirect(Url),
251 #[error("JMAP client missing session; call `session_get` first")]
253 MissingSession,
254}
255
256const READ_BUFFER_SIZE: usize = 16 * 1024;
257
258pub struct JmapClientStd {
260 pub stream: Box<dyn JmapStream>,
262 pub http_auth: SecretString,
264 pub session: Option<JmapSession>,
266}
267
268impl JmapClientStd {
269 pub fn new<S: Read + Write + Send + 'static>(stream: S, http_auth: SecretString) -> Self {
273 Self {
274 stream: Box::new(stream),
275 http_auth,
276 session: None,
277 }
278 }
279
280 pub fn default_alpn() -> Vec<String> {
284 vec![String::from("http/1.1")]
285 }
286
287 pub fn run<C, T, E>(&mut self, mut coroutine: C) -> Result<T, JmapClientStdError>
295 where
296 C: JmapCoroutine<Yield = JmapYield, Return = Result<T, E>>,
297 JmapClientStdError: From<E>,
298 {
299 let mut buf = [0u8; READ_BUFFER_SIZE];
300 let mut arg: Option<&[u8]> = None;
301
302 loop {
303 match coroutine.resume(arg.take()) {
304 JmapCoroutineState::Complete(Ok(out)) => return Ok(out),
305 JmapCoroutineState::Complete(Err(err)) => return Err(err.into()),
306 JmapCoroutineState::Yielded(JmapYield::WantsRead) => {
307 let n = self.stream.read(&mut buf)?;
308 arg = Some(&buf[..n]);
309 }
310 JmapCoroutineState::Yielded(JmapYield::WantsWrite(bytes)) => {
311 self.stream.write_all(&bytes)?;
312 arg = None;
313 }
314 }
315 }
316 }
317
318 pub fn from_parts<S: Read + Write + Send + 'static>(
321 stream: S,
322 http_auth: SecretString,
323 session: JmapSession,
324 ) -> Self {
325 Self {
326 stream: Box::new(stream),
327 http_auth,
328 session: Some(session),
329 }
330 }
331
332 #[cfg(any(
336 feature = "rustls-aws",
337 feature = "rustls-ring",
338 feature = "native-tls"
339 ))]
340 pub fn connect(
341 url: &Url,
342 tls: &Tls,
343 http_auth: SecretString,
344 ) -> Result<Self, JmapClientStdError> {
345 let host = url
346 .host_str()
347 .ok_or_else(|| JmapClientStdError::UrlMissingHost(url.to_string()))?;
348
349 let stream = match url.scheme() {
350 "http" | "jmap" => {
351 let port = url.port().unwrap_or(80);
352 let opts = TcpConnectOptions::default();
353 Stream::connect_tcp(host, port, opts)?
354 }
355 "https" | "jmaps" => {
356 let port = url.port().unwrap_or(443);
357 let opts = TlsConnectOptions {
358 tls: tls.clone(),
359 ..Default::default()
360 };
361
362 Stream::connect_tls(host, port, opts)?
363 }
364 scheme => {
365 return Err(JmapClientStdError::UrlUnsupportedScheme {
366 url: url.to_string(),
367 scheme: scheme.to_string(),
368 });
369 }
370 };
371
372 stream.set_read_timeout(Some(Duration::from_secs(5)))?;
376
377 Ok(Self {
378 stream: Box::new(stream),
379 http_auth,
380 session: None,
381 })
382 }
383
384 pub fn set_stream<S: Read + Write + Send + 'static>(&mut self, stream: S) {
388 self.stream = Box::new(stream);
389 }
390
391 pub fn session(&self) -> Option<&JmapSession> {
393 self.session.as_ref()
394 }
395
396 pub fn http_auth(&self) -> &SecretString {
398 &self.http_auth
399 }
400
401 fn session_or_err(&self) -> Result<&JmapSession, JmapClientStdError> {
402 self.session
403 .as_ref()
404 .ok_or(JmapClientStdError::MissingSession)
405 }
406
407 pub fn session_get(&mut self, url: &Url) -> Result<&JmapSession, JmapClientStdError> {
413 let mut coroutine = JmapSessionGet::new(&self.http_auth, url);
414 let mut buf = [0u8; READ_BUFFER_SIZE];
415 let mut arg: Option<&[u8]> = None;
416
417 loop {
418 match coroutine.resume(arg.take()) {
419 JmapCoroutineState::Complete(Ok(JmapSessionGetOutput { session, .. })) => {
420 self.session = Some(session);
421 return Ok(self.session.as_ref().unwrap());
422 }
423 JmapCoroutineState::Complete(Err(err)) => return Err(err.into()),
424 JmapCoroutineState::Yielded(JmapRedirectYield::WantsRead) => {
425 let n = self.stream.read(&mut buf)?;
426 arg = Some(&buf[..n]);
427 }
428 JmapCoroutineState::Yielded(JmapRedirectYield::WantsWrite(bytes)) => {
429 self.stream.write_all(&bytes)?;
430 arg = None;
431 }
432 JmapCoroutineState::Yielded(JmapRedirectYield::WantsRedirect { url, .. }) => {
433 return Err(JmapClientStdError::UnexpectedRedirect(url));
434 }
435 }
436 }
437 }
438
439 pub fn send_raw(&mut self, request: JmapRequest) -> Result<JmapResponse, JmapClientStdError> {
443 let session = self.session_or_err()?;
444 let coroutine = JmapSend::new(&self.http_auth, &session.api_url, request)?;
445 let out = self.run(coroutine)?;
446 Ok(out.response)
447 }
448
449 pub fn blob_upload(
453 &mut self,
454 upload_url: &Url,
455 content_type: &str,
456 data: Vec<u8>,
457 ) -> Result<JmapBlobUploadOutput, JmapClientStdError> {
458 let mut coroutine = JmapBlobUpload::new(&self.http_auth, upload_url, content_type, data);
459 let mut buf = [0u8; READ_BUFFER_SIZE];
460 let mut arg: Option<&[u8]> = None;
461
462 loop {
463 match coroutine.resume(arg.take()) {
464 JmapCoroutineState::Complete(Ok(out)) => return Ok(out),
465 JmapCoroutineState::Complete(Err(err)) => return Err(err.into()),
466 JmapCoroutineState::Yielded(JmapRedirectYield::WantsRead) => {
467 let n = self.stream.read(&mut buf)?;
468 arg = Some(&buf[..n]);
469 }
470 JmapCoroutineState::Yielded(JmapRedirectYield::WantsWrite(bytes)) => {
471 self.stream.write_all(&bytes)?;
472 arg = None;
473 }
474 JmapCoroutineState::Yielded(JmapRedirectYield::WantsRedirect { url, .. }) => {
475 return Err(JmapClientStdError::UnexpectedRedirect(url));
476 }
477 }
478 }
479 }
480
481 pub fn blob_download(&mut self, download_url: &Url) -> Result<Vec<u8>, JmapClientStdError> {
485 let mut coroutine = JmapBlobDownload::new(&self.http_auth, download_url);
486 let mut buf = [0u8; READ_BUFFER_SIZE];
487 let mut arg: Option<&[u8]> = None;
488
489 loop {
490 match coroutine.resume(arg.take()) {
491 JmapCoroutineState::Complete(Ok(out)) => return Ok(out.data),
492 JmapCoroutineState::Complete(Err(err)) => return Err(err.into()),
493 JmapCoroutineState::Yielded(JmapRedirectYield::WantsRead) => {
494 let n = self.stream.read(&mut buf)?;
495 arg = Some(&buf[..n]);
496 }
497 JmapCoroutineState::Yielded(JmapRedirectYield::WantsWrite(bytes)) => {
498 self.stream.write_all(&bytes)?;
499 arg = None;
500 }
501 JmapCoroutineState::Yielded(JmapRedirectYield::WantsRedirect { url, .. }) => {
502 return Err(JmapClientStdError::UnexpectedRedirect(url));
503 }
504 }
505 }
506 }
507
508 pub fn push_subscription_get(
510 &mut self,
511 opts: JmapPushSubscriptionGetOptions,
512 ) -> Result<JmapPushSubscriptionGetOutput, JmapClientStdError> {
513 let coroutine =
514 JmapPushSubscriptionGet::new(self.session_or_err()?, &self.http_auth, opts)?;
515 self.run(coroutine)
516 }
517
518 pub fn push_subscription_set(
520 &mut self,
521 args: JmapPushSubscriptionSetArgs,
522 ) -> Result<JmapPushSubscriptionSetOutput, JmapClientStdError> {
523 let coroutine =
524 JmapPushSubscriptionSet::new(self.session_or_err()?, &self.http_auth, args)?;
525 self.run(coroutine)
526 }
527
528 pub fn mailbox_get(
530 &mut self,
531 opts: JmapMailboxGetOptions,
532 ) -> Result<JmapMailboxGetOutput, JmapClientStdError> {
533 let coroutine = JmapMailboxGet::new(self.session_or_err()?, &self.http_auth, opts)?;
534 self.run(coroutine)
535 }
536
537 pub fn mailbox_query(
540 &mut self,
541 opts: JmapMailboxQueryOptions,
542 ) -> Result<JmapMailboxQueryOutput, JmapClientStdError> {
543 let coroutine = JmapMailboxQuery::new(self.session_or_err()?, &self.http_auth, opts)?;
544 self.run(coroutine)
545 }
546
547 pub fn mailbox_set(
549 &mut self,
550 args: JmapMailboxSetArgs,
551 ) -> Result<JmapMailboxSetOutput, JmapClientStdError> {
552 let coroutine = JmapMailboxSet::new(self.session_or_err()?, &self.http_auth, args)?;
553 self.run(coroutine)
554 }
555
556 pub fn mailbox_changes(
558 &mut self,
559 since_state: impl Into<String>,
560 opts: JmapMailboxChangesOptions,
561 ) -> Result<JmapChangesOutput, JmapClientStdError> {
562 let coroutine =
563 JmapMailboxChanges::new(self.session_or_err()?, &self.http_auth, since_state, opts)?;
564 self.run(coroutine)
565 }
566
567 pub fn email_get(
569 &mut self,
570 ids: Vec<String>,
571 opts: JmapEmailGetOptions,
572 ) -> Result<JmapEmailGetOutput, JmapClientStdError> {
573 let coroutine = JmapEmailGet::new(self.session_or_err()?, &self.http_auth, ids, opts)?;
574 self.run(coroutine)
575 }
576
577 pub fn email_query(
579 &mut self,
580 opts: JmapEmailQueryOptions,
581 ) -> Result<JmapEmailQueryOutput, JmapClientStdError> {
582 let coroutine = JmapEmailQuery::new(self.session_or_err()?, &self.http_auth, opts)?;
583 self.run(coroutine)
584 }
585
586 pub fn email_set(
588 &mut self,
589 args: JmapEmailSetArgs,
590 ) -> Result<JmapEmailSetOutput, JmapClientStdError> {
591 let coroutine = JmapEmailSet::new(self.session_or_err()?, &self.http_auth, args)?;
592 self.run(coroutine)
593 }
594
595 pub fn email_changes(
597 &mut self,
598 since_state: impl Into<String>,
599 opts: JmapEmailChangesOptions,
600 ) -> Result<JmapChangesOutput, JmapClientStdError> {
601 let coroutine =
602 JmapEmailChanges::new(self.session_or_err()?, &self.http_auth, since_state, opts)?;
603 self.run(coroutine)
604 }
605
606 pub fn email_copy(
608 &mut self,
609 from_account_id: impl Into<String>,
610 emails: BTreeMap<String, JmapEmailCopyArgs>,
611 ) -> Result<JmapEmailCopyOutput, JmapClientStdError> {
612 let coroutine = JmapEmailCopy::new(
613 self.session_or_err()?,
614 &self.http_auth,
615 from_account_id,
616 emails,
617 )?;
618 self.run(coroutine)
619 }
620
621 pub fn email_import(
623 &mut self,
624 emails: BTreeMap<String, JmapEmailImportArgs>,
625 ) -> Result<JmapEmailImportOutput, JmapClientStdError> {
626 let coroutine = JmapEmailImport::new(self.session_or_err()?, &self.http_auth, emails)?;
627 self.run(coroutine)
628 }
629
630 pub fn email_parse(
632 &mut self,
633 blob_ids: Vec<String>,
634 opts: JmapEmailParseOptions,
635 ) -> Result<JmapEmailParseOutput, JmapClientStdError> {
636 let coroutine =
637 JmapEmailParse::new(self.session_or_err()?, &self.http_auth, blob_ids, opts)?;
638 self.run(coroutine)
639 }
640
641 pub fn thread_get(
643 &mut self,
644 ids: Vec<String>,
645 ) -> Result<JmapThreadGetOutput, JmapClientStdError> {
646 let coroutine = JmapThreadGet::new(self.session_or_err()?, &self.http_auth, ids)?;
647 self.run(coroutine)
648 }
649
650 pub fn thread_changes(
652 &mut self,
653 since_state: impl Into<String>,
654 opts: JmapThreadChangesOptions,
655 ) -> Result<JmapChangesOutput, JmapClientStdError> {
656 let coroutine =
657 JmapThreadChanges::new(self.session_or_err()?, &self.http_auth, since_state, opts)?;
658 self.run(coroutine)
659 }
660
661 pub fn identity_get(
663 &mut self,
664 opts: JmapIdentityGetOptions,
665 ) -> Result<JmapIdentityGetOutput, JmapClientStdError> {
666 let coroutine = JmapIdentityGet::new(self.session_or_err()?, &self.http_auth, opts)?;
667 self.run(coroutine)
668 }
669
670 pub fn identity_set(
672 &mut self,
673 args: JmapIdentitySetArgs,
674 ) -> Result<JmapIdentitySetOutput, JmapClientStdError> {
675 let coroutine = JmapIdentitySet::new(self.session_or_err()?, &self.http_auth, args)?;
676 self.run(coroutine)
677 }
678
679 pub fn email_submission_get(
681 &mut self,
682 opts: JmapEmailSubmissionGetOptions,
683 ) -> Result<JmapEmailSubmissionGetOutput, JmapClientStdError> {
684 let coroutine = JmapEmailSubmissionGet::new(self.session_or_err()?, &self.http_auth, opts)?;
685 self.run(coroutine)
686 }
687
688 pub fn email_submission_query(
691 &mut self,
692 opts: JmapEmailSubmissionQueryOptions,
693 ) -> Result<JmapEmailSubmissionQueryOutput, JmapClientStdError> {
694 let coroutine =
695 JmapEmailSubmissionQuery::new(self.session_or_err()?, &self.http_auth, opts)?;
696 self.run(coroutine)
697 }
698
699 pub fn email_submission_set(
701 &mut self,
702 submissions: BTreeMap<String, JmapEmailSubmissionCreate>,
703 ) -> Result<JmapEmailSubmissionSetOutput, JmapClientStdError> {
704 let coroutine =
705 JmapEmailSubmissionSet::new(self.session_or_err()?, &self.http_auth, submissions)?;
706 self.run(coroutine)
707 }
708
709 pub fn email_submission_cancel(
712 &mut self,
713 ids: Vec<String>,
714 ) -> Result<JmapEmailSubmissionCancelOutput, JmapClientStdError> {
715 let coroutine =
716 JmapEmailSubmissionCancel::new(self.session_or_err()?, &self.http_auth, ids)?;
717 self.run(coroutine)
718 }
719
720 pub fn vacation_response_get(
722 &mut self,
723 ) -> Result<Option<JmapVacationResponse>, JmapClientStdError> {
724 let coroutine = JmapVacationResponseGet::new(self.session_or_err()?, &self.http_auth)?;
725 Ok(self.run(coroutine)?.vacation_response)
726 }
727
728 pub fn vacation_response_set(
731 &mut self,
732 patch: JmapVacationResponseUpdate,
733 ) -> Result<Option<JmapVacationResponse>, JmapClientStdError> {
734 let coroutine =
735 JmapVacationResponseSet::new(self.session_or_err()?, &self.http_auth, patch)?;
736 Ok(self.run(coroutine)?.updated)
737 }
738
739 pub fn address_book_get(
741 &mut self,
742 opts: JmapAddressBookGetOptions,
743 ) -> Result<JmapAddressBookGetOutput, JmapClientStdError> {
744 let coroutine = JmapAddressBookGet::new(self.session_or_err()?, &self.http_auth, opts)?;
745 self.run(coroutine)
746 }
747
748 pub fn address_book_set(
750 &mut self,
751 args: JmapAddressBookSetArgs,
752 ) -> Result<JmapAddressBookSetOutput, JmapClientStdError> {
753 let coroutine = JmapAddressBookSet::new(self.session_or_err()?, &self.http_auth, args)?;
754 self.run(coroutine)
755 }
756
757 pub fn address_book_changes(
759 &mut self,
760 since_state: impl Into<String>,
761 opts: JmapAddressBookChangesOptions,
762 ) -> Result<JmapChangesOutput, JmapClientStdError> {
763 let coroutine = JmapAddressBookChanges::new(
764 self.session_or_err()?,
765 &self.http_auth,
766 since_state,
767 opts,
768 )?;
769 self.run(coroutine)
770 }
771
772 pub fn contact_card_get(
774 &mut self,
775 opts: JmapContactCardGetOptions,
776 ) -> Result<JmapContactCardGetOutput, JmapClientStdError> {
777 let coroutine = JmapContactCardGet::new(self.session_or_err()?, &self.http_auth, opts)?;
778 self.run(coroutine)
779 }
780
781 pub fn contact_card_query(
784 &mut self,
785 opts: JmapContactCardQueryOptions,
786 ) -> Result<JmapContactCardQueryOutput, JmapClientStdError> {
787 let coroutine = JmapContactCardQuery::new(self.session_or_err()?, &self.http_auth, opts)?;
788 self.run(coroutine)
789 }
790
791 pub fn contact_card_set(
793 &mut self,
794 args: JmapContactCardSetArgs,
795 ) -> Result<JmapContactCardSetOutput, JmapClientStdError> {
796 let coroutine = JmapContactCardSet::new(self.session_or_err()?, &self.http_auth, args)?;
797 self.run(coroutine)
798 }
799
800 pub fn contact_card_changes(
802 &mut self,
803 since_state: impl Into<String>,
804 opts: JmapContactCardChangesOptions,
805 ) -> Result<JmapChangesOutput, JmapClientStdError> {
806 let coroutine = JmapContactCardChanges::new(
807 self.session_or_err()?,
808 &self.http_auth,
809 since_state,
810 opts,
811 )?;
812 self.run(coroutine)
813 }
814
815 pub fn contact_card_copy(
817 &mut self,
818 from_account_id: impl Into<String>,
819 cards: BTreeMap<String, JmapContactCardCopyArgs>,
820 ) -> Result<JmapContactCardCopyOutput, JmapClientStdError> {
821 let coroutine = JmapContactCardCopy::new(
822 self.session_or_err()?,
823 &self.http_auth,
824 from_account_id,
825 cards,
826 )?;
827 self.run(coroutine)
828 }
829
830 pub fn calendar_get(
832 &mut self,
833 opts: JmapCalendarGetOptions,
834 ) -> Result<JmapCalendarGetOutput, JmapClientStdError> {
835 let coroutine = JmapCalendarGet::new(self.session_or_err()?, &self.http_auth, opts)?;
836 self.run(coroutine)
837 }
838
839 pub fn calendar_changes(
841 &mut self,
842 since_state: impl Into<String>,
843 opts: JmapCalendarChangesOptions,
844 ) -> Result<JmapChangesOutput, JmapClientStdError> {
845 let coroutine =
846 JmapCalendarChanges::new(self.session_or_err()?, &self.http_auth, since_state, opts)?;
847 self.run(coroutine)
848 }
849
850 pub fn calendar_event_get(
852 &mut self,
853 opts: JmapCalendarEventGetOptions,
854 ) -> Result<JmapCalendarEventGetOutput, JmapClientStdError> {
855 let coroutine = JmapCalendarEventGet::new(self.session_or_err()?, &self.http_auth, opts)?;
856 self.run(coroutine)
857 }
858
859 pub fn calendar_event_query(
862 &mut self,
863 opts: JmapCalendarEventQueryOptions,
864 ) -> Result<JmapCalendarEventQueryOutput, JmapClientStdError> {
865 let coroutine = JmapCalendarEventQuery::new(self.session_or_err()?, &self.http_auth, opts)?;
866 self.run(coroutine)
867 }
868
869 pub fn calendar_event_changes(
871 &mut self,
872 since_state: impl Into<String>,
873 opts: JmapCalendarEventChangesOptions,
874 ) -> Result<JmapChangesOutput, JmapClientStdError> {
875 let coroutine = JmapCalendarEventChanges::new(
876 self.session_or_err()?,
877 &self.http_auth,
878 since_state,
879 opts,
880 )?;
881 self.run(coroutine)
882 }
883}
884
885impl fmt::Debug for JmapClientStd {
886 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
887 f.debug_struct("JmapClientStd")
888 .field("http_auth", &self.http_auth)
889 .field("session", &self.session)
890 .finish_non_exhaustive()
891 }
892}
893
894pub trait JmapStream: Read + Write + Send + Any {
900 fn as_any_mut(&mut self) -> &mut dyn Any;
902}
903
904impl<T: Read + Write + Send + Any> JmapStream for T {
905 fn as_any_mut(&mut self) -> &mut dyn Any {
906 self
907 }
908}