Add error types

This commit is contained in:
Alexis Mousset
2015-07-14 10:32:12 +02:00
parent 29c9b7661e
commit 75de338409
4 changed files with 37 additions and 29 deletions

View File

@@ -28,8 +28,12 @@ pub enum Error {
///
/// [RFC 5321, section 4.2.1](https://tools.ietf.org/html/rfc5321#section-4.2.1)
PermanentError(Response),
/// Error parsing a response
ResponseParsingError(&'static str),
/// Internal client error
ClientError(String),
ClientError(&'static str),
/// DNS resolution error
ResolutionError,
/// IO error
IoError(io::Error),
}
@@ -45,6 +49,8 @@ impl StdError for Error {
match *self {
TransientError(_) => "a transient error occured during the SMTP transaction",
PermanentError(_) => "a permanent error occured during the SMTP transaction",
ResponseParsingError(_) => "an error occured while parsing an SMTP response",
ResolutionError => "Could no resolve hostname",
ClientError(_) => "an unknown error occured",
IoError(_) => "an I/O error occured",
}
@@ -69,14 +75,14 @@ impl From<Response> for Error {
match response.severity() {
Severity::TransientNegativeCompletion => TransientError(response),
Severity::PermanentNegativeCompletion => PermanentError(response),
_ => ClientError("Unknown error code".to_string())
_ => ClientError("Unknown error code")
}
}
}
impl From<&'static str> for Error {
fn from(string: &'static str) -> Error {
ClientError(string.to_string())
ClientError(string)
}
}

View File

@@ -42,7 +42,7 @@ pub enum Extension {
impl Extension {
fn from_str(s: &str) -> Result<Vec<Extension>, &'static str> {
let splitted : Vec<&str> = s.split(' ').collect();
let splitted : Vec<&str> = s.split_whitespace().collect();
match (splitted[0], splitted.len()) {
("8BITMIME", 1) => Ok(vec![EightBitMime]),
("SMTPUTF8", 1) => Ok(vec![SmtpUtfEight]),

View File

@@ -31,14 +31,14 @@ pub enum Severity {
}
impl FromStr for Severity {
type Err = &'static str;
fn from_str(s: &str) -> RResult<Severity, &'static str> {
type Err = Error;
fn from_str(s: &str) -> RResult<Severity, Error> {
match s {
"2" => Ok(PositiveCompletion),
"3" => Ok(PositiveIntermediate),
"4" => Ok(TransientNegativeCompletion),
"5" => Ok(PermanentNegativeCompletion),
_ => Err("First digit must be between 2 and 5"),
_ => Err(Error::ResponseParsingError("First digit must be between 2 and 5")),
}
}
}
@@ -74,8 +74,8 @@ pub enum Category {
}
impl FromStr for Category {
type Err = &'static str;
fn from_str(s: &str) -> RResult<Category, &'static str> {
type Err = Error;
fn from_str(s: &str) -> RResult<Category, Error> {
match s {
"0" => Ok(Syntax),
"1" => Ok(Information),
@@ -83,7 +83,7 @@ impl FromStr for Category {
"3" => Ok(Unspecified3),
"4" => Ok(Unspecified4),
"5" => Ok(MailSystem),
_ => Err("Second digit must be between 0 and 5"),
_ => Err(Error::ResponseParsingError("Second digit must be between 0 and 5")),
}
}
}
@@ -122,10 +122,10 @@ impl FromStr for Code {
if s.len() == 3 {
match (s[0..1].parse::<Severity>(), s[1..2].parse::<Category>(), s[2..3].parse::<u8>()) {
(Ok(severity), Ok(category), Ok(detail)) => Ok(Code {severity: severity, category: category, detail: detail}),
_ => return Err(From::from("Could not parse reply code")),
_ => return Err(Error::ResponseParsingError("Could not parse response code")),
}
} else {
Err(From::from("Could not parse reply code"))
Err(Error::ResponseParsingError("Wrong code length (should be 3 digit)"))
}
}
}
@@ -169,15 +169,16 @@ impl ResponseParser {
pub fn read_line(&mut self, line: &str) -> RResult<bool, Error> {
if line.len() < 3 {
return Err(From::from("Could not parse reply code, line too short"));
return Err(Error::ResponseParsingError("Wrong code length (should be 3 digit)"));
}
if self.code.is_none() {
self.code = Some(try!(line[0..3].parse::<Code>()));
} else {
if self.code.as_ref().unwrap().code() != line[0..3] {
return Err(From::from("Could not parse reply code"));
}
match self.code {
Some(ref code) => {
if code.code() != line[0..3] {
return Err(Error::ResponseParsingError("Response code has changed during a reponse"));
}
},
None => self.code = Some(try!(line[0..3].parse::<Code>()))
}
if line.len() > 4 {
@@ -194,10 +195,9 @@ impl ResponseParser {
/// Builds a response from a `ResponseParser`
pub fn response(self) -> SmtpResult {
if self.code.is_some() {
Ok(Response::new(self.code.unwrap(), self.message))
} else {
Err(From::from("Could not parse reply code"))
match self.code {
Some(code) => Ok(Response::new(code, self.message)),
None => Err(Error::ResponseParsingError("Incomplete response, could not read response code"))
}
}
}
@@ -280,8 +280,8 @@ mod test {
#[test]
fn test_severity_from_str() {
assert_eq!("2".parse::<Severity>(), Ok(Severity::PositiveCompletion));
assert_eq!("4".parse::<Severity>(), Ok(Severity::TransientNegativeCompletion));
assert_eq!("2".parse::<Severity>().unwrap(), Severity::PositiveCompletion);
assert_eq!("4".parse::<Severity>().unwrap(), Severity::TransientNegativeCompletion);
assert!("1".parse::<Severity>().is_err());
}
@@ -292,8 +292,8 @@ mod test {
#[test]
fn test_category_from_str() {
assert_eq!("2".parse::<Category>(), Ok(Category::Connections));
assert_eq!("4".parse::<Category>(), Ok(Category::Unspecified4));
assert_eq!("2".parse::<Category>().unwrap(), Category::Connections);
assert_eq!("4".parse::<Category>().unwrap(), Category::Unspecified4);
assert!("6".parse::<Category>().is_err());
}

View File

@@ -171,6 +171,8 @@ impl Sender {
if self.state.connection_reuse_count == 0 {
try!(self.client.connect());
let hello_error = Error::ResponseParsingError("No hostname announced by the server");
// Log the connection
info!("connection established to {}", self.client_info.server_addr);
@@ -178,7 +180,7 @@ impl Sender {
match self.client.ehlo(&self.client_info.hello_name) {
Ok(response) => {self.server_info = Some(
ServerInfo{
name: response.first_word().expect("Server announced no hostname"),
name: try_smtp!(response.first_word().ok_or(hello_error), self),
esmtp_features: Extension::parse_esmtp_response(&response),
});
},
@@ -187,7 +189,7 @@ impl Sender {
match self.client.helo(&self.client_info.hello_name) {
Ok(response) => {self.server_info = Some(
ServerInfo{
name: response.first_word().expect("Server announced no hostname"),
name: try_smtp!(response.first_word().ok_or(hello_error), self),
esmtp_features: vec!(),
});
},