dory/src/status.rs

309 lines
9.2 KiB
Rust

use std::fmt;
#[derive(Debug, Clone, PartialEq)]
pub struct ParseStatusError;
impl fmt::Display for ParseStatusError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{self:?}")
}
}
impl std::error::Error for ParseStatusError {}
#[derive(Debug, Clone, PartialEq)]
#[repr(u8)]
pub enum Status {
/// These codes are reserved, and must not be sent by a Misfin server.
Input = 10,
/// Status codes beginning with 2 are SUCCESS status codes, which mean
/// that the client's message has been delivered.
Success = 20,
/// The mailbox has moved to a different address, and this message should
/// be resent to that address.
Redirect(Redirect) = 30,
/// Status codes beginning with 5 are PERMANENT FAILURE status codes, which
/// mean the request did not succeed, and should not be retried.
TemporaryFailure(TemporaryFailure) = 40,
/// Something is wrong with the mailserver, and you should not try to
/// resend your message.
PermanentFailure(PermanentFailure) = 50,
/// Status codes beginning with 6 are AUTHENTICATION FAILURE status codes,
/// which mean that there was an issue with the client's certificate.
AuthenticationFailure(AuthenticationFailure) = 60,
}
impl From<Status> for u8 {
fn from(value: Status) -> Self {
match value {
Status::Input | Status::Success => value.into(),
Status::Redirect(n) => 30 + n as u8,
Status::TemporaryFailure(n) => 40 + n as u8,
Status::PermanentFailure(n) => 50 + n as u8,
Status::AuthenticationFailure(n) => 60 + n as u8,
}
}
}
impl TryFrom<u8> for Status {
type Error = ParseStatusError;
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value / 10 {
1 => Ok(Self::Input),
2 => Ok(Self::Success),
3 => Ok(Self::Redirect((value % 30).try_into()?)),
4 => Ok(Self::TemporaryFailure((value % 40).try_into()?)),
5 => Ok(Self::PermanentFailure((value % 50).try_into()?)),
6 => Ok(Self::AuthenticationFailure((value % 60).try_into()?)),
_ => Err(ParseStatusError),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum Redirect {
/// The mailbox has moved to a different address, and this message
/// should be resent to that address.
Temporary = 0,
/// The mailbox has moved to a different address, and all future
/// messages should be sent to that address.
Permanent = 1,
Other,
}
impl TryFrom<u8> for Redirect {
type Error = ParseStatusError;
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
0 => Ok(Self::Temporary),
1 => Ok(Self::Permanent),
n if n < 10 => Ok(Self::Other),
_ => Err(ParseStatusError),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum TemporaryFailure {
/// The mailserver experienced a transient issue, and the message
/// should be resent.
TemporaryError = 0,
/// The mailserver can't accept mail right now.
ServerUnavailable = 1,
/// A mailserver script ran for your message, but experienced an error.
CgiError = 2,
/// There was a problem accepting mail for that domain, but it might
/// resolve itself.
ProxyError = 3,
/// You are being rate limited - wait before trying to send more mail.
RateLimit = 4,
/// The mailbox isn't accepting mail right now, but it might in the future.
MailboxFull = 5,
Other,
}
impl TryFrom<u8> for TemporaryFailure {
type Error = ParseStatusError;
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
0 => Ok(Self::TemporaryError),
1 => Ok(Self::ServerUnavailable),
2 => Ok(Self::CgiError),
3 => Ok(Self::ProxyError),
4 => Ok(Self::RateLimit),
5 => Ok(Self::MailboxFull),
n if n < 10 => Ok(Self::Other),
_ => Err(ParseStatusError),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum PermanentFailure {
PermanentError = 0,
MailboxNonexistent = 1,
MailboxGone = 2,
DomainNotServiced = 3,
BadRequest = 9,
Other,
}
impl TryFrom<u8> for PermanentFailure {
type Error = ParseStatusError;
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
0 => Ok(Self::PermanentError),
1 => Ok(Self::MailboxNonexistent),
2 => Ok(Self::MailboxGone),
3 => Ok(Self::DomainNotServiced),
9 => Ok(Self::BadRequest),
n if n < 10 => Ok(Self::Other),
_ => Err(ParseStatusError),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum AuthenticationFailure {
CertificateRequired = 0,
UnauthorizedSender = 1,
CertificateInvalid = 2,
IdentityMismatch = 3,
ProofRequired = 4,
Other,
}
impl TryFrom<u8> for AuthenticationFailure {
type Error = ParseStatusError;
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
0 => Ok(Self::CertificateRequired),
1 => Ok(Self::UnauthorizedSender),
2 => Ok(Self::CertificateInvalid),
3 => Ok(Self::IdentityMismatch),
4 => Ok(Self::ProofRequired),
n if n < 10 => Ok(Self::Other),
_ => Err(ParseStatusError),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parse_status_success() {
let status = Status::try_from(21).unwrap();
assert_eq!(status, Status::Success);
}
#[test]
fn parse_redirect_err() {
assert!(Redirect::try_from(42).is_err());
}
#[test]
fn parse_status_redirect() {
assert_eq!(
Status::try_from(30).unwrap(),
Status::Redirect(Redirect::Temporary)
);
assert_eq!(
Status::try_from(31).unwrap(),
Status::Redirect(Redirect::Permanent)
);
assert_eq!(
Status::try_from(32).unwrap(),
Status::Redirect(Redirect::Other)
);
}
#[test]
fn parse_temporary_err() {
assert!(TemporaryFailure::try_from(42).is_err());
}
#[test]
fn parse_status_temporary() {
assert_eq!(
Status::try_from(40).unwrap(),
Status::TemporaryFailure(TemporaryFailure::TemporaryError)
);
assert_eq!(
Status::try_from(41).unwrap(),
Status::TemporaryFailure(TemporaryFailure::ServerUnavailable)
);
assert_eq!(
Status::try_from(42).unwrap(),
Status::TemporaryFailure(TemporaryFailure::CgiError)
);
assert_eq!(
Status::try_from(43).unwrap(),
Status::TemporaryFailure(TemporaryFailure::ProxyError)
);
assert_eq!(
Status::try_from(44).unwrap(),
Status::TemporaryFailure(TemporaryFailure::RateLimit)
);
assert_eq!(
Status::try_from(45).unwrap(),
Status::TemporaryFailure(TemporaryFailure::MailboxFull)
);
assert_eq!(
Status::try_from(46).unwrap(),
Status::TemporaryFailure(TemporaryFailure::Other)
);
}
#[test]
fn parse_permanent_err() {
assert!(PermanentFailure::try_from(42).is_err());
}
#[test]
fn parse_status_permanent() {
assert_eq!(
Status::try_from(50).unwrap(),
Status::PermanentFailure(PermanentFailure::PermanentError)
);
assert_eq!(
Status::try_from(51).unwrap(),
Status::PermanentFailure(PermanentFailure::MailboxNonexistent)
);
assert_eq!(
Status::try_from(52).unwrap(),
Status::PermanentFailure(PermanentFailure::MailboxGone)
);
assert_eq!(
Status::try_from(53).unwrap(),
Status::PermanentFailure(PermanentFailure::DomainNotServiced)
);
assert_eq!(
Status::try_from(59).unwrap(),
Status::PermanentFailure(PermanentFailure::BadRequest)
);
assert_eq!(
Status::try_from(57).unwrap(),
Status::PermanentFailure(PermanentFailure::Other)
);
}
#[test]
fn parse_auth_err() {
assert!(AuthenticationFailure::try_from(42).is_err());
}
#[test]
fn parse_status_auth() {
assert_eq!(
Status::try_from(60).unwrap(),
Status::AuthenticationFailure(AuthenticationFailure::CertificateRequired)
);
assert_eq!(
Status::try_from(61).unwrap(),
Status::AuthenticationFailure(AuthenticationFailure::UnauthorizedSender)
);
assert_eq!(
Status::try_from(62).unwrap(),
Status::AuthenticationFailure(AuthenticationFailure::CertificateInvalid)
);
assert_eq!(
Status::try_from(63).unwrap(),
Status::AuthenticationFailure(AuthenticationFailure::IdentityMismatch)
);
assert_eq!(
Status::try_from(64).unwrap(),
Status::AuthenticationFailure(AuthenticationFailure::ProofRequired)
);
assert_eq!(
Status::try_from(65).unwrap(),
Status::AuthenticationFailure(AuthenticationFailure::Other)
);
}
}