use lettre::AsyncSmtpTransport;
use percent_encoding::percent_decode_str;
use crate::{MailListError, MailListResult, Smtps};
#[derive(Debug)]
pub struct SmtpsBuilder;
impl SmtpsBuilder {
pub fn parse(smtps_uri: &str) -> MailListResult<Smtps> {
use url::Url;
let url =
Url::parse(smtps_uri).or(Err(MailListError::Smtps("Invalid smtps URI".to_string())))?;
if url.scheme() != "smtps" {
return Err(MailListError::Smtps("Invalid SMTPs scheme".to_string()));
};
let domain = url
.host_str()
.ok_or(MailListError::Smtps(
"Domain not found in SMTPs".to_string(),
))?
.to_string();
let domain = percent_decode_str(&domain)
.decode_utf8()
.or(Err(MailListError::Smtps(
"Domain is not valid UTF-8".to_string(),
)))?
.to_string();
let ehlo_name = url.path().trim_start_matches('/');
let ehlo_name = percent_decode_str(ehlo_name)
.decode_utf8()
.or(Err(MailListError::Smtps(
"EHLO name is not valid UTF-8".to_string(),
)))?
.to_string();
let ehlo_name: Option<std::borrow::Cow<'_, str>> = if ehlo_name.is_empty() {
None
} else {
Some(std::borrow::Cow::Owned(ehlo_name))
};
#[cfg(feature = "tokio-runtime")]
let transport = AsyncSmtpTransport::<lettre::Tokio1Executor>::from_url(smtps_uri)
.map_err(|error| MailListError::Mailer(error.to_string()))?;
let mailer = if let Some(hello_name) = ehlo_name.as_ref() {
transport.hello_name(lettre::transport::smtp::extension::ClientId::Domain(
hello_name.to_string(),
))
} else {
transport
};
let mailer = mailer.build();
let outcome = Smtps {
domain: domain.into(),
ehlo_name,
mailer,
};
Ok(outcome)
}
}
#[cfg(test)]
mod sanity_checks {
use crate::SmtpsBuilder;
#[test]
fn smtps_builder() {
let smtps_uri = "smtps://support@example.com:password@smtp.example.com:465/foo.example.com";
SmtpsBuilder::parse(smtps_uri).unwrap();
}
}