rust_gmail/
lib.rs

1#![forbid(unsafe_code)]
2#![deny(missing_docs)]
3#![deny(missing_debug_implementations)]
4
5//! # A Rust library to interact with the google Gmail API using a service account.
6//!
7//! Currently focused only on support for sending emails but this may change in the future.
8//! Available both as async (default) or as blocking using the "blocking" feature.
9//!
10//! Note that usage of this lib will require a google API service account with domain wide delegation for gmail setup with a google cloud project with the gmail API enabled.
11//! Links for more information:
12//!  - <https://cloud.google.com/iam/docs/service-account-overview>
13//!  - <https://support.google.com/a/answer/162106?hl=en&fl=1&sjid=12697421685211806668-NA>
14//!  - <https://developers.google.com/gmail/api/guides>
15//!
16//! ## Features
17//! There is currently only one feature, `blocking` which will add blocking alternatives to all async functions with the same name suffixed with `_blocking`.
18//! E.g. `send_email_blocking` instead of `send_email`.
19//!
20//! ## Examples
21//! Examples of how to use this crate.
22//!
23//! ### Async Example
24//! ```rust
25//! let email_client = GmailClient::builder(
26//!     "service_account.json",
27//!     "noreply@example.test",
28//! )?
29//! .build()
30//! .await?;
31//!
32//! email_client
33//!     .send_email("some_user@domain.test")
34//!     .await?;
35//! ```
36//!
37//! ### Blocking Example
38//! Note: Requires the `blocking` feature.
39//! ```rust
40//! let email_client = GmailClient::builder(
41//!     "service_account.json",
42//!     "noreply@example.test",
43//! )?
44//! .build_blocking()?;
45//!
46//! email_client
47//!     .send_email_blocking("some_user@domain.test")?;
48//! ```
49
50use std::path::Path;
51
52use async_impl::{send_email::send_email, token::retrieve_token};
53use error::Result;
54use service_account::ServiceAccount;
55
56/// Types for error handling.
57pub mod error;
58
59mod async_impl;
60mod common;
61mod service_account;
62
63#[cfg(feature = "blocking")]
64mod blocking;
65
66/// The `GmailClientBuilder` is the intended way of creating a [`GmailClient`].
67#[derive(Debug, Clone)]
68pub struct GmailClientBuilder {
69    service_account: ServiceAccount,
70    send_from_email: String,
71    mock_mode: bool,
72}
73
74impl<'a> GmailClientBuilder {
75    /// Create a new `GmailClientBuilder`.
76    /// Will return an error if unable to read & parse the `service_account_path` if, for example, the file does not exist or has an incorrect format.
77    pub fn new<P: AsRef<Path>, S: Into<String>>(
78        service_account_path: P,
79        send_from_email: S,
80    ) -> Result<Self> {
81        Ok(Self {
82            service_account: ServiceAccount::load_from_file(service_account_path)?,
83            send_from_email: send_from_email.into(),
84            mock_mode: false,
85        })
86    }
87
88    /// Set "mock mode" which, if set to true, will log print the email instead of sending it.
89    pub fn mock_mode(mut self, enabled: bool) -> Self {
90        self.mock_mode = enabled;
91        self
92    }
93
94    /// Build a [`GmailClient`] from this `GmailClientBuilder`.
95    /// Note: This function will retrieve an access token from the Google API and as such make an API request.
96    pub async fn build(self) -> Result<GmailClient> {
97        let token = retrieve_token(&self.service_account, &self.send_from_email).await?;
98
99        Ok(GmailClient {
100            send_from_email: self.send_from_email,
101            token,
102            mock_mode: self.mock_mode,
103        })
104    }
105
106    /// A blocking alternative to the [`build()`] function.
107    #[cfg(feature = "blocking")]
108    pub fn build_blocking(self) -> Result<GmailClient> {
109        use blocking::token::retrieve_token_blocking;
110
111        let token = retrieve_token_blocking(&self.service_account, &self.send_from_email)?;
112
113        Ok(GmailClient {
114            send_from_email: self.send_from_email,
115            token,
116            mock_mode: self.mock_mode,
117        })
118    }
119}
120
121/// A client ready to send emails through the Gmail API.
122#[derive(Debug, Clone)]
123pub struct GmailClient {
124    send_from_email: String,
125    token: String,
126    mock_mode: bool,
127}
128
129impl GmailClient {
130    /// Alias for [`GmailClientBuilder::new()`].
131    pub fn builder<P: AsRef<Path>, S: Into<String>>(
132        service_account_path: P,
133        send_from_email: S,
134    ) -> Result<GmailClientBuilder> {
135        GmailClientBuilder::new(service_account_path, send_from_email)
136    }
137
138    /// Send an email to `send_to_email` with the specified `subject` and `content`.
139    pub async fn send_email(
140        &self,
141        send_to_email: &str,
142        subject: &str,
143        content: &str,
144    ) -> Result<()> {
145        send_email(
146            send_to_email,
147            subject,
148            content,
149            &self.token,
150            &self.send_from_email,
151            self.mock_mode,
152        )
153        .await
154    }
155
156    /// A blocking alternative to [`send_email()`].
157    #[cfg(feature = "blocking")]
158    pub fn send_email_blocking(
159        &self,
160        send_to_email: &str,
161        subject: &str,
162        content: &str,
163    ) -> Result<()> {
164        use blocking::send_email::send_email_blocking;
165
166        send_email_blocking(
167            send_to_email,
168            subject,
169            content,
170            &self.token,
171            &self.send_from_email,
172            self.mock_mode,
173        )
174    }
175}