use email_address::EmailAddress; use lettre::Transport; use log::error; use std::collections::HashMap; #[derive(Debug, thiserror::Error, derive_more::Display)] pub enum EmailMessengerError { FailedToSendEmail, } pub struct EmailValidationMessage { pub recipient: EmailAddress, pub subject: String, pub body: String, } #[async_trait::async_trait] pub(crate) trait EmailMessenger { async fn send_email( &self, email_address: &EmailAddress, template: &str, template_vars: HashMap<&str, &str>, ) -> Result<(), EmailMessengerError>; } pub(crate) struct LocalMailer {} #[async_trait::async_trait] impl EmailMessenger for LocalMailer { async fn send_email( &self, email_address: &EmailAddress, template: &str, template_vars: HashMap<&str, &str>, ) -> Result<(), EmailMessengerError> { todo!() } } #[async_trait::async_trait] pub(crate) trait Sendable { async fn send(&self) -> Result<(), EmailMessengerError>; } #[async_trait::async_trait] impl Sendable for EmailValidationMessage { // TODO: We need to break this up as before, especially so we can feature // gate unwanted things like Lettre... async fn send(&self) -> Result<(), EmailMessengerError> { // TODO: Get these things from the template... let email = lettre::Message::builder() .from("BranchControl ".parse().unwrap()) .reply_to("BranchControl ".parse().unwrap()) .to(self.recipient.to_string().parse().unwrap()) .subject(self.subject.clone()) .body(self.body.clone()) .unwrap(); let mailer = lettre::SmtpTransport::unencrypted_localhost(); mailer.send(&email).map_err(|e| { error!("failed to send email {:?}", e); EmailMessengerError::FailedToSendEmail })?; Ok(()) } }