feat(all): Start using the failure crate for errors

This commit is contained in:
Alexis Mousset
2018-05-31 13:08:02 +02:00
parent 9d14630552
commit c10fe3db84
9 changed files with 101 additions and 156 deletions

View File

@@ -28,6 +28,8 @@ hostname = { version = "^0.1", optional = true }
serde = { version = "^1.0", optional = true } serde = { version = "^1.0", optional = true }
serde_json = { version = "^1.0", optional = true } serde_json = { version = "^1.0", optional = true }
serde_derive = { version = "^1.0", optional = true } serde_derive = { version = "^1.0", optional = true }
failure = "^0.1"
failure_derive = "^0.1"
[dev-dependencies] [dev-dependencies]
env_logger = "^0.5" env_logger = "^0.5"

18
lettre/src/error.rs Normal file
View File

@@ -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<T> = Result<T, failure::Error>;

View File

@@ -1,63 +1,40 @@
//! Error and result type for file transport //! Error and result type for file transport
use self::Error::*; use failure;
use serde_json; use serde_json;
use std::error::Error as StdError;
use std::fmt::{self, Display, Formatter};
use std::io; use std::io;
/// An enum of all error kinds. /// An enum of all error kinds.
#[derive(Debug)] #[derive(Fail, Debug)]
pub enum Error { pub enum Error {
/// Internal client error /// Internal client error
Client(&'static str), #[fail(display = "Internal client error: {}", error)]
Client { error: &'static str },
/// IO error /// IO error
Io(io::Error), #[fail(display = "IO error: {}", error)]
Io { error: io::Error },
/// JSON serialization error /// JSON serialization error
JsonSerialization(serde_json::Error), #[fail(display = "JSON serialization error: {}", error)]
} JsonSerialization { error: 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,
}
}
} }
impl From<io::Error> for Error { impl From<io::Error> for Error {
fn from(err: io::Error) -> Error { fn from(err: io::Error) -> Error {
Io(err) Error::Io { error: err }
} }
} }
impl From<serde_json::Error> for Error { impl From<serde_json::Error> for Error {
fn from(err: serde_json::Error) -> Error { fn from(err: serde_json::Error) -> Error {
JsonSerialization(err) Error::JsonSerialization { error: err }
} }
} }
impl From<&'static str> for Error { impl From<&'static str> for Error {
fn from(string: &'static str) -> Error { fn from(string: &'static str) -> Error {
Client(string) Error::Client { error: string }
} }
} }
/// SMTP result type /// SMTP result type
pub type FileResult = Result<(), Error>; pub type FileResult = Result<(), failure::Error>;

View File

