Use a ReplyCode enum for reply codes

This commit is contained in:
Dirkjan Ochtman 2022-10-10 11:38:06 +02:00 committed by Damian Poddebniak
parent 78785279c6
commit a3f2cdc5c0
1 changed files with 169 additions and 10 deletions

View File

@ -301,7 +301,7 @@ pub enum Response {
capabilities: Vec<Capability>, capabilities: Vec<Capability>,
}, },
Other { Other {
code: u16, code: ReplyCode,
text: String, text: String,
}, },
} }
@ -330,7 +330,7 @@ impl Response {
} }
} }
pub fn other<T>(code: u16, text: T) -> Response pub fn other<T>(code: ReplyCode, text: T) -> Response
where where
T: Into<String>, T: Into<String>,
{ {
@ -388,6 +388,7 @@ impl Response {
} }
} }
Response::Other { code, text } => { Response::Other { code, text } => {
let code = u16::from(*code);
let lines = text.lines().collect::<Vec<_>>(); let lines = text.lines().collect::<Vec<_>>();
if let Some((last, head)) = lines.split_last() { if let Some((last, head)) = lines.split_last() {
@ -591,6 +592,164 @@ impl Capability {
} }
} }
#[cfg_attr(feature = "serdex", derive(Serialize, Deserialize))]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
pub enum ReplyCode {
/// 211 System status, or system help reply
SystemStatus,
/// 214 Help message
///
/// Information on how to use the receiver or the meaning of a particular non-standard
/// command; this reply is useful only to the human user.
HelpMessage,
/// 220 <domain> Service ready
Ready,
/// 221 <domain> Service closing transmission channel
ClosingChannel,
/// 250 Requested mail action okay, completed
Ok,
/// 251 User not local; will forward to <forward-path>
UserNotLocalWillForward,
/// 252 Cannot VRFY user, but will accept message and attempt delivery
CannotVrfy,
/// 354 Start mail input; end with <CRLF>.<CRLF>
StartMailInput,
/// 421 <domain> Service not available, closing transmission channel
///
/// This may be a reply to any command if the service knows it must shut down.
NotAvailable,
/// 450 Requested mail action not taken: mailbox unavailable
///
/// E.g., mailbox busy or temporarily blocked for policy reasons.
MailboxTemporarilyUnavailable,
/// 451 Requested action aborted: local error in processing
ProcessingError,
/// 452 Requested action not taken: insufficient system storage
InsufficientStorage,
/// 455 Server unable to accommodate parameters
UnableToAccommodateParameters,
/// 500 Syntax error, command unrecognized
SyntaxError,
/// 501 Syntax error in parameters or arguments
ParameterSyntaxError,
/// 502 Command not implemented
CommandNotImplemented,
/// 503 Bad sequence of commands
BadSequence,
/// 504 Command parameter not implemented
ParameterNotImplemented,
/// 521 <domain> does not accept mail (see RFC 1846)
NoMailService,
/// 550 Requested action not taken: mailbox unavailable
///
/// E.g. mailbox not found, no access, or command rejected for policy reasons.
MailboxPermanentlyUnavailable,
/// 551 User not local; please try <forward-path>
UserNotLocal,
/// 552 Requested mail action aborted: exceeded storage allocation
ExceededStorageAllocation,
/// 553 Requested action not taken: mailbox name not allowed
///
/// E.g. mailbox syntax incorrect.
MailboxNameNotAllowed,
/// 554 Transaction failed
///
/// Or, in the case of a connection-opening response, "No SMTP service here".
TransactionFailed,
/// 555 MAIL FROM/RCPT TO parameters not recognized or not implemented
ParametersNotImplemented,
/// Miscellaneous reply codes
Other(u16),
}
impl ReplyCode {
pub fn is_completed(&self) -> bool {
let code = u16::from(*self);
code > 199 && code < 300
}
pub fn is_accepted(&self) -> bool {
let code = u16::from(*self);
code > 299 && code < 400
}
pub fn is_temporary_error(&self) -> bool {
let code = u16::from(*self);
code > 399 && code < 500
}
pub fn is_permanent_error(&self) -> bool {
let code = u16::from(*self);
code > 499 && code < 600
}
}
impl From<u16> for ReplyCode {
fn from(value: u16) -> Self {
match value {
211 => ReplyCode::SystemStatus,
214 => ReplyCode::HelpMessage,
220 => ReplyCode::Ready,
221 => ReplyCode::ClosingChannel,
250 => ReplyCode::Ok,
251 => ReplyCode::UserNotLocalWillForward,
252 => ReplyCode::CannotVrfy,
354 => ReplyCode::StartMailInput,
421 => ReplyCode::NotAvailable,
450 => ReplyCode::MailboxTemporarilyUnavailable,
451 => ReplyCode::ProcessingError,
452 => ReplyCode::InsufficientStorage,
455 => ReplyCode::UnableToAccommodateParameters,
500 => ReplyCode::SyntaxError,
501 => ReplyCode::ParameterSyntaxError,
502 => ReplyCode::CommandNotImplemented,
503 => ReplyCode::BadSequence,
504 => ReplyCode::ParameterNotImplemented,
521 => ReplyCode::NoMailService,
550 => ReplyCode::MailboxPermanentlyUnavailable,
551 => ReplyCode::UserNotLocal,
552 => ReplyCode::ExceededStorageAllocation,
553 => ReplyCode::MailboxNameNotAllowed,
554 => ReplyCode::TransactionFailed,
555 => ReplyCode::ParametersNotImplemented,
_ => ReplyCode::Other(value),
}
}
}
impl From<ReplyCode> for u16 {
fn from(value: ReplyCode) -> Self {
match value {
ReplyCode::SystemStatus => 211,
ReplyCode::HelpMessage => 214,
ReplyCode::Ready => 220,
ReplyCode::ClosingChannel => 221,
ReplyCode::Ok => 250,
ReplyCode::UserNotLocalWillForward => 251,
ReplyCode::CannotVrfy => 252,
ReplyCode::StartMailInput => 354,
ReplyCode::NotAvailable => 421,
ReplyCode::MailboxTemporarilyUnavailable => 450,
ReplyCode::ProcessingError => 451,
ReplyCode::InsufficientStorage => 452,
ReplyCode::UnableToAccommodateParameters => 455,
ReplyCode::SyntaxError => 500,
ReplyCode::ParameterSyntaxError => 501,
ReplyCode::CommandNotImplemented => 502,
ReplyCode::BadSequence => 503,
ReplyCode::ParameterNotImplemented => 504,
ReplyCode::NoMailService => 521,
ReplyCode::MailboxPermanentlyUnavailable => 550,
ReplyCode::UserNotLocal => 551,
ReplyCode::ExceededStorageAllocation => 552,
ReplyCode::MailboxNameNotAllowed => 553,
ReplyCode::TransactionFailed => 554,
ReplyCode::ParametersNotImplemented => 555,
ReplyCode::Other(v) => v,
}
}
}
#[cfg_attr(feature = "serdex", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serdex", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive] #[non_exhaustive]
@ -628,7 +787,7 @@ impl AuthMechanism {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::types::{Capability, Response}; use super::{Capability, ReplyCode, Response};
#[test] #[test]
fn test_serialize_greeting() { fn test_serialize_greeting() {
@ -719,24 +878,24 @@ mod tests {
let tests = &[ let tests = &[
( (
Response::Other { Response::Other {
code: 333, code: ReplyCode::StartMailInput,
text: "".into(), text: String::new(),
}, },
b"333\r\n".as_ref(), b"354\r\n".as_ref(),
), ),
( (
Response::Other { Response::Other {
code: 333, code: ReplyCode::StartMailInput,
text: "A".into(), text: "A".into(),
}, },
b"333 A\r\n".as_ref(), b"354 A\r\n".as_ref(),
), ),
( (
Response::Other { Response::Other {
code: 333, code: ReplyCode::StartMailInput,
text: "A\nB".into(), text: "A\nB".into(),
}, },
b"333-A\r\n333 B\r\n".as_ref(), b"354-A\r\n354 B\r\n".as_ref(),
), ),
]; ];