use core::fmt;
use std::error::Error as StdError;
#[derive(Debug)]
pub enum SmtpError {
Io(IoError),
Protocol(ProtocolError),
Auth(AuthError),
InvalidInput(InvalidInputError),
}
impl fmt::Display for SmtpError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Io(e) => write!(f, "smtp transport error: {e}"),
Self::Protocol(e) => write!(f, "smtp protocol error: {e}"),
Self::Auth(e) => write!(f, "smtp auth error: {e}"),
Self::InvalidInput(e) => write!(f, "smtp invalid input: {e}"),
}
}
}
impl StdError for SmtpError {
fn source(&self) -> Option<&(dyn StdError + 'static)> {
match self {
Self::Io(e) => Some(e),
Self::Protocol(e) => Some(e),
Self::Auth(e) => Some(e),
Self::InvalidInput(e) => Some(e),
}
}
}
impl From<IoError> for SmtpError {
fn from(value: IoError) -> Self {
Self::Io(value)
}
}
impl From<ProtocolError> for SmtpError {
fn from(value: ProtocolError) -> Self {
Self::Protocol(value)
}
}
impl From<AuthError> for SmtpError {
fn from(value: AuthError) -> Self {
Self::Auth(value)
}
}
impl From<InvalidInputError> for SmtpError {
fn from(value: InvalidInputError) -> Self {
Self::InvalidInput(value)
}
}
#[derive(Debug)]
pub struct IoError {
message: String,
}
impl IoError {
pub fn new(message: impl Into<String>) -> Self {
Self {
message: message.into(),
}
}
pub fn message(&self) -> &str {
&self.message
}
}
impl fmt::Display for IoError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.message)
}
}
impl StdError for IoError {}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum SmtpOp {
Greeting,
Ehlo,
StartTls,
AuthPlain,
AuthLogin,
MailFrom,
RcptTo,
Data,
Quit,
}
impl SmtpOp {
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Self::Greeting => "greeting",
Self::Ehlo => "EHLO",
Self::StartTls => "STARTTLS",
Self::AuthPlain => "AUTH PLAIN",
Self::AuthLogin => "AUTH LOGIN",
Self::MailFrom => "MAIL FROM",
Self::RcptTo => "RCPT TO",
Self::Data => "DATA",
Self::Quit => "QUIT",
}
}
}
impl fmt::Display for SmtpOp {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
#[derive(Debug)]
#[non_exhaustive]
pub enum ProtocolError {
UnexpectedCode {
during: SmtpOp,
expected_class: u8,
actual: u16,
message: String,
},
Malformed(String),
UnexpectedClose,
LineTooLong,
InconsistentMultiline {
first: u16,
later: u16,
},
ExtensionUnavailable {
name: &'static str,
},
}
impl fmt::Display for ProtocolError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::UnexpectedCode {
during,
expected_class,
actual,
message,
} => write!(
f,
"during {during}, expected {expected_class}xx response but received {actual}: {message}",
),
Self::Malformed(s) => write!(f, "malformed server reply: {s}"),
Self::UnexpectedClose => f.write_str("server closed connection unexpectedly"),
Self::LineTooLong => f.write_str("server reply line exceeded SMTP line-length limit"),
Self::InconsistentMultiline { first, later } => {
write!(f, "multi-line reply mixed codes {first} and {later}",)
}
Self::ExtensionUnavailable { name } => {
write!(f, "server did not advertise the {name} extension")
}
}
}
}
impl StdError for ProtocolError {}
#[derive(Debug)]
pub enum AuthError {
Rejected {
code: u16,
message: String,
},
UnsupportedMechanism,
MalformedChallenge(String),
}
impl fmt::Display for AuthError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Rejected { code, message } => {
write!(f, "server rejected authentication ({code}): {message}")
}
Self::UnsupportedMechanism => f.write_str(
"server did not advertise an AUTH mechanism supported by this client \
(expected PLAIN or LOGIN)",
),
Self::MalformedChallenge(s) => {
write!(f, "server sent a malformed AUTH challenge: {s}")
}
}
}
}
impl StdError for AuthError {}
#[derive(Debug)]
pub struct InvalidInputError {
reason: &'static str,
}
impl InvalidInputError {
pub const fn new(reason: &'static str) -> Self {
Self { reason }
}
pub const fn reason(&self) -> &'static str {
self.reason
}
}
impl fmt::Display for InvalidInputError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.reason)
}
}
impl StdError for InvalidInputError {}