@@ -6,7 +6,7 @@
#![doc(html_root_url = "https://docs.rs/lettre/0.9.0")] #![doc(html_root_url = "https://docs.rs/lettre/0.9.0")]
#![deny( #![deny(
missing_copy_implementations, trivial_casts, trivial_numeric_casts, unsafe_code, 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")] #[cfg(feature = "smtp-transport")]
extern crate base64; extern crate base64;
@@ -26,9 +26,13 @@ extern crate serde;
#[cfg(feature = "serde-impls")] #[cfg(feature = "serde-impls")]
#[macro_use] #[macro_use]
extern crate serde_derive; extern crate serde_derive;
extern crate failure;
#[cfg(feature = "file-transport")] #[cfg(feature = "file-transport")]
extern crate serde_json; extern crate serde_json;
#[macro_use]
extern crate failure_derive;
pub mod error;
#[cfg(feature = "file-transport")] #[cfg(feature = "file-transport")]
pub mod file; pub mod file;
#[cfg(feature = "sendmail-transport")] #[cfg(feature = "sendmail-transport")]
@@ -37,6 +41,9 @@ pub mod sendmail;
pub mod smtp; pub mod smtp;
pub mod stub; pub mod stub;
use error::EmailResult;
use error::Error as EmailError;
use failure::Error;
#[cfg(feature = "file-transport")] #[cfg(feature = "file-transport")]
pub use file::FileTransport; pub use file::FileTransport;
#[cfg(feature = "sendmail-transport")] #[cfg(feature = "sendmail-transport")]
@@ -45,47 +52,12 @@ pub use sendmail::SendmailTransport;
pub use smtp::client::net::ClientTlsParameters; pub use smtp::client::net::ClientTlsParameters;
#[cfg(feature = "smtp-transport")] #[cfg(feature = "smtp-transport")]
pub use smtp::{ClientSecurity, SmtpClient, SmtpTransport}; pub use smtp::{ClientSecurity, SmtpClient, SmtpTransport};
use std::error::Error as StdError;
use std::fmt::{self, Display, Formatter}; use std::fmt::{self, Display, Formatter};
use std::io; use std::io;
use std::io::Cursor; use std::io::Cursor;
use std::io::Read; use std::io::Read;
use std::str::FromStr; 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<T> = Result<T, Error>;
/// Email address /// Email address
#[derive(PartialEq, Eq, Clone, Debug)] #[derive(PartialEq, Eq, Clone, Debug)]
#[cfg_attr(feature = "serde-impls", derive(Serialize, Deserialize))] #[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. /// Creates a new envelope, which may fail if `to` is empty.
pub fn new(from: Option<EmailAddress>, to: Vec<EmailAddress>) -> EmailResult<Envelope> { pub fn new(from: Option<EmailAddress>, to: Vec<EmailAddress>) -> EmailResult<Envelope> {
if to.is_empty() { if to.is_empty() {
return Err(Error::MissingTo); Err(EmailError::MissingTo)?;
} }
Ok(Envelope { Ok(Envelope {
forward_path: to, forward_path: to,

View File

@@ -1,52 +1,30 @@
//! Error and result type for sendmail transport //! Error and result type for sendmail transport
use self::Error::*; use failure;
use std::error::Error as StdError;
use std::fmt::{self, Display, Formatter};
use std::io; use std::io;
/// An enum of all error kinds. /// An enum of all error kinds.
#[derive(Debug)] #[derive(Fail, Debug)]
pub enum Error { pub enum Error {
/// Internal client error /// Internal client error
Client(&'static str), #[fail(display = "Internal client error: {}", error)]
Client { error: &'static str },
/// IO error /// IO error
Io(io::Error), #[fail(display = "IO error: {}", error)]
} Io { error: 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,
}
}
} }
impl From<io::Error> for Error { impl From<io::Error> for Error {
fn from(err: io::Error) -> Error { fn from(err: io::Error) -> Error {
Io(err) Error::Io { error: err }
} }
} }
impl From<&'static str> for Error { impl From<&'static str> for Error {
fn from(string: &'static str) -> Error { fn from(string: &'static str) -> Error {
Client(string) Error::Client { error: string }
} }
} }
/// sendmail result type /// sendmail result type
pub type SendmailResult = Result<(), Error>; pub type SendmailResult = Result<(), failure::Error>;

View File

@@ -58,26 +58,23 @@ impl<'a> Transport<'a> for SendmailTransport {
let mut message_content = String::new(); let mut message_content = String::new();
let _ = email.message().read_to_string(&mut message_content); let _ = email.message().read_to_string(&mut message_content);
match process process
.stdin .stdin
.as_mut() .as_mut()
.unwrap() .unwrap()
.write_all(message_content.as_bytes()) .write_all(message_content.as_bytes())?;
{
Ok(_) => (),
Err(error) => return Err(From::from(error)),
}
info!("Wrote {} message to stdin", message_id); info!("Wrote {} message to stdin", message_id);
if let Ok(output) = process.wait_with_output() { let output = process.wait_with_output()?;
if output.status.success() {
Ok(()) if output.status.success() {
} else { Ok(())
Err(From::from("The message could not be sent"))
}
} else { } else {
Err(From::from("The sendmail process stopped")) // TODO display stderr
Err(error::Error::Client {
error: "The message could not be sent",
})?
} }
} }
} }

View File

@@ -29,3 +29,5 @@ time = "^0.1"
uuid = { version = "^0.6", features = ["v4"] } uuid = { version = "^0.6", features = ["v4"] }
lettre = { version = "^0.9", path = "../lettre", default-features = false } lettre = { version = "^0.9", path = "../lettre", default-features = false }
base64 = "^0.9" base64 = "^0.9"
failure = "^0.1"
failure_derive = "^0.1"

View File

@@ -1,47 +1,36 @@
//! Error and result type for emails //! Error and result type for emails
use self::Error::*; use lettre;
use std::error::Error as StdError;
use std::fmt::{self, Display, Formatter};
use std::io; use std::io;
use lettre;
/// An enum of all error kinds. /// An enum of all error kinds.
#[derive(Debug)] #[derive(Debug, Fail)]
pub enum Error { pub enum Error {
/// Envelope error /// Envelope error
Email(lettre::Error), #[fail(display = "lettre error: {}", error)]
Envelope {
/// inner error
error: lettre::error::Error,
},
/// Unparseable filename for attachment /// Unparseable filename for attachment
#[fail(display = "the attachment filename could not be parsed")]
CannotParseFilename, CannotParseFilename,
/// IO error /// IO error
Io(io::Error), #[fail(display = "IO error: {}", error)]
} Io {
/// inner error
impl Display for Error { error: io::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(),
}
}
} }
impl From<io::Error> for Error { impl From<io::Error> for Error {
fn from(err: io::Error) -> Error { fn from(err: io::Error) -> Error {
Io(err) Error::Io { error: err }
} }
} }
impl From<lettre::Error> for Error { impl From<lettre::error::Error> for Error {
fn from(err: lettre::Error) -> Error { fn from(err: lettre::error::Error) -> Error {
Email(err) Error::Envelope { error: err }
} }
} }

