From bc874fa8f4db74e01ebb5515ed98c5c3a3fe92d0 Mon Sep 17 00:00:00 2001 From: Alexis Mousset Date: Wed, 31 Aug 2016 21:33:02 +0200 Subject: [PATCH] feat(transport): Add an Error and Result type for each transport --- src/email/error.rs | 4 +-- src/email/mod.rs | 12 +++---- src/transport/file/error.rs | 54 ++++++++++++++++++++++++++++ src/transport/file/mod.rs | 20 +++++------ src/transport/mod.rs | 6 ++-- src/transport/smtp/authentication.rs | 12 +++---- src/transport/smtp/client/mod.rs | 52 +++++++++++++-------------- src/transport/smtp/client/net.rs | 8 ++--- src/transport/{ => smtp}/error.rs | 14 ++++---- src/transport/smtp/extension.rs | 10 +++--- src/transport/smtp/mod.rs | 23 ++++++------ src/transport/smtp/response.rs | 13 +++---- src/transport/stub/error.rs | 37 +++++++++++++++++++ src/transport/stub/mod.rs | 16 +++++---- 14 files changed, 186 insertions(+), 95 deletions(-) create mode 100644 src/transport/file/error.rs rename src/transport/{ => smtp}/error.rs (98%) create mode 100644 src/transport/stub/error.rs diff --git a/src/email/error.rs b/src/email/error.rs index 574b219..5ce7650 100644 --- a/src/email/error.rs +++ b/src/email/error.rs @@ -1,11 +1,11 @@ //! Error and result type for emails + +use self::Error::*; use std::error::Error as StdError; use std::fmt; use std::fmt::{Display, Formatter}; -use self::Error::*; - /// An enum of all error kinds. #[derive(Debug)] pub enum Error { diff --git a/src/email/mod.rs b/src/email/mod.rs index 2fc6533..6ae0dfb 100644 --- a/src/email/mod.rs +++ b/src/email/mod.rs @@ -1,14 +1,14 @@ //! Simple email representation pub mod error; -use std::fmt; -use std::fmt::{Display, Formatter}; +use email::error::Error; use email_format::{Header, Mailbox, MimeMessage, MimeMultipartType}; use mime::Mime; +use std::fmt; +use std::fmt::{Display, Formatter}; use time::{Tm, now}; use uuid::Uuid; -use email::error::Error; /// Converts an address or an address with an alias to a `Header` pub trait ToHeader { @@ -640,12 +640,12 @@ impl SendableEmail for Email { #[cfg(test)] mod test { - use time::now; - - use uuid::Uuid; use email_format::{Header, MimeMessage}; use super::{Email, EmailBuilder, Envelope, SendableEmail}; + use time::now; + + use uuid::Uuid; #[test] fn test_email_display() { diff --git a/src/transport/file/error.rs b/src/transport/file/error.rs new file mode 100644 index 0000000..694e3d7 --- /dev/null +++ b/src/transport/file/error.rs @@ -0,0 +1,54 @@ +//! Error and result type for file transport + + +use self::Error::*; +use std::error::Error as StdError; +use std::fmt; +use std::fmt::{Display, Formatter}; +use std::io; + +/// An enum of all error kinds. +#[derive(Debug)] +pub enum Error { + /// Internal client error + Client(&'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(_) => "an unknown error occured", + Io(_) => "an I/O error occured", + } + } + + fn cause(&self) -> Option<&StdError> { + match *self { + Io(ref err) => Some(&*err as &StdError), + _ => None, + } + } +} + +impl From for Error { + fn from(err: io::Error) -> Error { + Io(err) + } +} + +impl From<&'static str> for Error { + fn from(string: &'static str) -> Error { + Client(string) + } +} + +/// SMTP result type +pub type FileResult = Result<(), Error>; diff --git a/src/transport/file/mod.rs b/src/transport/file/mod.rs index 2a179fa..e5bc778 100644 --- a/src/transport/file/mod.rs +++ b/src/transport/file/mod.rs @@ -1,15 +1,15 @@ //! This transport creates a file for each email, containing the envelope information and the email //! itself. -use std::path::{Path, PathBuf}; -use std::io::prelude::*; +use email::SendableEmail; use std::fs::File; +use std::io::prelude::*; +use std::path::{Path, PathBuf}; use transport::EmailTransport; -use transport::error::EmailResult; -use transport::smtp::response::Response; -use transport::smtp::response::{Category, Code, Severity}; -use email::SendableEmail; +use transport::file::error::FileResult; + +pub mod error; /// Writes the content and the envelope information to a file pub struct FileEmailTransport { @@ -25,8 +25,8 @@ impl FileEmailTransport { } } -impl EmailTransport for FileEmailTransport { - fn send(&mut self, email: T) -> EmailResult { +impl EmailTransport for FileEmailTransport { + fn send(&mut self, email: T) -> FileResult { let mut file = self.path.clone(); file.push(format!("{}.txt", email.message_id())); @@ -42,9 +42,7 @@ impl EmailTransport for FileEmailTransport { info!("{} status=", log_line); - Ok(Response::new(Code::new(Severity::PositiveCompletion, Category::MailSystem, 0), - vec![format!("Ok: email written to {}", - file.to_str().unwrap_or("non-UTF-8 path"))])) + Ok(()) } fn close(&mut self) { diff --git a/src/transport/mod.rs b/src/transport/mod.rs index c371c30..e884bb8 100644 --- a/src/transport/mod.rs +++ b/src/transport/mod.rs @@ -1,16 +1,14 @@ //! Represents an Email transport pub mod smtp; -pub mod error; pub mod stub; pub mod file; -use transport::error::EmailResult; use email::SendableEmail; /// Transport method for emails -pub trait EmailTransport { +pub trait EmailTransport { /// Sends the email - fn send(&mut self, email: T) -> EmailResult; + fn send(&mut self, email: T) -> U; /// Close the transport explicitly fn close(&mut self); } diff --git a/src/transport/smtp/authentication.rs b/src/transport/smtp/authentication.rs index 8d30373..9f75262 100644 --- a/src/transport/smtp/authentication.rs +++ b/src/transport/smtp/authentication.rs @@ -1,16 +1,16 @@ //! Provides authentication mechanisms -use std::fmt; -use std::fmt::{Display, Formatter}; +use crypto::hmac::Hmac; +use crypto::mac::Mac; +use crypto::md5::Md5; use rustc_serialize::base64::{self, FromBase64, ToBase64}; use rustc_serialize::hex::ToHex; -use crypto::hmac::Hmac; -use crypto::md5::Md5; -use crypto::mac::Mac; +use std::fmt; +use std::fmt::{Display, Formatter}; use transport::smtp::NUL; -use transport::error::Error; +use transport::smtp::error::Error; /// Represents authentication mechanisms #[derive(PartialEq,Eq,Copy,Clone,Hash,Debug)] diff --git a/src/transport/smtp/client/mod.rs b/src/transport/smtp/client/mod.rs index cc61e9c..fddb61b 100644 --- a/src/transport/smtp/client/mod.rs +++ b/src/transport/smtp/client/mod.rs @@ -1,19 +1,19 @@ //! SMTP client -use std::string::String; -use std::net::ToSocketAddrs; -use std::io; -use std::io::{BufRead, Read, Write}; -use std::fmt::Debug; use bufstream::BufStream; use openssl::ssl::SslContext; - -use transport::error::{EmailResult, Error}; -use transport::smtp::response::ResponseParser; +use std::fmt::Debug; +use std::io; +use std::io::{BufRead, Read, Write}; +use std::net::ToSocketAddrs; +use std::string::String; +use transport::smtp::{CRLF, MESSAGE_ENDING}; use transport::smtp::authentication::Mechanism; use transport::smtp::client::net::{Connector, NetworkStream}; -use transport::smtp::{CRLF, MESSAGE_ENDING}; + +use transport::smtp::error::{SmtpResult, Error}; +use transport::smtp::response::ResponseParser; pub mod net; @@ -98,7 +98,7 @@ impl Client { pub fn connect(&mut self, addr: &A, ssl_context: Option<&SslContext>) - -> EmailResult { + -> SmtpResult { // Connect should not be called when the client is already connected if self.stream.is_some() { return_err!("The connection is already established", self); @@ -125,17 +125,17 @@ impl Client { } /// Sends an SMTP command - pub fn command(&mut self, command: &str) -> EmailResult { + pub fn command(&mut self, command: &str) -> SmtpResult { self.send_server(command, CRLF) } /// Sends a EHLO command - pub fn ehlo(&mut self, hostname: &str) -> EmailResult { + pub fn ehlo(&mut self, hostname: &str) -> SmtpResult { self.command(&format!("EHLO {}", hostname)) } /// Sends a MAIL command - pub fn mail(&mut self, address: &str, options: Option<&str>) -> EmailResult { + pub fn mail(&mut self, address: &str, options: Option<&str>) -> SmtpResult { match options { Some(ref options) => self.command(&format!("MAIL FROM:<{}> {}", address, options)), None => self.command(&format!("MAIL FROM:<{}>", address)), @@ -143,27 +143,27 @@ impl Client { } /// Sends a RCPT command - pub fn rcpt(&mut self, address: &str) -> EmailResult { + pub fn rcpt(&mut self, address: &str) -> SmtpResult { self.command(&format!("RCPT TO:<{}>", address)) } /// Sends a DATA command - pub fn data(&mut self) -> EmailResult { + pub fn data(&mut self) -> SmtpResult { self.command("DATA") } /// Sends a QUIT command - pub fn quit(&mut self) -> EmailResult { + pub fn quit(&mut self) -> SmtpResult { self.command("QUIT") } /// Sends a NOOP command - pub fn noop(&mut self) -> EmailResult { + pub fn noop(&mut self) -> SmtpResult { self.command("NOOP") } /// Sends a HELP command - pub fn help(&mut self, argument: Option<&str>) -> EmailResult { + pub fn help(&mut self, argument: Option<&str>) -> SmtpResult { match argument { Some(ref argument) => self.command(&format!("HELP {}", argument)), None => self.command("HELP"), @@ -171,22 +171,22 @@ impl Client { } /// Sends a VRFY command - pub fn vrfy(&mut self, address: &str) -> EmailResult { + pub fn vrfy(&mut self, address: &str) -> SmtpResult { self.command(&format!("VRFY {}", address)) } /// Sends a EXPN command - pub fn expn(&mut self, address: &str) -> EmailResult { + pub fn expn(&mut self, address: &str) -> SmtpResult { self.command(&format!("EXPN {}", address)) } /// Sends a RSET command - pub fn rset(&mut self) -> EmailResult { + pub fn rset(&mut self) -> SmtpResult { self.command("RSET") } /// Sends an AUTH command with the given mechanism - pub fn auth(&mut self, mechanism: Mechanism, username: &str, password: &str) -> EmailResult { + pub fn auth(&mut self, mechanism: Mechanism, username: &str, password: &str) -> SmtpResult { if mechanism.supports_initial_response() { self.command(&format!("AUTH {} {}", @@ -209,17 +209,17 @@ impl Client { } /// Sends a STARTTLS command - pub fn starttls(&mut self) -> EmailResult { + pub fn starttls(&mut self) -> SmtpResult { self.command("STARTTLS") } /// Sends the message content - pub fn message(&mut self, message_content: &str) -> EmailResult { + pub fn message(&mut self, message_content: &str) -> SmtpResult { self.send_server(&escape_dot(message_content), MESSAGE_ENDING) } /// Sends a string to the server and gets the response - fn send_server(&mut self, string: &str, end: &str) -> EmailResult { + fn send_server(&mut self, string: &str, end: &str) -> SmtpResult { if self.stream.is_none() { return Err(From::from("Connection closed")); } @@ -233,7 +233,7 @@ impl Client { } /// Gets the SMTP response - fn get_reply(&mut self) -> EmailResult { + fn get_reply(&mut self) -> SmtpResult { let mut parser = ResponseParser::default(); diff --git a/src/transport/smtp/client/net.rs b/src/transport/smtp/client/net.rs index c4a0e6f..6716122 100644 --- a/src/transport/smtp/client/net.rs +++ b/src/transport/smtp/client/net.rs @@ -1,12 +1,12 @@ //! A trait to represent a stream + +use openssl::ssl::{SslContext, SslStream}; +use std::fmt; +use std::fmt::{Debug, Formatter}; use std::io; use std::io::{ErrorKind, Read, Write}; use std::net::{SocketAddr, TcpStream}; -use std::fmt; -use std::fmt::{Debug, Formatter}; - -use openssl::ssl::{SslContext, SslStream}; /// A trait for the concept of opening a stream pub trait Connector: Sized { diff --git a/src/transport/error.rs b/src/transport/smtp/error.rs similarity index 98% rename from src/transport/error.rs rename to src/transport/smtp/error.rs index 6d08c93..4cac2a6 100644 --- a/src/transport/error.rs +++ b/src/transport/smtp/error.rs @@ -1,13 +1,13 @@ //! Error and result type for SMTP clients -use std::error::Error as StdError; -use std::io; -use std::fmt; -use std::fmt::{Display, Formatter}; - -use transport::smtp::response::{Response, Severity}; use rustc_serialize::base64::FromBase64Error; use self::Error::*; +use std::error::Error as StdError; +use std::fmt; +use std::fmt::{Display, Formatter}; +use std::io; + +use transport::smtp::response::{Response, Severity}; /// An enum of all error kinds. #[derive(Debug)] @@ -82,4 +82,4 @@ impl From<&'static str> for Error { } /// SMTP result type -pub type EmailResult = Result; +pub type SmtpResult = Result; diff --git a/src/transport/smtp/extension.rs b/src/transport/smtp/extension.rs index c62de01..beb8e22 100644 --- a/src/transport/smtp/extension.rs +++ b/src/transport/smtp/extension.rs @@ -1,14 +1,14 @@ //! ESMTP features -use std::result::Result; +use std::collections::HashSet; use std::fmt; use std::fmt::{Display, Formatter}; -use std::collections::HashSet; - -use transport::error::Error; -use transport::smtp::response::Response; +use std::result::Result; use transport::smtp::authentication::Mechanism; +use transport::smtp::error::Error; +use transport::smtp::response::Response; + /// Supported ESMTP keywords #[derive(PartialEq,Eq,Hash,Clone,Debug)] pub enum Extension { diff --git a/src/transport/smtp/mod.rs b/src/transport/smtp/mod.rs index 6a964b0..6b0eeee 100644 --- a/src/transport/smtp/mod.rs +++ b/src/transport/smtp/mod.rs @@ -1,21 +1,22 @@ //! Sends an email using the client -use std::string::String; -use std::net::{SocketAddr, ToSocketAddrs}; +use email::SendableEmail; use openssl::ssl::{SslContext, SslMethod}; - -use transport::error::{EmailResult, Error}; -use transport::smtp::extension::{Extension, ServerInfo}; -use transport::smtp::client::Client; -use transport::smtp::authentication::Mechanism; +use std::net::{SocketAddr, ToSocketAddrs}; +use std::string::String; use transport::EmailTransport; -use email::SendableEmail; +use transport::smtp::authentication::Mechanism; +use transport::smtp::client::Client; + +use transport::smtp::error::{SmtpResult, Error}; +use transport::smtp::extension::{Extension, ServerInfo}; pub mod extension; pub mod authentication; pub mod response; pub mod client; +pub mod error; // Registrated port numbers: // https://www.iana. @@ -256,7 +257,7 @@ impl SmtpTransport { } /// Gets the EHLO response and updates server information - pub fn get_ehlo(&mut self) -> EmailResult { + pub fn get_ehlo(&mut self) -> SmtpResult { // Extended Hello let ehlo_response = try_smtp!(self.client.ehlo(&self.client_info.hello_name), self); @@ -269,9 +270,9 @@ impl SmtpTransport { } } -impl EmailTransport for SmtpTransport { +impl EmailTransport for SmtpTransport { /// Sends an email - fn send(&mut self, email: T) -> EmailResult { + fn send(&mut self, email: T) -> SmtpResult { // Extract email information let message_id = email.message_id(); diff --git a/src/transport/smtp/response.rs b/src/transport/smtp/response.rs index 50687c3..bcafb22 100644 --- a/src/transport/smtp/response.rs +++ b/src/transport/smtp/response.rs @@ -1,13 +1,14 @@ //! SMTP response, containing a mandatory return code and an optional text //! message -use std::str::FromStr; -use std::fmt::{Display, Formatter, Result}; -use std::result; +use self::Category::*; use self::Severity::*; -use self::Category::*; -use transport::error::{EmailResult, Error}; +use std::fmt::{Display, Formatter, Result}; +use std::result; +use std::str::FromStr; +use transport::smtp::error::{Error, SmtpResult}; + /// First digit indicates severity #[derive(PartialEq,Eq,Copy,Clone,Debug)] @@ -183,7 +184,7 @@ impl ResponseParser { } /// Builds a response from a `ResponseParser` - pub fn response(self) -> EmailResult { + pub fn response(self) -> SmtpResult { match self.code { Some(code) => Ok(Response::new(code, self.message)), None => { diff --git a/src/transport/stub/error.rs b/src/transport/stub/error.rs new file mode 100644 index 0000000..277f1e0 --- /dev/null +++ b/src/transport/stub/error.rs @@ -0,0 +1,37 @@ +//! Error and result type for file transport + +use self::Error::*; +use std::error::Error as StdError; +use std::fmt; +use std::fmt::{Display, Formatter}; + +/// An enum of all error kinds. +#[derive(Debug)] +pub enum Error { + /// Internal client error + Client(&'static str), +} + +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(_) => "an unknown error occured", + } + } + + fn cause(&self) -> Option<&StdError> { + None + } +} + +impl From<&'static str> for Error { + fn from(string: &'static str) -> Error { + Client(string) + } +} diff --git a/src/transport/stub/mod.rs b/src/transport/stub/mod.rs index 46a9618..c102979 100644 --- a/src/transport/stub/mod.rs +++ b/src/transport/stub/mod.rs @@ -1,23 +1,25 @@ //! This transport is a stub that only logs the message, and always returns //! success -use transport::error::EmailResult; -use transport::smtp::response::{Category, Code, Response, Severity}; -use transport::EmailTransport; use email::SendableEmail; +use transport::EmailTransport; + +pub mod error; /// This transport does nothing except logging the message envelope pub struct StubEmailTransport; -impl EmailTransport for StubEmailTransport { - fn send(&mut self, email: T) -> EmailResult { +/// SMTP result type +pub type StubResult = Result<(), error::Error>; + +impl EmailTransport for StubEmailTransport { + fn send(&mut self, email: T) -> StubResult { info!("{}: from=<{}> to=<{:?}>", email.message_id(), email.from_address(), email.to_addresses()); - Ok(Response::new(Code::new(Severity::PositiveCompletion, Category::MailSystem, 0), - vec!["Ok: email logged".to_string()])) + Ok(()) } fn close(&mut self) {