diff --git a/Cargo.toml b/Cargo.toml index 62b369b..3291d14 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,7 @@ rust-crypto = "0.2" bufstream = "0.1" [dependencies.email] -git = "https://github.com/amousset/rust-email.git" +path = "../rust-email" [dev-dependencies] env_logger = "*" diff --git a/examples/client.rs b/examples/client.rs index db22a5c..62200e4 100644 --- a/examples/client.rs +++ b/examples/client.rs @@ -18,13 +18,12 @@ use smtp::mailer::EmailBuilder; fn main() { env_logger::init().unwrap(); - let mut email_builder = EmailBuilder::new(); - - email_builder = email_builder.to("user@localhost"); - let email = email_builder.from("user@localhost") - .body("Hello World!") - .subject("Hello") - .build(); + let email = EmailBuilder::new() + .to("user@localhost") + .from("user@localhost") + .body("Hello World!") + .subject("Hello") + .build(); let mut sender: Sender = SenderBuilder::localhost().hello_name("localhost") .enable_connection_reuse(true).build(); diff --git a/src/error.rs b/src/error.rs index 9b84e5b..80d75bc 100644 --- a/src/error.rs +++ b/src/error.rs @@ -20,15 +20,15 @@ use self::SmtpError::*; /// An enum of all error kinds. #[derive(Debug)] pub enum SmtpError { - /// Transient error, 4xx reply code + /// Transient SMTP error, 4xx reply code /// /// [RFC 5321, section 4.2.1](https://tools.ietf.org/html/rfc5321#section-4.2.1) TransientError(Response), - /// Permanent error, 5xx reply code + /// Permanent SMTP error, 5xx reply code /// /// [RFC 5321, section 4.2.1](https://tools.ietf.org/html/rfc5321#section-4.2.1) PermanentError(Response), - /// TODO + /// Internal client error ClientError(String), /// IO error IoError(io::Error), diff --git a/src/mailer/mod.rs b/src/mailer/mod.rs index 3fb7899..82e8556 100644 --- a/src/mailer/mod.rs +++ b/src/mailer/mod.rs @@ -9,7 +9,9 @@ //! Simple email (very incomplete) -use email::{MimeMessage, Header, Address}; +use std::fmt::{Display, Formatter, Result}; + +use email::{MimeMessage, Header, Mailbox}; use time::{now, Tm}; use uuid::Uuid; @@ -17,7 +19,7 @@ use sendable_email::SendableEmail; /// Converts an adress or an address with an alias to an `Address` pub trait ToHeader { - /// Converts to an `Address` struct + /// Converts to an `Header` struct fn to_header(&self) -> Header; } @@ -34,32 +36,32 @@ impl<'a> ToHeader for (&'a str, &'a str) { } } -/// Converts an adress or an address with an alias to an `Address` -pub trait ToAddress { - /// Converts to an `Address` struct - fn to_address(&self) -> Address; +/// Converts an adress or an address with an alias to an `Mailbox` +pub trait ToMailbox { + /// Converts to an `Mailbox` struct + fn to_mailbox(&self) -> Mailbox; } -impl ToAddress for Address { - fn to_address(&self) -> Address { +impl ToMailbox for Mailbox { + fn to_mailbox(&self) -> Mailbox { (*self).clone() } } -impl<'a> ToAddress for &'a str { - fn to_address(&self) -> Address { - Address::new_mailbox(self.to_string()) +impl<'a> ToMailbox for &'a str { + fn to_mailbox(&self) -> Mailbox { + Mailbox::new(self.to_string()) } } -impl<'a> ToAddress for (&'a str, &'a str) { - fn to_address(&self) -> Address { +impl<'a> ToMailbox for (&'a str, &'a str) { + fn to_mailbox(&self) -> Mailbox { let (address, alias) = *self; - Address::new_mailbox_with_name(address.to_string(), alias.to_string()) + Mailbox::new_with_name(alias.to_string(), address.to_string()) } } -/// TODO +/// Builds an `Email` structure #[derive(PartialEq,Eq,Clone,Debug)] pub struct EmailBuilder { /// Email content @@ -81,10 +83,11 @@ pub struct Email { message_id: Uuid, } -impl Email { - /// Displays the formatted email content - pub fn as_string(&self) -> String { - self.message.as_string() +impl Display for Email { + fn fmt(&self, f: &mut Formatter) -> Result { + write!(f, "{}", + self.message.as_string() + ) } } @@ -129,36 +132,41 @@ impl EmailBuilder { } /// Adds a `From` header and store the sender address - pub fn from(mut self, address: A) -> EmailBuilder { - self.content.from = Some(address.to_address().get_address().unwrap()); - self.insert_header(("From", address.to_address().to_string().as_ref())); + pub fn from(mut self, address: A) -> EmailBuilder { + let mailbox = address.to_mailbox(); + self.insert_header(("From", mailbox.to_string().as_ref())); + self.content.from = Some(mailbox.address); self } /// Adds a `To` header and store the recipient address - pub fn to(mut self, address: A) -> EmailBuilder { - self.content.to.push(address.to_address().get_address().unwrap()); - self.insert_header(("To", address.to_address().to_string().as_ref())); + pub fn to(mut self, address: A) -> EmailBuilder { + let mailbox = address.to_mailbox(); + self.insert_header(("To", mailbox.to_string().as_ref())); + self.content.to.push(mailbox.address); self } /// Adds a `Cc` header and store the recipient address - pub fn cc(mut self, address: A) -> EmailBuilder { - self.content.to.push(address.to_address().get_address().unwrap()); - self.insert_header(("Cc", address.to_address().to_string().as_ref())); + pub fn cc(mut self, address: A) -> EmailBuilder { + let mailbox = address.to_mailbox(); + self.insert_header(("Cc", mailbox.to_string().as_ref())); + self.content.to.push(mailbox.address); self } /// Adds a `Reply-To` header - pub fn reply_to(mut self, address: A) -> EmailBuilder { - self.insert_header(("Reply-To", address.to_address().to_string().as_ref())); + pub fn reply_to(mut self, address: A) -> EmailBuilder { + let mailbox = address.to_mailbox(); + self.insert_header(("Reply-To", mailbox.to_string().as_ref())); self } /// Adds a `Sender` header - pub fn sender(mut self, address: A) -> EmailBuilder { - self.content.from = Some(address.to_address().get_address().unwrap()); - self.insert_header(("Sender", address.to_address().to_string().as_ref())); + pub fn sender(mut self, address: A) -> EmailBuilder { + let mailbox = address.to_mailbox(); + self.insert_header(("Sender", mailbox.to_string().as_ref())); + self.content.from = Some(mailbox.address); self } @@ -203,10 +211,105 @@ impl SendableEmail for Email { } fn message(&self) -> String { - format! ("{}", self.as_string()) + format!("{}", self) } fn message_id(&self) -> String { format!("{}", self.message_id) } } + +#[cfg(test)] +mod test { + use time::now; + + use uuid::Uuid; + use email::{MimeMessage, Header}; + + use sendable_email::SendableEmail; + use super::{EmailBuilder, Email}; + + #[test] + fn test_email_display() { + let current_message = Uuid::new_v4(); + + let mut email = Email { + message: MimeMessage::new_blank_message(), + to: vec![], + from: None, + message_id: current_message, + }; + + email.message.headers.insert( + Header::new_with_value("Message-ID".to_string(), + format!("<{}@rust-smtp>", current_message) + ).unwrap() + ); + + email.message.headers.insert( + Header::new_with_value("To".to_string(), "to@example.com".to_string()).unwrap() + ); + + email.message.body = "body".to_string(); + + assert_eq!( + format!("{}", email), + format!("Message-ID: <{}@rust-smtp>\r\nTo: to@example.com\r\n\r\nbody\r\n", current_message) + ); + assert_eq!(current_message.to_string(), email.message_id()); + } + + #[test] + fn test_email_builder() { + let email_builder = EmailBuilder::new(); + let date_now = now(); + + let email = email_builder.to("user@localhost") + .from("user@localhost") + .cc(("cc@localhost", "Alias")) + .reply_to("reply@localhost") + .sender("sender@localhost") + .body("Hello World!") + .date(&date_now) + .subject("Hello") + .add_header(("X-test", "value")) + .build(); + + assert_eq!( + format!("{}", email), + format!("Message-ID: <{}@rust-smtp>\r\nTo: \r\nFrom: \r\nCc: \"Alias\" \r\nReply-To: \r\nSender: \r\nDate: {}\r\nSubject: Hello\r\nX-test: value\r\n\r\nHello World!\r\n", + email.message_id(), date_now.rfc822()) + ); + } + + #[test] + fn test_email_sendable() { + let email_builder = EmailBuilder::new(); + let date_now = now(); + + let email = email_builder.to("user@localhost") + .from("user@localhost") + .cc(("cc@localhost", "Alias")) + .reply_to("reply@localhost") + .sender("sender@localhost") + .body("Hello World!") + .date(&date_now) + .subject("Hello") + .add_header(("X-test", "value")) + .build(); + + assert_eq!( + email.from_address(), + "sender@localhost".to_string() + ); + assert_eq!( + email.to_addresses(), + vec!["user@localhost".to_string(), "cc@localhost".to_string()] + ); + assert_eq!( + email.message(), + format!("{}", email) + ); + } + +}