use std::str::FromStr;
use headers::HeaderValue;
use time::{Date, format_description::well_known::Rfc2822};
use crate::{
casesensitive_headermap::CaseSensitiveHeaderMap,
reset_time::{ResetTime, ResetTimeKind},
};
use super::error::{Error, Result};
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct RateLimit {
pub reset: ResetTime,
}
impl RateLimit {
pub fn new<T: Into<CaseSensitiveHeaderMap>>(headers: T) -> std::result::Result<Self, Error> {
let headers = headers.into();
let reset = match Self::get_retry_after_header(&headers) {
Some(retry_after) => {
if Date::parse(retry_after.to_str()?, &Rfc2822).is_ok() {
ResetTime::new(retry_after, ResetTimeKind::ImfFixdate)?
} else {
ResetTime::new(retry_after, ResetTimeKind::Seconds)?
}
}
None => return Err(Error::MissingRetryAfter),
};
Ok(RateLimit { reset })
}
fn get_retry_after_header(header_map: &CaseSensitiveHeaderMap) -> Option<&HeaderValue> {
header_map
.get("Retry-After")
.or_else(|| header_map.get("retry-after"))
}
#[must_use]
pub const fn reset(&self) -> ResetTime {
self.reset
}
}
impl FromStr for RateLimit {
type Err = Error;
fn from_str(map: &str) -> Result<Self> {
RateLimit::new(CaseSensitiveHeaderMap::from_str(map)?)
}
}
#[cfg(test)]
mod tests {
use super::*;
use indoc::indoc;
use time::macros::datetime;
#[test]
fn parse_retry_after_seconds() {
let map = CaseSensitiveHeaderMap::from_str("Retry-After: 30").unwrap();
let retry = RateLimit::get_retry_after_header(&map).unwrap();
assert_eq!("30", retry);
}
#[test]
fn retry_after_seconds() {
let headers = indoc! {"
Retry-After: 19
"};
let rate = RateLimit::from_str(headers).unwrap();
assert_eq!(rate.reset(), ResetTime::Seconds(19));
}
#[test]
fn retry_after_seconds_case_sensitive() {
let headers = indoc! {"
retry-after: 19
"};
let rate = RateLimit::from_str(headers).unwrap();
assert_eq!(rate.reset(), ResetTime::Seconds(19));
}
#[test]
fn retry_after_imf_fixdate() {
let headers = indoc! {"
Retry-After: Fri, 31 Dec 1999 23:59:59 GMT
"};
let rate = RateLimit::from_str(headers).unwrap();
assert_eq!(
rate.reset(),
ResetTime::DateTime(datetime!(1999-12-31 23:59:59 UTC))
);
}
}