mail-list 0.2.5-alpha

Email subscriptions list library. Unicast, narrowcast and broadcast lists
Documentation
use std::borrow::Cow;

use lettre::{
    Address, AsyncSmtpTransport, AsyncTransport, Message, message::Mailbox,
    transport::smtp::response::Severity,
};

use crate::{EmailEnvelopeDetails, MailListError, MailListResult};

#[derive(Debug)]
pub struct Smtps {
    pub(crate) domain: Cow<'static, str>,
    pub(crate) from: Mailbox,
    pub(crate) reply_to: Option<Mailbox>,
    pub(crate) ehlo_name: Option<Cow<'static, str>>,
    #[cfg(feature = "tokio-runtime")]
    pub(crate) mailer: AsyncSmtpTransport<lettre::Tokio1Executor>,
}

impl Smtps {
    pub fn set_reply_to(
        &mut self,
        reply_to_name: &str,
        reply_to_address: &str,
    ) -> MailListResult<&mut Self> {
        self.reply_to.replace(Mailbox::new(
            Some(reply_to_name.to_string()),
            reply_to_address
                .parse::<Address>()
                .map_err(|error| MailListError::Mailer(error.to_string()))?,
        ));

        Ok(self)
    }

    pub fn domain(&self) -> &str {
        self.domain.as_ref()
    }

    pub fn ehlo_name(&self) -> Option<&Cow<'_, str>> {
        self.ehlo_name.as_ref()
    }

    pub fn from(&self) -> &Mailbox {
        &self.from
    }

    pub fn reply_to(&self) -> Option<&Mailbox> {
        self.reply_to.as_ref()
    }

    #[cfg(feature = "tokio-runtime")]
    pub fn mailer(&self) -> &AsyncSmtpTransport<lettre::Tokio1Executor> {
        &self.mailer
    }

    pub async fn send(&self, message: &EmailEnvelopeDetails) -> MailListResult<()> {
        let email = Message::builder().from(self.from().clone());

        let email = if let Some(reply_to) = self.reply_to() {
            email.reply_to(reply_to.clone())
        } else {
            email
        };

        let email = email
            .to(message
                .to
                .parse::<Mailbox>()
                .map_err(|error| MailListError::Mailer(error.to_string()))?)
            .subject(message.subject.as_str())
            .header(lettre::message::header::ContentType::TEXT_HTML)
            .body(message.body.to_string())
            .map_err(|error| MailListError::Mailer(error.to_string()))?;

        let response = self
            .mailer
            .send(email)
            .await
            .map_err(|error| MailListError::MailDelivery(error.to_string()))?;

        if response.code().severity == Severity::PositiveCompletion {
            Ok(())
        } else {
            Err(MailListError::Smtps(
                response.message().map(|msg| msg.to_string()).collect(),
            ))
        }
    }

    pub async fn test_connection(&self) -> MailListResult<bool> {
        self.mailer
            .test_connection()
            .await
            .map_err(|error| MailListError::Mailer(error.to_string()))
    }
}