1use self::Error::*;
4use crate::response::{Response, Severity};
5use base64::DecodeError;
6use std::io;
7use std::net::AddrParseError;
8use std::string::FromUtf8Error;
9
10#[derive(thiserror::Error, Debug)]
12pub enum Error {
13 #[error("transient: {}", if .0.message.is_empty() { "undetailed error during SMTP transaction".to_string() } else { .0.message.join("; ") })]
17 Transient(Response),
18 #[error("permanent: {}", if .0.message.is_empty() { "undetailed error during SMTP transaction".to_string() } else { .0.message.join("; ") })]
22 Permanent(Response),
23 #[error("{0}")]
25 ResponseParsing(&'static str),
26 #[error("challenge parsing: {0}")]
28 ChallengeParsing(#[from] DecodeError),
29 #[error("utf8: {0}")]
31 Utf8Parsing(#[from] FromUtf8Error),
32 #[error("client: {0}")]
34 Client(&'static str),
35 #[error("could not resolve hostname")]
37 Resolution,
38 #[error("io: {0}")]
40 Io(#[from] io::Error),
41 #[error("parsing: {0:?}")]
43 Parsing(nom::error::ErrorKind),
44 #[cfg(feature = "runtime-tokio")]
45 #[error("timeout: {0}")]
47 Timeout(#[from] tokio::time::error::Elapsed),
48 #[cfg(feature = "runtime-async-std")]
49 #[error("timeout: {0}")]
51 Timeout(#[from] async_std::future::TimeoutError),
52 #[error("address parse error: {0}")]
54 AddrParseError(#[from] AddrParseError),
55}
56
57impl From<nom::Err<nom::error::Error<&str>>> for Error {
58 fn from(err: nom::Err<nom::error::Error<&str>>) -> Error {
59 Parsing(match err {
60 nom::Err::Incomplete(_) => nom::error::ErrorKind::Complete,
61 nom::Err::Failure(e) => e.code,
62 nom::Err::Error(e) => e.code,
63 })
64 }
65}
66
67impl From<Response> for Error {
68 fn from(response: Response) -> Error {
69 match response.code.severity {
70 Severity::TransientNegativeCompletion => Transient(response),
71 Severity::PermanentNegativeCompletion => Permanent(response),
72 _ => Client("Unknown error code"),
73 }
74 }
75}
76
77impl From<&'static str> for Error {
78 fn from(string: &'static str) -> Error {
79 Client(string)
80 }
81}
82
83pub type SmtpResult = Result<Response, Error>;
85
86#[cfg(test)]
87mod test {
88 use super::*;
89 use crate::response::{Category, Code, Detail, Response, Severity};
90
91 #[test]
92 fn test_error_response_to_string() {
93 let err = Error::Permanent(Response::new(
94 Code::new(
95 Severity::PermanentNegativeCompletion,
96 Category::Information,
97 Detail::Zero,
98 ),
99 vec![
100 "gmx.net (mxgmx117) Nemesis ESMTP Service not available".to_string(),
101 "No SMTP service".to_string(),
102 "IP address is block listed.".to_string(),
103 "For explanation visit https://www.gmx.net/mail/senderguidelines?c=bl".to_string(),
104 ],
105 ));
106 assert_eq!(format!("{}", err), "permanent: gmx.net (mxgmx117) Nemesis ESMTP Service not available; No SMTP service; IP address is block listed.; For explanation visit https://www.gmx.net/mail/senderguidelines?c=bl".to_string());
107 }
108}