Skip to main content

pidge_client/graph/
mod.rs

1//! Microsoft Graph API client.
2
3mod mail;
4mod me;
5
6pub use mail::{get_attachment_bytes, get_message, list_attachments, list_inbox, mark_read};
7pub use me::{Me, get_me};
8
9use crate::auth::AuthClient;
10use crate::auth::config;
11use crate::error::ClientError;
12use pidge_core::Message;
13
14/// Stateful Microsoft Graph client. Holds an AuthClient and a shared HTTP client.
15pub struct GraphClient {
16    auth: AuthClient,
17    http: reqwest::Client,
18    base_url: String,
19}
20
21impl GraphClient {
22    pub fn new(auth: AuthClient) -> Result<Self, ClientError> {
23        Ok(Self {
24            auth,
25            http: reqwest::Client::builder()
26                .user_agent(format!("pidge/{}", env!("CARGO_PKG_VERSION")))
27                .build()?,
28            base_url: config::GRAPH_BASE.to_string(),
29        })
30    }
31
32    pub fn for_test(auth: AuthClient, base_url: impl Into<String>) -> Self {
33        Self {
34            auth,
35            http: reqwest::Client::new(),
36            base_url: base_url.into(),
37        }
38    }
39
40    pub fn auth(&self) -> &AuthClient {
41        &self.auth
42    }
43
44    /// GET /me. Used right after sign-in to learn the user's email.
45    pub async fn me(&self, access_token: &str) -> Result<Me, ClientError> {
46        get_me(&self.http, &self.base_url, access_token).await
47    }
48
49    /// GET /me/mailFolders/inbox/messages for a given account email.
50    /// Acquires/refreshes a token transparently via `AuthClient::get_valid_token`.
51    pub async fn list_inbox(
52        &self,
53        account: &str,
54        limit: usize,
55        unread_only: bool,
56    ) -> Result<Vec<Message>, ClientError> {
57        let token = self.auth.get_valid_token(account).await?;
58        list_inbox(
59            &self.http,
60            &self.base_url,
61            &token,
62            account,
63            limit,
64            unread_only,
65        )
66        .await
67    }
68
69    /// GET /me/messages/{id} for a given account email.
70    pub async fn get_message(
71        &self,
72        account: &str,
73        message_id: &str,
74    ) -> Result<pidge_core::FullMessage, ClientError> {
75        let token = self.auth.get_valid_token(account).await?;
76        mail::get_message(&self.http, &self.base_url, &token, account, message_id).await
77    }
78
79    /// GET /me/messages/{id}/attachments.
80    pub async fn list_attachments(
81        &self,
82        account: &str,
83        message_id: &str,
84    ) -> Result<Vec<pidge_core::Attachment>, ClientError> {
85        let token = self.auth.get_valid_token(account).await?;
86        mail::list_attachments(&self.http, &self.base_url, &token, message_id).await
87    }
88
89    /// GET /me/messages/{id}/attachments/{att_id} returning decoded bytes.
90    pub async fn get_attachment_bytes(
91        &self,
92        account: &str,
93        message_id: &str,
94        attachment_id: &str,
95    ) -> Result<Vec<u8>, ClientError> {
96        let token = self.auth.get_valid_token(account).await?;
97        mail::get_attachment_bytes(
98            &self.http,
99            &self.base_url,
100            &token,
101            message_id,
102            attachment_id,
103        )
104        .await
105    }
106
107    /// PATCH /me/messages/{id} with isRead: true.
108    pub async fn mark_read(&self, account: &str, message_id: &str) -> Result<(), ClientError> {
109        let token = self.auth.get_valid_token(account).await?;
110        mail::mark_read(&self.http, &self.base_url, &token, message_id).await
111    }
112}