use lettre::{
transport::smtp::{self, authentication::Credentials},
Address, Message, Transport,
};
pub struct EmailClientConfig {
pub smtp_host: String,
pub smtp_port: u16,
pub smtp_username: String,
pub smtp_password: String,
}
pub struct EmailClient {
config: EmailClientConfig,
}
impl EmailClient {
pub fn new(config: EmailClientConfig) -> Self {
EmailClient { config }
}
pub async fn send_email(
&self,
to: String,
subject: String,
body: String,
) -> Result<(), Box<dyn std::error::Error>> {
let smtp_config = smtp::SmtpTransport::starttls_relay(&self.config.smtp_host)
.unwrap()
.port(self.config.smtp_port)
.credentials(Credentials::new(
self.config.smtp_username.clone(),
self.config.smtp_password.clone(),
))
.build();
let _conn = smtp_config.test_connection()?;
let address = self.config.smtp_username.parse::<Address>()?;
let mailbox = lettre::message::Mailbox::new(None, address);
let email = Message::builder()
.from(mailbox)
.to(to.parse().unwrap())
.subject(subject)
.body(body)
.unwrap();
smtp_config.send(&email)?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use dotenv::dotenv;
use std::env;
#[tokio::test]
async fn test_smtp_connection() -> Result<(), Box<dyn std::error::Error>> {
dotenv().ok();
let smtp_host = env::var("SMTP_HOST").expect("SMTP_HOST is missing");
let smtp_port = env::var("SMTP_PORT")
.expect("SMTP_PORT is missing")
.parse::<u16>()?;
let smtp_username = env::var("SMTP_USERNAME").expect("SMTP_USERNAME is missing");
let smtp_password = env::var("SMTP_PASSWORD").expect("SMTP_PASSWORD is missing");
let config = EmailClientConfig {
smtp_host,
smtp_port,
smtp_username,
smtp_password,
};
let email_client = EmailClient::new(config);
let result = email_client
.send_email(
"test@example.com".to_string(),
"Test Connection".to_string(),
"Test".to_string(),
)
.await;
assert!(result.is_ok());
Ok(())
}
}