use super::http_date::parse_http_date;
#[derive(Clone, Copy, Debug, Default, Eq, Ord, PartialEq, PartialOrd)]
pub struct DelaySeconds(u64);
impl DelaySeconds {
#[must_use]
pub const fn new(seconds: u64) -> Self {
Self(seconds)
}
#[must_use]
pub const fn get(self) -> u64 {
self.0
}
}
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub struct WallClockTimestamp(u64);
impl WallClockTimestamp {
#[must_use]
pub const fn new(epoch_seconds: u64) -> Self {
Self(epoch_seconds)
}
#[must_use]
pub const fn get(self) -> u64 {
self.0
}
}
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub struct HttpDate(i64);
impl HttpDate {
pub(crate) const fn new(epoch_seconds: i64) -> Self {
Self(epoch_seconds)
}
#[must_use]
pub const fn epoch_seconds(self) -> i64 {
self.0
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum RetryAfter {
Delay(DelaySeconds),
HttpDate(HttpDate),
}
impl RetryAfter {
pub fn parse(value: &[u8], now: WallClockTimestamp) -> Result<Self, RetryAfterError> {
if value.is_empty() {
return Err(RetryAfterError::Empty);
}
if value.iter().all(u8::is_ascii_digit) {
return parse_decimal(value).map(DelaySeconds::new).map(Self::Delay);
}
parse_http_date(value, now).map(Self::HttpDate)
}
}
fn parse_decimal(value: &[u8]) -> Result<u64, RetryAfterError> {
let mut parsed = 0_u64;
for byte in value {
parsed = parsed
.checked_mul(10)
.and_then(|current| current.checked_add(u64::from(*byte & 0x0f)))
.ok_or(RetryAfterError::Overflow)?;
}
Ok(parsed)
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum RetryAfterError {
Empty,
Overflow,
InvalidSyntax,
InvalidDate,
WeekdayMismatch,
}
impl_static_error!(RetryAfterError,
Self::Empty => "Retry-After value is empty",
Self::Overflow => "Retry-After value exceeds its numeric range",
Self::InvalidSyntax => "Retry-After syntax is invalid",
Self::InvalidDate => "Retry-After date is invalid",
Self::WeekdayMismatch => "Retry-After weekday does not match its date",
);