Skip to main content

mail_list/
mailer.rs

1use std::borrow::Cow;
2
3use lettre::{AsyncSmtpTransport, AsyncTransport, Message, transport::smtp::response::Severity};
4
5use crate::{EmailEnvelopeDetails, MailListError, MailListResult};
6
7#[derive(Debug)]
8pub struct Smtps {
9    pub(crate) domain: Cow<'static, str>,
10    pub(crate) ehlo_name: Option<Cow<'static, str>>,
11    #[cfg(feature = "tokio-runtime")]
12    pub(crate) mailer: AsyncSmtpTransport<lettre::Tokio1Executor>,
13}
14
15impl Smtps {
16    pub fn domain(&self) -> &str {
17        self.domain.as_ref()
18    }
19
20    pub fn ehlo_name(&self) -> Option<&Cow<'_, str>> {
21        self.ehlo_name.as_ref()
22    }
23
24    #[cfg(feature = "tokio-runtime")]
25    pub fn mailer(&self) -> &AsyncSmtpTransport<lettre::Tokio1Executor> {
26        &self.mailer
27    }
28
29    pub async fn send(&self, message: &EmailEnvelopeDetails) -> MailListResult<()> {
30        let email = Message::builder()
31            .from(message.from_as_mailbox())
32            .reply_to(message.reply_as_mailbox())
33            .to(message.to_as_mailbox())
34            .subject(message.subject.as_str())
35            .header(lettre::message::header::ContentType::TEXT_HTML)
36            .body(message.body.to_string())
37            .map_err(|error| MailListError::Mailer(error.to_string()))?;
38
39        let response = self
40            .mailer
41            .send(email)
42            .await
43            .map_err(|error| MailListError::MailDelivery(error.to_string()))?;
44
45        if response.code().severity == Severity::PositiveCompletion {
46            Ok(())
47        } else {
48            Err(MailListError::Smtps(
49                response.message().map(|msg| msg.to_string()).collect(),
50            ))
51        }
52    }
53
54    pub async fn test_connection(&self) -> MailListResult<bool> {
55        self.mailer
56            .test_connection()
57            .await
58            .map_err(|error| MailListError::Mailer(error.to_string()))
59    }
60}