From 778849876272eea34fcd9085b0e2bbbce9319a35 Mon Sep 17 00:00:00 2001 From: Constantin Berhard Date: Wed, 19 Oct 2016 12:36:47 +0200 Subject: [PATCH 1/2] feat(email): support for a custom envelope The EmailBuilder now has a function to add a preconfigured envelope, overriding the auto generated one. fixes #84 --- src/email/mod.rs | 121 ++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 98 insertions(+), 23 deletions(-) diff --git a/src/email/mod.rs b/src/email/mod.rs index 0c73216..a55d3e5 100644 --- a/src/email/mod.rs +++ b/src/email/mod.rs @@ -247,10 +247,8 @@ pub struct EmailBuilder { reply_to_header: Vec
, /// The sender address for the mail header sender_header: Option, - /// The envelope recipients' addresses - to: Vec, - /// The envelope sender address - from: Option, + /// The envelope + envelope: Option, /// Date issued date_issued: bool, } @@ -264,6 +262,34 @@ pub struct Envelope { from: String, } +impl Envelope { + /// Constructs an envelope with no receivers and an empty sender + pub fn new() -> Self { + Envelope { + to: vec![], + from: String::new(), + } + } + /// Adds a receiver + pub fn to>(mut self, adr: S) -> Self { + self.add_to(adr); + self + } + /// Adds a receiver + pub fn add_to>(&mut self, adr: S) { + self.to.push(adr.into()); + } + /// Sets the sender + pub fn from>(mut self, adr: S) -> Self { + self.set_from(adr); + self + } + /// Sets the sender + pub fn set_from>(&mut self, adr: S) { + self.from = adr.into(); + } +} + /// Simple email representation #[derive(PartialEq,Eq,Clone,Debug)] pub struct Email { @@ -360,8 +386,7 @@ impl EmailBuilder { cc_header: vec![], reply_to_header: vec![], sender_header: None, - to: vec![], - from: None, + envelope: None, date_issued: false, } } @@ -397,7 +422,6 @@ impl EmailBuilder { /// Adds a `From` header and stores the sender address pub fn add_from(&mut self, address: A) { let mailbox = address.to_mailbox(); - self.from = Some(mailbox.address.clone()); self.from_header.push(Address::Mailbox(mailbox)); } @@ -410,7 +434,6 @@ impl EmailBuilder { /// Adds a `To` header and stores the recipient address pub fn add_to(&mut self, address: A) { let mailbox = address.to_mailbox(); - self.to.push(mailbox.address.clone()); self.to_header.push(Address::Mailbox(mailbox)); } @@ -423,7 +446,6 @@ impl EmailBuilder { /// Adds a `Cc` header and stores the recipient address pub fn add_cc(&mut self, address: A) { let mailbox = address.to_mailbox(); - self.to.push(mailbox.address.clone()); self.cc_header.push(Address::Mailbox(mailbox)); } @@ -448,7 +470,6 @@ impl EmailBuilder { /// Adds a `Sender` header pub fn set_sender(&mut self, address: A) { let mailbox = address.to_mailbox(); - self.from = Some(mailbox.address.clone()); self.sender_header = Some(mailbox); } @@ -551,14 +572,23 @@ impl EmailBuilder { self.add_child(alternate.build()); } + /// Sets the envelope for manual destination control + /// If this function is not called, the envelope will be calculated + /// from the "to" and "cc" addresses you set. + pub fn envelope(mut self, envelope: Envelope) -> EmailBuilder { + self.set_envelope(envelope); + self + } + + /// Sets the envelope for manual destination control + /// If this function is not called, the envelope will be calculated + /// from the "to" and "cc" addresses you set. + pub fn set_envelope(&mut self, envelope: Envelope) { + self.envelope = Some(envelope); + } + /// Builds the Email pub fn build(mut self) -> Result { - if self.from.is_none() { - return Err(Error::MissingFrom); - } - if self.to.is_empty() { - return Err(Error::MissingTo); - } // If there are multiple addresses in "From", the "Sender" is required. if self.from_header.len() >= 2 && self.sender_header.is_none() { // So, we must find something to put as Sender. @@ -575,13 +605,61 @@ impl EmailBuilder { assert!(self.sender_header.is_some()); } // Add the sender header, if any. - if let Some(v) = self.sender_header { + if let Some(ref v) = self.sender_header { self.message.add_header(("Sender", v.to_string().as_ref())); } + // Calculate the envelope + let envelope = match self.envelope { + Some(e) => e, + None => { + // we need to generate the envelope + let mut e = Envelope::new(); + // add all receivers in to_header and cc_header + for a in self.to_header.iter().chain(self.cc_header.iter()) { + match *a { + Address::Mailbox(ref m) => e.add_to(m.address.clone()), + Address::Group(_, ref ms) => { + for m in ms.iter() { + e.add_to(m.address.clone()); + } + } + } + } + if e.to.is_empty() { + return Err(Error::MissingTo); + } + e.set_from(match self.sender_header { + Some(x) => x.address.clone(), // if we have a sender_header, use it + None => { + debug_assert!(self.from_header.len()<=1); // else we'd have sender_header + match self.from_header.first() { + Some(a) => match *a { + // if we have a sender_header + Address::Mailbox(ref m) => m.address.clone(), // use it + Address::Group(_,ref ms) => match ms.first() { + // if it's an author group, use the first author + Some(m) => m.address.clone(), + // for an empty author group (the rarest of the rare cases) + None => String::new(), // empty envelope sender + }, + }, + // if we don't have a sender_header + None => String::new(), // empty envelope sender + } + } + }); + e + } + }; // Add the collected addresses as mailbox-list all at once. // The unwraps are fine because the conversions for Vec
never errs. - self.message.add_header(Header::new_with_value("To".into(), self.to_header).unwrap()); - self.message.add_header(Header::new_with_value("From".into(), self.from_header).unwrap()); + if !self.to_header.is_empty() { + self.message.add_header(Header::new_with_value("To".into(), self.to_header).unwrap()); + } + if !self.from_header.is_empty() { + self.message + .add_header(Header::new_with_value("From".into(), self.from_header).unwrap()); + } if !self.cc_header.is_empty() { self.message.add_header(Header::new_with_value("Cc".into(), self.cc_header).unwrap()); } @@ -607,10 +685,7 @@ impl EmailBuilder { Ok(Email { message: self.message.build(), - envelope: Envelope { - to: self.to, - from: self.from.unwrap(), - }, + envelope: envelope, message_id: message_id, }) } From b415edcfe0a8756ef5e932d7d8c4d94979ad5b9b Mon Sep 17 00:00:00 2001 From: Constantin Berhard Date: Sat, 22 Oct 2016 18:11:35 +0200 Subject: [PATCH 2/2] refactor(email): requested changes for PR #94 renamed variables meaningfully return more errors in email building modified some comments PR #94 --- src/email/mod.rs | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/src/email/mod.rs b/src/email/mod.rs index a55d3e5..b35847e 100644 --- a/src/email/mod.rs +++ b/src/email/mod.rs @@ -271,22 +271,22 @@ impl Envelope { } } /// Adds a receiver - pub fn to>(mut self, adr: S) -> Self { - self.add_to(adr); + pub fn to>(mut self, address: S) -> Self { + self.add_to(address); self } /// Adds a receiver - pub fn add_to>(&mut self, adr: S) { - self.to.push(adr.into()); + pub fn add_to>(&mut self, address: S) { + self.to.push(address.into()); } /// Sets the sender - pub fn from>(mut self, adr: S) -> Self { - self.set_from(adr); + pub fn from>(mut self, address: S) -> Self { + self.set_from(address); self } /// Sets the sender - pub fn set_from>(&mut self, adr: S) { - self.from = adr.into(); + pub fn set_from>(&mut self, address: S) { + self.from = address.into(); } } @@ -615,8 +615,8 @@ impl EmailBuilder { // we need to generate the envelope let mut e = Envelope::new(); // add all receivers in to_header and cc_header - for a in self.to_header.iter().chain(self.cc_header.iter()) { - match *a { + for receiver in self.to_header.iter().chain(self.cc_header.iter()) { + match *receiver { Address::Mailbox(ref m) => e.add_to(m.address.clone()), Address::Group(_, ref ms) => { for m in ms.iter() { @@ -631,20 +631,21 @@ impl EmailBuilder { e.set_from(match self.sender_header { Some(x) => x.address.clone(), // if we have a sender_header, use it None => { + // use a from header debug_assert!(self.from_header.len()<=1); // else we'd have sender_header match self.from_header.first() { Some(a) => match *a { - // if we have a sender_header - Address::Mailbox(ref m) => m.address.clone(), // use it - Address::Group(_,ref ms) => match ms.first() { + // if we have a from header + Address::Mailbox(ref mailbox) => mailbox.address.clone(), // use it + Address::Group(_,ref mailbox_list) => match mailbox_list.first() { // if it's an author group, use the first author - Some(m) => m.address.clone(), + Some(mailbox) => mailbox.address.clone(), // for an empty author group (the rarest of the rare cases) - None => String::new(), // empty envelope sender + None => return Err(Error::MissingFrom), // empty envelope sender }, }, - // if we don't have a sender_header - None => String::new(), // empty envelope sender + // if we don't have a from header + None => return Err(Error::MissingFrom), // empty envelope sender } } }); @@ -659,6 +660,8 @@ impl EmailBuilder { if !self.from_header.is_empty() { self.message .add_header(Header::new_with_value("From".into(), self.from_header).unwrap()); + } else { + return Err(Error::MissingFrom); } if !self.cc_header.is_empty() { self.message.add_header(Header::new_with_value("Cc".into(), self.cc_header).unwrap());