use std::time::Duration;
use color_eyre::Result;
use lettre::transport::smtp::authentication::Credentials;
use lettre::{AsyncSmtpTransport, AsyncTransport, Message, Tokio1Executor};
use serde::{Deserialize, Serialize};
use tracing::{error, info};
#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(default)]
pub struct MailerConfig {
pub username: String,
pub password: String,
pub relay: String,
pub port: u16,
pub tls: bool,
pub timeout: u64,
}
impl Default for MailerConfig {
fn default() -> Self {
Self {
username: Default::default(),
password: Default::default(),
relay: Default::default(),
port: 465,
tls: false,
timeout: 5000,
}
}
}
#[derive(Clone)]
pub struct Mailer {
address: String,
mailer: AsyncSmtpTransport<Tokio1Executor>,
}
impl Mailer {
pub fn new(config: &MailerConfig) -> Result<Self> {
let creds = Credentials::new(config.username.to_string(), config.password.to_string());
let builder = if config.tls {
AsyncSmtpTransport::<Tokio1Executor>::starttls_relay(&config.relay)?
} else {
AsyncSmtpTransport::<Tokio1Executor>::relay(&config.relay)?
};
let mailer = builder
.port(config.port)
.credentials(creds)
.timeout(Some(Duration::from_millis(config.timeout)))
.build();
Ok(Self {
address: config.username.to_string(),
mailer,
})
}
pub async fn send(&self, subject: &str, body: &str, to: &str) {
let email = Message::builder()
.from(self.address.parse().unwrap())
.to(to.parse().unwrap())
.subject(subject)
.body(String::from(body))
.unwrap();
match self.mailer.send(email).await {
Ok(_) => info!("Email sent successfully!"),
Err(e) => error!("Could not send email: {:?}", e),
}
}
}
#[tokio::test]
async fn test_mailer() -> Result<()> {
let mailer = Mailer::new(&MailerConfig {
username: "username".to_string(),
password: "password".to_string(),
relay: "relay".to_string(),
port: 587,
tls: false,
timeout: 5000,
})?;
mailer
.send("Test", "This is a test email", "name@host")
.await;
Ok(())
}