View File

@@ -4,10 +4,13 @@
#![doc(html_root_url = "https://docs.rs/lettre_email/0.9.0")] #![doc(html_root_url = "https://docs.rs/lettre_email/0.9.0")]
#![deny( #![deny(
missing_docs, missing_debug_implementations, missing_copy_implementations, trivial_casts, missing_docs, missing_debug_implementations, missing_copy_implementations, trivial_casts,
trivial_numeric_casts, unsafe_code, unstable_features, unused_import_braces, trivial_numeric_casts, unsafe_code, unstable_features, unused_import_braces
unused_qualifications
)] )]
extern crate failure;
#[macro_use]
extern crate failure_derive;
extern crate base64; extern crate base64;
extern crate email as email_format; extern crate email as email_format;
extern crate lettre; extern crate lettre;
@@ -18,8 +21,9 @@ extern crate uuid;
pub mod error; pub mod error;
pub use email_format::{Address, Header, Mailbox, MimeMessage, MimeMultipartType}; pub use email_format::{Address, Header, Mailbox, MimeMessage, MimeMultipartType};
use error::Error; use error::Error as EmailError;
use lettre::{EmailAddress, Envelope, Error as EmailError, SendableEmail}; use failure::Error;
use lettre::{error::Error as LettreError, EmailAddress, Envelope, SendableEmail};
use mime::Mime; use mime::Mime;
use std::fs; use std::fs;
use std::path::Path; use std::path::Path;
@@ -231,7 +235,7 @@ impl EmailBuilder {
fs::read(path)?.as_slice(), fs::read(path)?.as_slice(),
filename.unwrap_or(path.file_name() filename.unwrap_or(path.file_name()
.and_then(|x| x.to_str()) .and_then(|x| x.to_str())
.ok_or(Error::CannotParseFilename)?), .ok_or(EmailError::CannotParseFilename)?),
content_type, content_type,
) )
} }
@@ -369,26 +373,30 @@ impl EmailBuilder {
} }
} }
let from = Some(EmailAddress::from_str(&match self.sender { 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 => { None => {
// use a from header // use a from header
debug_assert!(self.from.len() <= 1); // else we'd have sender_header debug_assert!(self.from.len() <= 1); // else we'd have sender_header
match self.from.first() { match self.from.first() {
Some(a) => match *a { Some(a) => match *a {
// if we have a from header // 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() { Address::Group(_, ref mailbox_list) => match mailbox_list.first() {
// if it's an author group, use the first author // 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) // 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 // 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)? Envelope::new(from, to)?
} }
}; };
@@ -402,7 +410,9 @@ impl EmailBuilder {
self.message = self.message self.message = self.message
.header(Header::new_with_value("From".into(), self.from).unwrap()); .header(Header::new_with_value("From".into(), self.from).unwrap());
} else { } else {
return Err(Error::Email(EmailError::MissingFrom)); Err(EmailError::Envelope {
error: LettreError::MissingFrom,
})?;
} }
if !self.cc.is_empty() { if !self.cc.is_empty() {
self.message = self.message self.message = self.message