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}