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