Skip to main content

io_msgraph/v1/
client.rs

1//! Std-blocking Microsoft Graph client: wraps a `Read + Write` stream
2//! plus the bearer credential and runs the coroutines against
3//! `graph.microsoft.com`. Gated behind the `client` feature.
4
5#[cfg(any(
6    feature = "rustls-aws",
7    feature = "rustls-ring",
8    feature = "native-tls"
9))]
10use core::time::Duration;
11use core::{any::Any, fmt};
12
13use alloc::{
14    boxed::Box,
15    string::{String, ToString},
16    vec::Vec,
17};
18use std::io::{self, Read, Write};
19
20use io_http::rfc6750::bearer::HttpAuthBearer;
21#[cfg(any(
22    feature = "rustls-aws",
23    feature = "rustls-ring",
24    feature = "native-tls"
25))]
26use pimalaya_stream::std::stream::StreamStd;
27/// TLS backend selection re-exported from pimalaya-stream, feeding
28/// [`MsgraphClientStdConnectOptions::tls`].
29#[cfg(any(
30    feature = "rustls-aws",
31    feature = "rustls-ring",
32    feature = "native-tls"
33))]
34pub use pimalaya_stream::tls::*;
35use thiserror::Error;
36#[cfg(any(
37    feature = "rustls-aws",
38    feature = "rustls-ring",
39    feature = "native-tls"
40))]
41use url::Url;
42
43#[cfg(any(
44    feature = "rustls-aws",
45    feature = "rustls-ring",
46    feature = "native-tls"
47))]
48use crate::v1::send::MSGRAPH_API_BASE;
49use crate::{
50    coroutine::*,
51    v1::rest::users::{
52        MsgraphUser,
53        contact_folders::{
54            MsgraphContactFolder,
55            child_folders::MsgraphContactChildFoldersList,
56            create::MsgraphContactFolderCreate,
57            delete::MsgraphContactFolderDelete,
58            get::MsgraphContactFolderGet,
59            list::{
60                MsgraphContactFoldersList, MsgraphContactFoldersListParams,
61                MsgraphContactFoldersListResponse,
62            },
63            update::MsgraphContactFolderUpdate,
64        },
65        contacts::{
66            MsgraphContact,
67            create::MsgraphContactCreate,
68            delete::MsgraphContactDelete,
69            delta::{MsgraphContactsDelta, MsgraphContactsDeltaResponse},
70            get::MsgraphContactGet,
71            list::{MsgraphContactsList, MsgraphContactsListParams, MsgraphContactsListResponse},
72            update::MsgraphContactUpdate,
73        },
74        get::MsgraphUserGet,
75        mail_folders::{
76            MsgraphMailFolder,
77            child_folders::MsgraphMailChildFoldersList,
78            copy::MsgraphMailFolderCopy,
79            create::MsgraphMailFolderCreate,
80            delete::MsgraphMailFolderDelete,
81            get::MsgraphMailFolderGet,
82            list::{
83                MsgraphMailFoldersList, MsgraphMailFoldersListParams,
84                MsgraphMailFoldersListResponse,
85            },
86            r#move::MsgraphMailFolderMove,
87            update::MsgraphMailFolderUpdate,
88        },
89        messages::{
90            MsgraphMessage,
91            attachments::{
92                MsgraphAttachment,
93                create::MsgraphAttachmentCreate,
94                delete::MsgraphAttachmentDelete,
95                get_raw::MsgraphAttachmentGetRaw,
96                list::{MsgraphAttachmentsList, MsgraphAttachmentsListResponse},
97            },
98            copy::MsgraphMessageCopy,
99            create::MsgraphMessageCreate,
100            create_mime::MsgraphMessageCreateMime,
101            delete::MsgraphMessageDelete,
102            get::MsgraphMessageGet,
103            get_raw::MsgraphMessageGetRaw,
104            list::{MsgraphMessagesList, MsgraphMessagesListParams, MsgraphMessagesListResponse},
105            r#move::MsgraphMessageMove,
106            send::MsgraphMessageSend,
107            update::MsgraphMessageUpdate,
108        },
109        send_mail::{MsgraphMailSend, MsgraphMailSendMime},
110    },
111    v1::send::{MsgraphNoResponse, MsgraphSendError, MsgraphSendOutput},
112};
113
114/// Error returned by [`MsgraphClientStd`] operations.
115#[derive(Debug, Error)]
116pub enum MsgraphClientStdError {
117    /// A coroutine completed with an error.
118    #[error(transparent)]
119    Send(#[from] MsgraphSendError),
120    /// Reading from or writing to the stream failed.
121    #[error(transparent)]
122    Io(#[from] io::Error),
123    /// Opening the TCP/TLS connection failed.
124    #[cfg(any(
125        feature = "rustls-aws",
126        feature = "rustls-ring",
127        feature = "native-tls"
128    ))]
129    #[error(transparent)]
130    Tls(#[from] anyhow::Error),
131    /// The API base URL has no host to connect to.
132    #[cfg(any(
133        feature = "rustls-aws",
134        feature = "rustls-ring",
135        feature = "native-tls"
136    ))]
137    #[error("Microsoft Graph URL `{0}` has no host")]
138    UrlMissingHost(String),
139    /// The API base URL scheme is neither http nor https.
140    #[cfg(any(
141        feature = "rustls-aws",
142        feature = "rustls-ring",
143        feature = "native-tls"
144    ))]
145    #[error(
146        "Microsoft Graph URL `{url}` has unsupported scheme `{scheme}` (expected `http` or `https`)"
147    )]
148    UrlUnsupportedScheme {
149        /// The rejected API base URL.
150        url: String,
151        /// The scheme of the rejected URL.
152        scheme: String,
153    },
154}
155
156/// Optional settings for [`MsgraphClientStd::connect`]; every field has a
157/// default (the TLS backend default, and `me` as the mailbox owner).
158pub struct MsgraphClientStdConnectOptions {
159    /// The TLS backend configuration used to open the connection.
160    #[cfg(any(
161        feature = "rustls-aws",
162        feature = "rustls-ring",
163        feature = "native-tls"
164    ))]
165    pub tls: Tls,
166    /// The mailbox owner: `me`, a user id or a principal name.
167    pub user_id: String,
168}
169
170impl Default for MsgraphClientStdConnectOptions {
171    fn default() -> Self {
172        Self {
173            #[cfg(any(
174                feature = "rustls-aws",
175                feature = "rustls-ring",
176                feature = "native-tls"
177            ))]
178            tls: Tls::default(),
179            user_id: String::from("me"),
180        }
181    }
182}
183
184const READ_BUFFER_SIZE: usize = 16 * 1024;
185
186/// Std blocking Microsoft Graph client: a stream, the bearer
187/// credential and the mailbox owner, with one method per operation.
188pub struct MsgraphClientStd {
189    /// The stream carrying the HTTPS connection to the Graph API.
190    pub stream: Box<dyn MsgraphStream>,
191    /// The bearer credential added to every request.
192    pub auth: HttpAuthBearer,
193    /// The mailbox owner: `me`, a user id or a principal name.
194    pub user_id: String,
195}
196
197impl MsgraphClientStd {
198    /// Builds a client over a caller-managed stream.
199    pub fn new<S: Read + Write + Send + 'static>(
200        stream: S,
201        token: impl ToString,
202        options: MsgraphClientStdConnectOptions,
203    ) -> Self {
204        Self {
205            stream: Box::new(stream),
206            auth: HttpAuthBearer::new(token.to_string()),
207            user_id: options.user_id,
208        }
209    }
210
211    /// Builds a client by opening a TCP/TLS connection to the Graph
212    /// API endpoint through pimalaya-stream.
213    #[cfg(any(
214        feature = "rustls-aws",
215        feature = "rustls-ring",
216        feature = "native-tls"
217    ))]
218    pub fn connect(
219        token: impl ToString,
220        options: MsgraphClientStdConnectOptions,
221    ) -> Result<Self, MsgraphClientStdError> {
222        let MsgraphClientStdConnectOptions { tls, user_id } = options;
223
224        let url = Url::parse(MSGRAPH_API_BASE).expect("Microsoft Graph API base URL is valid");
225        let host = url
226            .host_str()
227            .ok_or_else(|| MsgraphClientStdError::UrlMissingHost(url.to_string()))?;
228
229        let stream = match url.scheme() {
230            "http" => StreamStd::connect_tcp(host, url.port().unwrap_or(80))?,
231            "https" => StreamStd::connect_tls(host, url.port().unwrap_or(443), &tls)?,
232            scheme => {
233                return Err(MsgraphClientStdError::UrlUnsupportedScheme {
234                    url: url.to_string(),
235                    scheme: scheme.to_string(),
236                });
237            }
238        };
239
240        stream.set_read_timeout(Some(Duration::from_secs(30)))?;
241
242        Ok(Self {
243            stream: Box::new(stream),
244            auth: HttpAuthBearer::new(token.to_string()),
245            user_id,
246        })
247    }
248
249    /// Replaces the underlying stream (e.g. after a connection reset).
250    pub fn set_stream<S: Read + Write + Send + 'static>(&mut self, stream: S) {
251        self.stream = Box::new(stream);
252    }
253
254    /// Runs the given coroutine to completion against the stream,
255    /// fulfilling its read and write requests.
256    pub fn run<C, T>(
257        &mut self,
258        mut coroutine: C,
259    ) -> Result<MsgraphSendOutput<T>, MsgraphClientStdError>
260    where
261        C: MsgraphCoroutine<
262                Yield = MsgraphYield,
263                Return = Result<MsgraphSendOutput<T>, MsgraphSendError>,
264            >,
265    {
266        let mut buf = [0u8; READ_BUFFER_SIZE];
267        let mut arg: Option<&[u8]> = None;
268
269        loop {
270            match coroutine.resume(arg.take()) {
271                MsgraphCoroutineState::Complete(Ok(out)) => return Ok(out),
272                MsgraphCoroutineState::Complete(Err(err)) => return Err(err.into()),
273                MsgraphCoroutineState::Yielded(MsgraphYield::WantsRead) => {
274                    let n = self.stream.read(&mut buf)?;
275                    arg = Some(&buf[..n]);
276                }
277                MsgraphCoroutineState::Yielded(MsgraphYield::WantsWrite(bytes)) => {
278                    self.stream.write_all(&bytes)?;
279                    arg = None;
280                }
281            }
282        }
283    }
284
285    /// Gets the profile of the mailbox owner.
286    pub fn me(&mut self) -> Result<MsgraphSendOutput<MsgraphUser>, MsgraphClientStdError> {
287        let coroutine = MsgraphUserGet::new(&self.auth, &self.user_id)?;
288        self.run(coroutine)
289    }
290
291    /// Lists the mail folders of the mailbox.
292    pub fn mail_folders_list(
293        &mut self,
294        params: &MsgraphMailFoldersListParams,
295    ) -> Result<MsgraphSendOutput<MsgraphMailFoldersListResponse>, MsgraphClientStdError> {
296        let coroutine = MsgraphMailFoldersList::new(&self.auth, &self.user_id, params)?;
297        self.run(coroutine)
298    }
299
300    /// Gets a mail folder by id.
301    pub fn mail_folder_get(
302        &mut self,
303        id: &str,
304    ) -> Result<MsgraphSendOutput<MsgraphMailFolder>, MsgraphClientStdError> {
305        let coroutine = MsgraphMailFolderGet::new(&self.auth, &self.user_id, id)?;
306        self.run(coroutine)
307    }
308
309    /// Creates a mail folder.
310    pub fn mail_folder_create(
311        &mut self,
312        folder: &MsgraphMailFolder,
313    ) -> Result<MsgraphSendOutput<MsgraphMailFolder>, MsgraphClientStdError> {
314        let coroutine = MsgraphMailFolderCreate::new(&self.auth, &self.user_id, folder)?;
315        self.run(coroutine)
316    }
317
318    /// Updates a mail folder by id.
319    pub fn mail_folder_update(
320        &mut self,
321        id: &str,
322        folder: &MsgraphMailFolder,
323    ) -> Result<MsgraphSendOutput<MsgraphMailFolder>, MsgraphClientStdError> {
324        let coroutine = MsgraphMailFolderUpdate::new(&self.auth, &self.user_id, id, folder)?;
325        self.run(coroutine)
326    }
327
328    /// Deletes a mail folder by id.
329    pub fn mail_folder_delete(
330        &mut self,
331        id: &str,
332    ) -> Result<MsgraphSendOutput<MsgraphNoResponse>, MsgraphClientStdError> {
333        let coroutine = MsgraphMailFolderDelete::new(&self.auth, &self.user_id, id)?;
334        self.run(coroutine)
335    }
336
337    /// Copies a mail folder into a destination folder.
338    pub fn mail_folder_copy(
339        &mut self,
340        id: &str,
341        destination: &str,
342    ) -> Result<MsgraphSendOutput<MsgraphMailFolder>, MsgraphClientStdError> {
343        let coroutine = MsgraphMailFolderCopy::new(&self.auth, &self.user_id, id, destination)?;
344        self.run(coroutine)
345    }
346
347    /// Moves a mail folder into a destination folder.
348    pub fn mail_folder_move(
349        &mut self,
350        id: &str,
351        destination: &str,
352    ) -> Result<MsgraphSendOutput<MsgraphMailFolder>, MsgraphClientStdError> {
353        let coroutine = MsgraphMailFolderMove::new(&self.auth, &self.user_id, id, destination)?;
354        self.run(coroutine)
355    }
356
357    /// Lists the child folders of a mail folder.
358    pub fn mail_child_folders_list(
359        &mut self,
360        id: &str,
361        params: &MsgraphMailFoldersListParams,
362    ) -> Result<MsgraphSendOutput<MsgraphMailFoldersListResponse>, MsgraphClientStdError> {
363        let coroutine = MsgraphMailChildFoldersList::new(&self.auth, &self.user_id, id, params)?;
364        self.run(coroutine)
365    }
366
367    /// Lists the contact folders of the mailbox.
368    pub fn contact_folders_list(
369        &mut self,
370        params: &MsgraphContactFoldersListParams,
371    ) -> Result<MsgraphSendOutput<MsgraphContactFoldersListResponse>, MsgraphClientStdError> {
372        let coroutine = MsgraphContactFoldersList::new(&self.auth, &self.user_id, params)?;
373        self.run(coroutine)
374    }
375
376    /// Gets a contact folder by id.
377    pub fn contact_folder_get(
378        &mut self,
379        id: &str,
380    ) -> Result<MsgraphSendOutput<MsgraphContactFolder>, MsgraphClientStdError> {
381        let coroutine = MsgraphContactFolderGet::new(&self.auth, &self.user_id, id)?;
382        self.run(coroutine)
383    }
384
385    /// Creates a contact folder.
386    pub fn contact_folder_create(
387        &mut self,
388        folder: &MsgraphContactFolder,
389    ) -> Result<MsgraphSendOutput<MsgraphContactFolder>, MsgraphClientStdError> {
390        let coroutine = MsgraphContactFolderCreate::new(&self.auth, &self.user_id, folder)?;
391        self.run(coroutine)
392    }
393
394    /// Updates a contact folder by id.
395    pub fn contact_folder_update(
396        &mut self,
397        id: &str,
398        folder: &MsgraphContactFolder,
399    ) -> Result<MsgraphSendOutput<MsgraphContactFolder>, MsgraphClientStdError> {
400        let coroutine = MsgraphContactFolderUpdate::new(&self.auth, &self.user_id, id, folder)?;
401        self.run(coroutine)
402    }
403
404    /// Deletes a contact folder by id.
405    pub fn contact_folder_delete(
406        &mut self,
407        id: &str,
408    ) -> Result<MsgraphSendOutput<MsgraphNoResponse>, MsgraphClientStdError> {
409        let coroutine = MsgraphContactFolderDelete::new(&self.auth, &self.user_id, id)?;
410        self.run(coroutine)
411    }
412
413    /// Lists the child folders of a contact folder.
414    pub fn contact_child_folders_list(
415        &mut self,
416        id: &str,
417        params: &MsgraphContactFoldersListParams,
418    ) -> Result<MsgraphSendOutput<MsgraphContactFoldersListResponse>, MsgraphClientStdError> {
419        let coroutine = MsgraphContactChildFoldersList::new(&self.auth, &self.user_id, id, params)?;
420        self.run(coroutine)
421    }
422
423    /// Lists the contacts of the default Contacts folder, or of the
424    /// given contact folder.
425    pub fn contacts_list(
426        &mut self,
427        folder: Option<&str>,
428        params: &MsgraphContactsListParams,
429    ) -> Result<MsgraphSendOutput<MsgraphContactsListResponse>, MsgraphClientStdError> {
430        let coroutine = MsgraphContactsList::new(&self.auth, &self.user_id, folder, params)?;
431        self.run(coroutine)
432    }
433
434    /// Gets a contact by id, optionally expanding the given relations.
435    pub fn contact_get(
436        &mut self,
437        id: &str,
438        expand: Option<&str>,
439    ) -> Result<MsgraphSendOutput<MsgraphContact>, MsgraphClientStdError> {
440        let coroutine = MsgraphContactGet::new(&self.auth, &self.user_id, id, expand)?;
441        self.run(coroutine)
442    }
443
444    /// Creates a contact in the default Contacts folder, or in the
445    /// given contact folder.
446    pub fn contact_create(
447        &mut self,
448        folder: Option<&str>,
449        contact: &MsgraphContact,
450    ) -> Result<MsgraphSendOutput<MsgraphContact>, MsgraphClientStdError> {
451        let coroutine = MsgraphContactCreate::new(&self.auth, &self.user_id, folder, contact)?;
452        self.run(coroutine)
453    }
454
455    /// Updates a contact by id.
456    pub fn contact_update(
457        &mut self,
458        id: &str,
459        contact: &MsgraphContact,
460    ) -> Result<MsgraphSendOutput<MsgraphContact>, MsgraphClientStdError> {
461        let coroutine = MsgraphContactUpdate::new(&self.auth, &self.user_id, id, contact)?;
462        self.run(coroutine)
463    }
464
465    /// Deletes a contact by id.
466    pub fn contact_delete(
467        &mut self,
468        id: &str,
469    ) -> Result<MsgraphSendOutput<MsgraphNoResponse>, MsgraphClientStdError> {
470        let coroutine = MsgraphContactDelete::new(&self.auth, &self.user_id, id)?;
471        self.run(coroutine)
472    }
473
474    /// Starts a contacts delta round over the default Contacts folder,
475    /// or over the given contact folder.
476    pub fn contacts_delta(
477        &mut self,
478        folder: Option<&str>,
479        select: Option<&str>,
480    ) -> Result<MsgraphSendOutput<MsgraphContactsDeltaResponse>, MsgraphClientStdError> {
481        let coroutine = MsgraphContactsDelta::new(&self.auth, &self.user_id, folder, select)?;
482        self.run(coroutine)
483    }
484
485    /// Lists the messages of the whole mailbox, or of the given mail
486    /// folder.
487    pub fn messages_list(
488        &mut self,
489        folder: Option<&str>,
490        params: &MsgraphMessagesListParams,
491    ) -> Result<MsgraphSendOutput<MsgraphMessagesListResponse>, MsgraphClientStdError> {
492        let coroutine = MsgraphMessagesList::new(&self.auth, &self.user_id, folder, params)?;
493        self.run(coroutine)
494    }
495
496    /// Gets a message by id.
497    pub fn message_get(
498        &mut self,
499        id: &str,
500    ) -> Result<MsgraphSendOutput<MsgraphMessage>, MsgraphClientStdError> {
501        let coroutine = MsgraphMessageGet::new(&self.auth, &self.user_id, id)?;
502        self.run(coroutine)
503    }
504
505    /// Gets the raw RFC 5322 MIME content of a message by id.
506    pub fn message_get_raw(
507        &mut self,
508        id: &str,
509    ) -> Result<MsgraphSendOutput<Vec<u8>>, MsgraphClientStdError> {
510        let coroutine = MsgraphMessageGetRaw::new(&self.auth, &self.user_id, id)?;
511        self.run(coroutine)
512    }
513
514    /// Creates a draft message from JSON in the Drafts folder, or in
515    /// the given mail folder.
516    pub fn message_create(
517        &mut self,
518        folder: Option<&str>,
519        message: &MsgraphMessage,
520    ) -> Result<MsgraphSendOutput<MsgraphMessage>, MsgraphClientStdError> {
521        let coroutine = MsgraphMessageCreate::new(&self.auth, &self.user_id, folder, message)?;
522        self.run(coroutine)
523    }
524
525    /// Creates a draft message from raw RFC 5322 MIME bytes in the
526    /// Drafts folder, or in the given mail folder.
527    pub fn message_create_mime(
528        &mut self,
529        folder: Option<&str>,
530        raw: &[u8],
531    ) -> Result<MsgraphSendOutput<MsgraphMessage>, MsgraphClientStdError> {
532        let coroutine = MsgraphMessageCreateMime::new(&self.auth, &self.user_id, folder, raw)?;
533        self.run(coroutine)
534    }
535
536    /// Updates a message by id.
537    pub fn message_update(
538        &mut self,
539        id: &str,
540        message: &MsgraphMessage,
541    ) -> Result<MsgraphSendOutput<MsgraphMessage>, MsgraphClientStdError> {
542        let coroutine = MsgraphMessageUpdate::new(&self.auth, &self.user_id, id, message)?;
543        self.run(coroutine)
544    }
545
546    /// Deletes a message by id.
547    pub fn message_delete(
548        &mut self,
549        id: &str,
550    ) -> Result<MsgraphSendOutput<MsgraphNoResponse>, MsgraphClientStdError> {
551        let coroutine = MsgraphMessageDelete::new(&self.auth, &self.user_id, id)?;
552        self.run(coroutine)
553    }
554
555    /// Moves a message into a destination folder.
556    pub fn message_move(
557        &mut self,
558        id: &str,
559        destination: &str,
560    ) -> Result<MsgraphSendOutput<MsgraphMessage>, MsgraphClientStdError> {
561        let coroutine = MsgraphMessageMove::new(&self.auth, &self.user_id, id, destination)?;
562        self.run(coroutine)
563    }
564
565    /// Copies a message into a destination folder.
566    pub fn message_copy(
567        &mut self,
568        id: &str,
569        destination: &str,
570    ) -> Result<MsgraphSendOutput<MsgraphMessage>, MsgraphClientStdError> {
571        let coroutine = MsgraphMessageCopy::new(&self.auth, &self.user_id, id, destination)?;
572        self.run(coroutine)
573    }
574
575    /// Creates a file attachment on a message.
576    pub fn attachment_create(
577        &mut self,
578        message_id: &str,
579        name: &str,
580        content: &[u8],
581        content_type: Option<&str>,
582    ) -> Result<MsgraphSendOutput<MsgraphAttachment>, MsgraphClientStdError> {
583        let coroutine = MsgraphAttachmentCreate::new(
584            &self.auth,
585            &self.user_id,
586            message_id,
587            name,
588            content,
589            content_type,
590        )?;
591        self.run(coroutine)
592    }
593
594    /// Lists the attachments of a message.
595    pub fn attachments_list(
596        &mut self,
597        message_id: &str,
598    ) -> Result<MsgraphSendOutput<MsgraphAttachmentsListResponse>, MsgraphClientStdError> {
599        let coroutine = MsgraphAttachmentsList::new(&self.auth, &self.user_id, message_id)?;
600        self.run(coroutine)
601    }
602
603    /// Gets the raw content of an attachment.
604    pub fn attachment_get_raw(
605        &mut self,
606        message_id: &str,
607        attachment_id: &str,
608    ) -> Result<MsgraphSendOutput<Vec<u8>>, MsgraphClientStdError> {
609        let coroutine =
610            MsgraphAttachmentGetRaw::new(&self.auth, &self.user_id, message_id, attachment_id)?;
611        self.run(coroutine)
612    }
613
614    /// Deletes an attachment of a message.
615    pub fn attachment_delete(
616        &mut self,
617        message_id: &str,
618        attachment_id: &str,
619    ) -> Result<MsgraphSendOutput<MsgraphNoResponse>, MsgraphClientStdError> {
620        let coroutine =
621            MsgraphAttachmentDelete::new(&self.auth, &self.user_id, message_id, attachment_id)?;
622        self.run(coroutine)
623    }
624
625    /// Sends an existing draft message by id.
626    pub fn message_send(
627        &mut self,
628        id: &str,
629    ) -> Result<MsgraphSendOutput<MsgraphNoResponse>, MsgraphClientStdError> {
630        let coroutine = MsgraphMessageSend::new(&self.auth, &self.user_id, id)?;
631        self.run(coroutine)
632    }
633
634    /// Sends a message described as JSON through the sendMail action.
635    pub fn mail_send(
636        &mut self,
637        message: &MsgraphMessage,
638        save_to_sent_items: bool,
639    ) -> Result<MsgraphSendOutput<MsgraphNoResponse>, MsgraphClientStdError> {
640        let coroutine =
641            MsgraphMailSend::new(&self.auth, &self.user_id, message, save_to_sent_items)?;
642        self.run(coroutine)
643    }
644
645    /// Sends a message given as raw RFC 5322 MIME bytes through the
646    /// sendMail action.
647    pub fn mail_send_mime(
648        &mut self,
649        raw: &[u8],
650    ) -> Result<MsgraphSendOutput<MsgraphNoResponse>, MsgraphClientStdError> {
651        let coroutine = MsgraphMailSendMime::new(&self.auth, &self.user_id, raw)?;
652        self.run(coroutine)
653    }
654}
655
656impl fmt::Debug for MsgraphClientStd {
657    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
658        f.debug_struct("MsgraphClientStd")
659            .field("auth", &self.auth)
660            .field("user_id", &self.user_id)
661            .finish_non_exhaustive()
662    }
663}
664
665/// Blocking stream the client runs over, downcastable through `Any`
666/// (e.g. to recover a concrete TLS stream).
667pub trait MsgraphStream: Read + Write + Send + Any {
668    /// The stream as a mutable `Any`, ready for downcasting.
669    fn as_any_mut(&mut self) -> &mut dyn Any;
670}
671
672impl<T: Read + Write + Send + Any> MsgraphStream for T {
673    fn as_any_mut(&mut self) -> &mut dyn Any {
674        self
675    }
676}