From c10fe3db846b5176b6b47e51f35b510a2d7ca348 Mon Sep 17 00:00:00 2001 From: Alexis Mousset Date: Thu, 31 May 2018 13:08:02 +0200 Subject: [PATCH] feat(all): Start using the failure crate for errors --- lettre/Cargo.toml | 2 ++ lettre/src/error.rs | 18 ++++++++++++++ lettre/src/file/error.rs | 47 +++++++++--------------------------- lettre/src/lib.rs | 46 +++++++---------------------------- lettre/src/sendmail/error.rs | 40 +++++++----------------------- lettre/src/sendmail/mod.rs | 23 ++++++++---------- lettre_email/Cargo.toml | 2 ++ lettre_email/src/error.rs | 45 +++++++++++++--------------------- lettre_email/src/lib.rs | 34 +++++++++++++++++--------- 9 files changed, 101 insertions(+), 156 deletions(-) create mode 100644 lettre/src/error.rs diff --git a/lettre/Cargo.toml b/lettre/Cargo.toml index 503a261..b1c3f65 100644 --- a/lettre/Cargo.toml +++ b/lettre/Cargo.toml @@ -28,6 +28,8 @@ hostname = { version = "^0.1", optional = true } serde = { version = "^1.0", optional = true } serde_json = { version = "^1.0", optional = true } serde_derive = { version = "^1.0", optional = true } +failure = "^0.1" +failure_derive = "^0.1" [dev-dependencies] env_logger = "^0.5" diff --git a/lettre/src/error.rs b/lettre/src/error.rs new file mode 100644 index 0000000..d33b98a --- /dev/null +++ b/lettre/src/error.rs @@ -0,0 +1,18 @@ +use failure; + +/// Error type for email content +#[derive(Fail, Debug, Clone, Copy)] +pub enum Error { + /// Missing from in envelope + #[fail(display = "missing source address, invalid envelope")] + MissingFrom, + /// Missing to in envelope + #[fail(display = "missing destination address, invalid envelope")] + MissingTo, + /// Invalid email + #[fail(display = "invalid email address")] + InvalidEmailAddress, +} + +/// Email result type +pub type EmailResult = Result; diff --git a/lettre/src/file/error.rs b/lettre/src/file/error.rs index 62d0c38..e24c540 100644 --- a/lettre/src/file/error.rs +++ b/lettre/src/file/error.rs @@ -1,63 +1,40 @@ //! Error and result type for file transport -use self::Error::*; +use failure; use serde_json; -use std::error::Error as StdError; -use std::fmt::{self, Display, Formatter}; use std::io; /// An enum of all error kinds. -#[derive(Debug)] +#[derive(Fail, Debug)] pub enum Error { /// Internal client error - Client(&'static str), + #[fail(display = "Internal client error: {}", error)] + Client { error: &'static str }, /// IO error - Io(io::Error), + #[fail(display = "IO error: {}", error)] + Io { error: io::Error }, /// JSON serialization error - JsonSerialization(serde_json::Error), -} - -impl Display for Error { - fn fmt(&self, fmt: &mut Formatter) -> Result<(), fmt::Error> { - fmt.write_str(self.description()) - } -} - -impl StdError for Error { - fn description(&self) -> &str { - match *self { - Client(err) => err, - Io(ref err) => err.description(), - JsonSerialization(ref err) => err.description(), - } - } - - fn cause(&self) -> Option<&StdError> { - match *self { - Io(ref err) => Some(&*err), - JsonSerialization(ref err) => Some(&*err), - _ => None, - } - } + #[fail(display = "JSON serialization error: {}", error)] + JsonSerialization { error: serde_json::Error }, } impl From for Error { fn from(err: io::Error) -> Error { - Io(err) + Error::Io { error: err } } } impl From for Error { fn from(err: serde_json::Error) -> Error { - JsonSerialization(err) + Error::JsonSerialization { error: err } } } impl From<&'static str> for Error { fn from(string: &'static str) -> Error { - Client(string) + Error::Client { error: string } } } /// SMTP result type -pub type FileResult = Result<(), Error>; +pub type FileResult = Result<(), failure::Error>; diff --git a/lettre/src/lib.rs b/lettre/src/lib.rs index de5abc6..d3053f4 100644 --- a/lettre/src/lib.rs +++ b/lettre/src/lib.rs @@ -6,7 +6,7 @@ #![doc(html_root_url = "https://docs.rs/lettre/0.9.0")] #![deny( missing_copy_implementations, trivial_casts, trivial_numeric_casts, unsafe_code, - unstable_features, unused_import_braces, unused_qualifications + unstable_features, unused_import_braces )] #[cfg(feature = "smtp-transport")] extern crate base64; @@ -26,9 +26,13 @@ extern crate serde; #[cfg(feature = "serde-impls")] #[macro_use] extern crate serde_derive; +extern crate failure; #[cfg(feature = "file-transport")] extern crate serde_json; +#[macro_use] +extern crate failure_derive; +pub mod error; #[cfg(feature = "file-transport")] pub mod file; #[cfg(feature = "sendmail-transport")] @@ -37,6 +41,9 @@ pub mod sendmail; pub mod smtp; pub mod stub; +use error::EmailResult; +use error::Error as EmailError; +use failure::Error; #[cfg(feature = "file-transport")] pub use file::FileTransport; #[cfg(feature = "sendmail-transport")] @@ -45,47 +52,12 @@ pub use sendmail::SendmailTransport; pub use smtp::client::net::ClientTlsParameters; #[cfg(feature = "smtp-transport")] pub use smtp::{ClientSecurity, SmtpClient, SmtpTransport}; -use std::error::Error as StdError; use std::fmt::{self, Display, Formatter}; use std::io; use std::io::Cursor; use std::io::Read; use std::str::FromStr; -/// Error type for email content -#[derive(Debug, Clone, Copy)] -pub enum Error { - /// Missing from in envelope - MissingFrom, - /// Missing to in envelope - MissingTo, - /// Invalid email - InvalidEmailAddress, -} - -impl StdError for Error { - fn description(&self) -> &str { - match *self { - Error::MissingFrom => "missing source address, invalid envelope", - Error::MissingTo => "missing destination address, invalid envelope", - Error::InvalidEmailAddress => "invalid email address", - } - } - - fn cause(&self) -> Option<&StdError> { - None - } -} - -impl Display for Error { - fn fmt(&self, fmt: &mut Formatter) -> Result<(), fmt::Error> { - fmt.write_str(self.description()) - } -} - -/// Email result type -pub type EmailResult = Result; - /// Email address #[derive(PartialEq, Eq, Clone, Debug)] #[cfg_attr(feature = "serde-impls", derive(Serialize, Deserialize))] @@ -131,7 +103,7 @@ impl Envelope { /// Creates a new envelope, which may fail if `to` is empty. pub fn new(from: Option, to: Vec) -> EmailResult { if to.is_empty() { - return Err(Error::MissingTo); + Err(EmailError::MissingTo)?; } Ok(Envelope { forward_path: to, diff --git a/lettre/src/sendmail/error.rs b/lettre/src/sendmail/error.rs index 6b9a4aa..5a5e64c 100644 --- a/lettre/src/sendmail/error.rs +++ b/lettre/src/sendmail/error.rs @@ -1,52 +1,30 @@ //! Error and result type for sendmail transport -use self::Error::*; -use std::error::Error as StdError; -use std::fmt::{self, Display, Formatter}; +use failure; use std::io; /// An enum of all error kinds. -#[derive(Debug)] +#[derive(Fail, Debug)] pub enum Error { /// Internal client error - Client(&'static str), + #[fail(display = "Internal client error: {}", error)] + Client { error: &'static str }, /// IO error - Io(io::Error), -} - -impl Display for Error { - fn fmt(&self, fmt: &mut Formatter) -> Result<(), fmt::Error> { - fmt.write_str(self.description()) - } -} - -impl StdError for Error { - fn description(&self) -> &str { - match *self { - Client(err) => err, - Io(ref err) => err.description(), - } - } - - fn cause(&self) -> Option<&StdError> { - match *self { - Io(ref err) => Some(&*err), - _ => None, - } - } + #[fail(display = "IO error: {}", error)] + Io { error: io::Error }, } impl From for Error { fn from(err: io::Error) -> Error { - Io(err) + Error::Io { error: err } } } impl From<&'static str> for Error { fn from(string: &'static str) -> Error { - Client(string) + Error::Client { error: string } } } /// sendmail result type -pub type SendmailResult = Result<(), Error>; +pub type SendmailResult = Result<(), failure::Error>; diff --git a/lettre/src/sendmail/mod.rs b/lettre/src/sendmail/mod.rs index b33dabb..70d0685 100644 --- a/lettre/src/sendmail/mod.rs +++ b/lettre/src/sendmail/mod.rs @@ -58,26 +58,23 @@ impl<'a> Transport<'a> for SendmailTransport { let mut message_content = String::new(); let _ = email.message().read_to_string(&mut message_content); - match process + process .stdin .as_mut() .unwrap() - .write_all(message_content.as_bytes()) - { - Ok(_) => (), - Err(error) => return Err(From::from(error)), - } + .write_all(message_content.as_bytes())?; info!("Wrote {} message to stdin", message_id); - if let Ok(output) = process.wait_with_output() { - if output.status.success() { - Ok(()) - } else { - Err(From::from("The message could not be sent")) - } + let output = process.wait_with_output()?; + + if output.status.success() { + Ok(()) } else { - Err(From::from("The sendmail process stopped")) + // TODO display stderr + Err(error::Error::Client { + error: "The message could not be sent", + })? } } } diff --git a/lettre_email/Cargo.toml b/lettre_email/Cargo.toml index 22585b2..d3a54ad 100644 --- a/lettre_email/Cargo.toml +++ b/lettre_email/Cargo.toml @@ -29,3 +29,5 @@ time = "^0.1" uuid = { version = "^0.6", features = ["v4"] } lettre = { version = "^0.9", path = "../lettre", default-features = false } base64 = "^0.9" +failure = "^0.1" +failure_derive = "^0.1" \ No newline at end of file diff --git a/lettre_email/src/error.rs b/lettre_email/src/error.rs index 3b3c6d9..84508f8 100644 --- a/lettre_email/src/error.rs +++ b/lettre_email/src/error.rs @@ -1,47 +1,36 @@ //! Error and result type for emails -use self::Error::*; -use std::error::Error as StdError; -use std::fmt::{self, Display, Formatter}; +use lettre; use std::io; -use lettre; - /// An enum of all error kinds. -#[derive(Debug)] +#[derive(Debug, Fail)] pub enum Error { /// Envelope error - Email(lettre::Error), + #[fail(display = "lettre error: {}", error)] + Envelope { + /// inner error + error: lettre::error::Error, + }, /// Unparseable filename for attachment + #[fail(display = "the attachment filename could not be parsed")] CannotParseFilename, /// IO error - Io(io::Error), -} - -impl Display for Error { - fn fmt(&self, fmt: &mut Formatter) -> Result<(), fmt::Error> { - fmt.write_str(self.description()) - } -} - -impl StdError for Error { - fn description(&self) -> &str { - match *self { - Email(ref err) => err.description(), - CannotParseFilename => "the attachment filename could not be parsed", - Io(ref err) => err.description(), - } - } + #[fail(display = "IO error: {}", error)] + Io { + /// inner error + error: io::Error, + }, } impl From for Error { fn from(err: io::Error) -> Error { - Io(err) + Error::Io { error: err } } } -impl From for Error { - fn from(err: lettre::Error) -> Error { - Email(err) +impl From for Error { + fn from(err: lettre::error::Error) -> Error { + Error::Envelope { error: err } } } diff --git a/lettre_email/src/lib.rs b/lettre_email/src/lib.rs index 6229765..6aab23b 100644 --- a/lettre_email/src/lib.rs +++ b/lettre_email/src/lib.rs @@ -4,10 +4,13 @@ #![doc(html_root_url = "https://docs.rs/lettre_email/0.9.0")] #![deny( missing_docs, missing_debug_implementations, missing_copy_implementations, trivial_casts, - trivial_numeric_casts, unsafe_code, unstable_features, unused_import_braces, - unused_qualifications + trivial_numeric_casts, unsafe_code, unstable_features, unused_import_braces )] +extern crate failure; +#[macro_use] +extern crate failure_derive; + extern crate base64; extern crate email as email_format; extern crate lettre; @@ -18,8 +21,9 @@ extern crate uuid; pub mod error; pub use email_format::{Address, Header, Mailbox, MimeMessage, MimeMultipartType}; -use error::Error; -use lettre::{EmailAddress, Envelope, Error as EmailError, SendableEmail}; +use error::Error as EmailError; +use failure::Error; +use lettre::{error::Error as LettreError, EmailAddress, Envelope, SendableEmail}; use mime::Mime; use std::fs; use std::path::Path; @@ -231,7 +235,7 @@ impl EmailBuilder { fs::read(path)?.as_slice(), filename.unwrap_or(path.file_name() .and_then(|x| x.to_str()) - .ok_or(Error::CannotParseFilename)?), + .ok_or(EmailError::CannotParseFilename)?), content_type, ) } @@ -369,26 +373,30 @@ impl EmailBuilder { } } let from = Some(EmailAddress::from_str(&match self.sender { - Some(x) => x.address.clone(), // if we have a sender_header, use it + Some(x) => Ok(x.address.clone()), // if we have a sender_header, use it None => { // use a from header debug_assert!(self.from.len() <= 1); // else we'd have sender_header match self.from.first() { Some(a) => match *a { // if we have a from header - Address::Mailbox(ref mailbox) => mailbox.address.clone(), // use it + Address::Mailbox(ref mailbox) => Ok(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(mailbox) => mailbox.address.clone(), + Some(mailbox) => Ok(mailbox.address.clone()), // for an empty author group (the rarest of the rare cases) - None => return Err(Error::Email(EmailError::MissingFrom)), // empty envelope sender + None => Err(EmailError::Envelope { + error: LettreError::MissingFrom, + }), // empty envelope sender }, }, // if we don't have a from header - None => return Err(Error::Email(EmailError::MissingFrom)), // empty envelope sender + None => Err(EmailError::Envelope { + error: LettreError::MissingFrom, + }), // empty envelope sender } } - })?); + }?)?); Envelope::new(from, to)? } }; @@ -402,7 +410,9 @@ impl EmailBuilder { self.message = self.message .header(Header::new_with_value("From".into(), self.from).unwrap()); } else { - return Err(Error::Email(EmailError::MissingFrom)); + Err(EmailError::Envelope { + error: LettreError::MissingFrom, + })?; } if !self.cc.is_empty() { self.message = self.message