email_service/
lib.rs

1use lettre::{
2    transport::smtp::{self, authentication::Credentials},
3    Address, Message, Transport,
4};
5
6#[derive(Clone)]
7pub struct EmailClientConfig {
8    pub smtp_host: String,
9    pub smtp_port: u16,
10    pub smtp_username: String,
11    pub smtp_password: String,
12}
13
14#[derive(Clone)]
15pub struct EmailClient {
16    config: EmailClientConfig,
17}
18
19impl EmailClient {
20    pub fn new(config: EmailClientConfig) -> Self {
21        EmailClient { config }
22    }
23
24    pub async fn send_email(
25        &self,
26        to: String,
27        subject: String,
28        body: String,
29    ) -> Result<(), Box<dyn std::error::Error>> {
30        let smtp_config = smtp::SmtpTransport::starttls_relay(&self.config.smtp_host)
31            .unwrap()
32            .port(self.config.smtp_port)
33            .credentials(Credentials::new(
34                self.config.smtp_username.clone(),
35                self.config.smtp_password.clone(),
36            ))
37            .build();
38
39        let _conn = smtp_config.test_connection()?;
40
41        let address = self.config.smtp_username.parse::<Address>()?;
42
43        let mailbox = lettre::message::Mailbox::new(None, address);
44
45        let email = Message::builder()
46            .from(mailbox)
47            .to(to.parse().unwrap())
48            .subject(subject)
49            .body(body)
50            .unwrap();
51
52        smtp_config.send(&email)?;
53
54        Ok(())
55    }
56}
57
58#[cfg(test)]
59mod tests {
60    use super::*;
61    use dotenv::dotenv;
62    use std::env;
63
64    #[tokio::test]
65    async fn test_smtp_connection() -> Result<(), Box<dyn std::error::Error>> {
66        dotenv().ok();
67
68        let smtp_host = env::var("SMTP_HOST").expect("SMTP_HOST is missing");
69        let smtp_port = env::var("SMTP_PORT")
70            .expect("SMTP_PORT is missing")
71            .parse::<u16>()?;
72        let smtp_username = env::var("SMTP_USERNAME").expect("SMTP_USERNAME is missing");
73        let smtp_password = env::var("SMTP_PASSWORD").expect("SMTP_PASSWORD is missing");
74
75        let config = EmailClientConfig {
76            smtp_host,
77            smtp_port,
78            smtp_username,
79            smtp_password,
80        };
81
82        let email_client = EmailClient::new(config);
83
84        // Attempt to send a test email (replace with a valid recipient)
85        let result = email_client
86            .send_email(
87                "test@example.com".to_string(),
88                "Test Connection".to_string(),
89                "Test".to_string(),
90            )
91            .await;
92
93        // Assert that the connection was successful (no error)
94        assert!(result.is_ok());
95
96        Ok(())
97    }
98}