use error_stack::Report;
use serde::Serialize;
use self::services::{EmailError, EmailSender};
pub mod services;
pub mod templates;
#[derive(Debug, Default)]
pub struct Email {
pub from: String,
pub to: Vec<String>,
pub reply_to: Option<String>,
pub cc: Vec<String>,
pub bcc: Vec<String>,
pub subject: String,
pub text: String,
pub html: String,
pub attachments: Vec<EmailAttachment>,
pub tags: Vec<String>,
}
#[cfg_attr(test, derive(PartialEq, Eq))]
#[derive(Serialize)]
pub struct EmailAttachment {
pub filename: String,
pub content: Vec<u8>,
}
impl std::fmt::Debug for EmailAttachment {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("EmailAttachment")
.field("filename", &self.filename)
.field("content_length", &self.content.len())
.finish()
}
}
pub struct EmailBuilder {
email: Email,
}
impl EmailBuilder {
pub fn new(to: impl ToString, subject: impl ToString) -> Self {
Self {
email: Email {
to: vec![to.to_string()],
subject: subject.to_string(),
..Default::default()
},
}
}
pub fn new_to_multiple(to: Vec<String>, subject: impl ToString) -> Self {
Self {
email: Email {
to,
subject: subject.to_string(),
..Default::default()
},
}
}
pub fn from(mut self, from: impl ToString) -> Self {
self.email.from = from.to_string();
self
}
pub fn to(mut self, to: impl ToString) -> Self {
self.email.to.push(to.to_string());
self
}
pub fn to_vec(mut self, to: Vec<String>) -> Self {
self.email.to = to;
self
}
pub fn reply_to(mut self, reply_to: impl ToString) -> Self {
self.email.reply_to = Some(reply_to.to_string());
self
}
pub fn cc(mut self, cc: impl ToString) -> Self {
self.email.cc.push(cc.to_string());
self
}
pub fn cc_vec(mut self, cc: Vec<String>) -> Self {
self.email.cc = cc;
self
}
pub fn bcc(mut self, bcc: impl ToString) -> Self {
self.email.bcc.push(bcc.to_string());
self
}
pub fn bcc_vec(mut self, bcc: Vec<String>) -> Self {
self.email.bcc = bcc;
self
}
pub fn subject(mut self, subject: impl ToString) -> Self {
self.email.subject = subject.to_string();
self
}
pub fn html(mut self, html: impl ToString) -> Self {
self.email.html = html.to_string();
self
}
pub fn text(mut self, text: impl ToString) -> Self {
self.email.text = text.to_string();
self
}
pub fn attachment(mut self, attachment: EmailAttachment) -> Self {
self.email.attachments.push(attachment);
self
}
pub fn attachments(mut self, attachments: Vec<EmailAttachment>) -> Self {
self.email.attachments = attachments;
self
}
pub fn tag(mut self, tag: impl ToString) -> Self {
self.email.tags.push(tag.to_string());
self
}
pub fn tags(mut self, tags: Vec<String>) -> Self {
self.email.tags = tags;
self
}
pub async fn send(self, sender: &EmailSender) -> Result<(), Report<EmailError>> {
sender.send(self.email).await
}
pub fn build(self) -> Email {
self.email
}
}
#[cfg(test)]
mod test {
#[test]
fn email_builder() {
let email = super::EmailBuilder::new("someone@example.com", "Hello!")
.from("abc@def.com")
.to("someone@else.com")
.cc("manager@example.com")
.bcc_vec(vec!["crm@example.com".to_string()])
.html("<b>Hello</b>!")
.attachment(super::EmailAttachment {
filename: "hello.txt".to_string(),
content: vec![1, 2, 3],
})
.build();
assert_eq!(email.to, vec!["someone@example.com", "someone@else.com"]);
assert_eq!(email.cc, vec!["manager@example.com"]);
assert_eq!(email.bcc, vec!["crm@example.com"]);
assert_eq!(email.subject, "Hello!");
assert_eq!(email.html, "<b>Hello</b>!");
assert_eq!(email.text, "");
assert_eq!(
email.attachments,
vec![super::EmailAttachment {
filename: "hello.txt".to_string(),
content: vec![1, 2, 3],
}]
)
}
}