use brass_config::{Config, SmtpTlsType}; use lettre::{ address::Envelope, transport::{ smtp::{authentication::Credentials, extension::ClientId}, stub::AsyncStubTransport, }, AsyncSmtpTransport, AsyncStd1Executor, AsyncTransport, }; use crate::utils::ApplicationError; mod forgot_password; mod registration; mod testmail; #[derive(Clone, Debug)] pub struct Mailer { transport: Transports, hostname: String, } #[derive(Clone, Debug)] enum Transports { SmtpTransport(AsyncSmtpTransport), #[allow(unused)] StubTransport(AsyncStubTransport), } impl AsyncTransport for Transports { type Ok = (); type Error = ApplicationError; fn send_raw<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, envelope: &'life1 Envelope, email: &'life2 [u8], ) -> ::core::pin::Pin< Box< dyn ::core::future::Future> + ::core::marker::Send + 'async_trait, >, > where 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, Self: 'async_trait, { Box::pin(async move { match self { Transports::SmtpTransport(smtp_transport) => smtp_transport .send_raw(envelope, email) .await .map(|_| ()) .map_err(ApplicationError::EmailTransport), Transports::StubTransport(stub_transport) => stub_transport .send_raw(envelope, email) .await .map(|_| ()) .map_err(ApplicationError::EmailStubTransport), } }) } } impl Mailer { pub fn new(config: &Config) -> anyhow::Result { let mut builder = match config.smtp_tlstype { SmtpTlsType::StartTLS => { AsyncSmtpTransport::::starttls_relay(&config.smtp_server)? .port(config.smtp_port) } SmtpTlsType::TLS => { AsyncSmtpTransport::::relay(&config.smtp_server)? .port(config.smtp_port) } SmtpTlsType::NoTLS => { AsyncSmtpTransport::::builder_dangerous(&config.smtp_server) .port(config.smtp_port) } }; if let (Some(login), Some(password)) = (config.smtp_login.as_ref(), config.smtp_password.as_ref()) { builder = builder.credentials(Credentials::new(login.to_string(), password.to_string())); } let transport = builder .hello_name(ClientId::Domain(config.hostname.clone())) .build(); let mailer = Mailer { transport: Transports::SmtpTransport(transport), hostname: config.hostname.to_string(), }; Ok(mailer) } } #[cfg(test)] impl Mailer { pub fn new_stub() -> Self { Mailer { transport: Transports::StubTransport(AsyncStubTransport::new_ok()), hostname: String::from("testhostname"), } } }