use std::time::Duration;
use serde::{Deserialize, Serialize};
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case", tag = "class")]
pub enum FailureClass {
Transient,
Permanent,
Ambiguous,
RateLimited {
retry_after: Option<Duration>,
},
Authentication,
Authorization,
Validation,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Disposition {
Retry,
Fail,
Unknown,
}
impl FailureClass {
pub const fn disposition(self) -> Disposition {
match self {
Self::Transient | Self::RateLimited { .. } => Disposition::Retry,
Self::Ambiguous => Disposition::Unknown,
Self::Permanent | Self::Authentication | Self::Authorization | Self::Validation => {
Disposition::Fail
}
}
}
pub const fn retry_after(self) -> Option<Duration> {
match self {
Self::RateLimited { retry_after } => retry_after,
_ => None,
}
}
#[must_use]
pub const fn with_request_sent(self, request_sent: Option<bool>) -> Self {
match (self, request_sent) {
(Self::Ambiguous, Some(false)) => Self::Transient,
_ => self,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn unsent_requests_are_never_ambiguous() {
assert_eq!(
FailureClass::Ambiguous.with_request_sent(Some(false)),
FailureClass::Transient
);
assert_eq!(
FailureClass::Ambiguous.with_request_sent(Some(true)),
FailureClass::Ambiguous
);
assert_eq!(
FailureClass::Ambiguous.with_request_sent(None),
FailureClass::Ambiguous
);
assert_eq!(
FailureClass::Transient.with_request_sent(Some(true)),
FailureClass::Transient
);
}
#[test]
fn dispositions() {
assert_eq!(FailureClass::Ambiguous.disposition(), Disposition::Unknown);
assert_eq!(
FailureClass::RateLimited { retry_after: None }.disposition(),
Disposition::Retry
);
assert_eq!(
FailureClass::Authentication.disposition(),
Disposition::Fail
);
}
}