Skip to main content

mail_list/
mailer.rs

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