use std::collections::HashMap;
use serde::Serialize;
use crate::error::MessageBuilderError;
#[derive(Debug, Serialize)]
pub struct PostalMessage<'a> {
to: Vec<&'a str>,
from: &'a str,
cc: Option<Vec<&'a str>>,
bcc: Option<Vec<&'a str>>,
sender: Option<&'a str>,
reply_to: Option<&'a str>,
html_body: Option<&'a str>,
plain_body: Option<&'a str>,
subject: Option<&'a str>,
tag: Option<&'a str>,
headers: Option<HashMap<&'a str, &'a str>>,
}
#[derive(Debug)]
pub struct MessageBuilder<'a> {
to: Option<Vec<&'a str>>,
cc: Option<Vec<&'a str>>,
bcc: Option<Vec<&'a str>>,
sender: Option<&'a str>,
from: Option<&'a str>,
headers: Option<HashMap<&'a str, &'a str>>,
reply_to: Option<&'a str>,
html_body: Option<&'a str>,
plain_body: Option<&'a str>,
tag: Option<&'a str>,
subject: Option<&'a str>,
}
impl<'a> MessageBuilder<'a> {
pub fn new() -> Self {
Self {
to: None,
cc: None,
bcc: None,
sender: None,
from: None,
headers: None,
reply_to: None,
html_body: None,
plain_body: None,
tag: None,
subject: None,
}
}
pub fn add_to(mut self, address: &'a str) -> Result<Self, MessageBuilderError> {
match self.to.as_mut() {
Some(to) if to.len() < 50 => to.push(address),
Some(_) => Err(MessageBuilderError::TooManyToAddresses)?,
None => {
let to = vec![address];
self.to = Some(to);
}
}
Ok(self)
}
pub fn append_to(mut self, addresses: Vec<&'a str>) -> Result<Self, MessageBuilderError> {
if addresses.len() >= 50 {
Err(MessageBuilderError::TooManyToAddresses)?
}
match self.to.as_mut() {
Some(to) if to.len() + addresses.len() <= 50 => to.extend(addresses),
Some(_) => Err(MessageBuilderError::TooManyToAddresses)?,
None => {
self.to = Some(addresses);
}
}
Ok(self)
}
pub fn add_cc(mut self, address: &'a str) -> Result<Self, MessageBuilderError> {
match self.cc.as_mut() {
Some(cc) if cc.len() < 50 => cc.push(address),
Some(_) => Err(MessageBuilderError::TooManyCCAddresses)?,
None => {
let cc = vec![address];
self.cc = Some(cc);
}
}
Ok(self)
}
pub fn append_cc(mut self, addresses: Vec<&'a str>) -> Result<Self, MessageBuilderError> {
if addresses.len() >= 50 {
Err(MessageBuilderError::TooManyCCAddresses)?
}
match self.cc.as_mut() {
Some(cc) if cc.len() + addresses.len() <= 50 => cc.extend(addresses),
Some(_) => Err(MessageBuilderError::TooManyToAddresses)?,
None => {
self.cc = Some(addresses);
}
}
Ok(self)
}
pub fn add_bcc(mut self, address: &'a str) -> Result<Self, MessageBuilderError> {
match self.bcc.as_mut() {
Some(bcc) if bcc.len() < 50 => bcc.push(address),
Some(_) => Err(MessageBuilderError::TooManyBCCAddresses)?,
None => {
let bcc = vec![address];
self.bcc = Some(bcc);
}
}
Ok(self)
}
pub fn append_bcc(mut self, addresses: Vec<&'a str>) -> Result<Self, MessageBuilderError> {
if addresses.len() >= 50 {
Err(MessageBuilderError::TooManyBCCAddresses)?
}
match self.bcc.as_mut() {
Some(bcc) if bcc.len() + addresses.len() <= 50 => bcc.extend(addresses),
Some(_) => Err(MessageBuilderError::TooManyToAddresses)?,
None => {
self.bcc = Some(addresses);
}
}
Ok(self)
}
pub fn add_header(mut self, header: (&'a str, &'a str)) -> Result<Self, MessageBuilderError> {
let (header_name, header_val) = header;
match self.headers.as_mut() {
Some(headers) => {
if headers.insert(header_name, header_val).is_some() {
Err(MessageBuilderError::HeaderExists)?
};
}
None => {
let mut headers: HashMap<&'a str, &'a str> = HashMap::new();
headers.insert(header_name, header_val);
self.headers = Some(headers);
}
}
Ok(self)
}
pub fn set_from(mut self, address: &'a str) -> Self {
self.from = Some(address);
self
}
pub fn set_sender(mut self, address: &'a str) -> Self {
self.sender = Some(address);
self
}
pub fn set_reply_to(mut self, address: &'a str) -> Self {
self.sender = Some(address);
self
}
pub fn set_html_body(mut self, content: &'a str) -> Self {
self.html_body = Some(content);
self
}
pub fn set_plain_body(mut self, content: &'a str) -> Self {
self.plain_body = Some(content);
self
}
pub fn set_tag(mut self, tag: &'a str) -> Self {
self.tag = Some(tag);
self
}
pub fn set_subject(mut self, subject: &'a str) -> Self {
self.subject = Some(subject);
self
}
pub fn build(self) -> Result<PostalMessage<'a>, MessageBuilderError> {
let Some(to) = self.to.clone() else {
return Err(MessageBuilderError::NoRecipients)
};
if to.is_empty() {
return Err(MessageBuilderError::NoRecipients);
}
let Some(from) = self.from else {
return Err(MessageBuilderError::FromAddressMissing);
};
if self.plain_body.is_none() && self.plain_body.is_none() {
return Err(MessageBuilderError::NoContent);
}
Ok(PostalMessage {
to,
from,
subject: self.subject,
sender: self.sender,
cc: self.cc,
bcc: self.bcc,
reply_to: self.reply_to,
html_body: self.html_body,
plain_body: self.plain_body,
tag: self.tag,
headers: self.headers,
})
}
}
impl<'a> Default for MessageBuilder<'a> {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod test {
use super::MessageBuilder;
use crate::error::MessageBuilderError;
#[test]
fn minimal_message_ok() {
MessageBuilder::new()
.add_to("mail@example.com")
.unwrap()
.set_from("mail@postalserver.com")
.set_plain_body("This is a message")
.build()
.unwrap();
}
fn expect_err<T>(res: Result<T, MessageBuilderError>, expect_err: MessageBuilderError) {
match res {
Err(err) => assert_eq!(err, expect_err),
Ok(_) => panic!("This should not build"),
}
}
#[test]
fn minimal_message_missing_to() {
expect_err(
MessageBuilder::new()
.set_from("mail@postalserver.com")
.set_plain_body("This is a message")
.build(),
MessageBuilderError::NoRecipients,
);
}
#[test]
fn minimal_message_missing_content() {
expect_err(
MessageBuilder::new()
.add_to("mail@example.com")
.unwrap()
.set_from("mail@postalserver.com")
.build(),
MessageBuilderError::NoContent,
);
}
#[test]
fn minimal_message_missing_from() {
expect_err(
MessageBuilder::new()
.add_to("mail@example.com")
.unwrap()
.set_plain_body("This is a Message")
.build(),
MessageBuilderError::FromAddressMissing,
)
}
#[test]
fn minimal_message_too_many_to_addresses() {
let mut mb = MessageBuilder::new();
for _ in 0..50 {
mb = mb.add_to("test@example.com").unwrap()
}
expect_err(
mb.add_to("overflow_to@example.com"),
MessageBuilderError::TooManyToAddresses,
);
}
#[test]
fn minimal_message_too_many_cc_addresses() {
let mut mb = MessageBuilder::new();
for _ in 0..50 {
mb = mb.add_cc("test_cc@example.com").unwrap()
}
expect_err(
mb.add_cc("overflow_cc@example.com"),
MessageBuilderError::TooManyCCAddresses,
);
}
#[test]
fn minimal_message_too_many_bcc_addresses() {
let mut mb = MessageBuilder::new();
for _ in 0..50 {
mb = mb.add_bcc("test_cc@example.com").unwrap()
}
expect_err(
mb.add_bcc("overflow_cc@example.com"),
MessageBuilderError::TooManyBCCAddresses,
);
}
#[test]
fn minimal_message_header_exists() {
let header = ("Totally-Real-Header", "this is a value");
let mb = MessageBuilder::new().add_header(header).unwrap();
expect_err(mb.add_header(header), MessageBuilderError::HeaderExists)
}
}