feat(transport): Add an Error and Result type for each transport

This commit is contained in:
Alexis Mousset
2016-08-31 21:33:02 +02:00
parent 86c45f13c3
commit bc874fa8f4
14 changed files with 186 additions and 95 deletions

View File

@@ -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 {

View File

@@ -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() {

View File

@@ -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<io::Error> 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>;

View File

@@ -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<T: SendableEmail>(&mut self, email: T) -> EmailResult {
impl EmailTransport<FileResult> for FileEmailTransport {
fn send<T: SendableEmail>(&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=<written>", 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) {

View File

@@ -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<U> {
/// Sends the email
fn send<T: SendableEmail>(&mut self, email: T) -> EmailResult;
fn send<T: SendableEmail>(&mut self, email: T) -> U;
/// Close the transport explicitly
fn close(&mut self);
}

View File

@@ -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)]

View File

@@ -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<S: Connector + Write + Read + Debug + Clone> Client<S> {
pub fn connect<A: ToSocketAddrs>(&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<S: Connector + Write + Read + Debug + Clone> Client<S> {
}
/// 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<S: Connector + Write + Read + Debug + Clone> Client<S> {
}
/// 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<S: Connector + Write + Read + Debug + Clone> Client<S> {
}
/// 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<S: Connector + Write + Read + Debug + Clone> Client<S> {
}
/// 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<S: Connector + Write + Read + Debug + Clone> Client<S> {
}
/// Gets the SMTP response
fn get_reply(&mut self) -> EmailResult {
fn get_reply(&mut self) -> SmtpResult {
let mut parser = ResponseParser::default();

View File

@@ -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 {

View File

@@ -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<Response, Error>;
pub type SmtpResult = Result<Response, Error>;

View File

@@ -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 {

View File

@@ -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<SmtpResult> for SmtpTransport {
/// Sends an email
fn send<T: SendableEmail>(&mut self, email: T) -> EmailResult {
fn send<T: SendableEmail>(&mut self, email: T) -> SmtpResult {
// Extract email information
let message_id = email.message_id();

View File

@@ -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 => {

View File

@@ -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)
}
}

View File

@@ -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<T: SendableEmail>(&mut self, email: T) -> EmailResult {
/// SMTP result type
pub type StubResult = Result<(), error::Error>;
impl EmailTransport<StubResult> for StubEmailTransport {
fn send<T: SendableEmail>(&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) {