use crate::reset_time::ResetTimeKind;
use std::time::Duration;
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum Vendor {
Generic,
Akamai,
Discord,
Github,
Gitlab,
Linear,
OpenAI,
PolliDraft,
Reddit,
Twilio,
Twitter,
Vimeo,
}
impl Vendor {
pub(crate) const fn bit(self) -> Option<VendorMask> {
Some(match self {
Vendor::Generic => return None,
Vendor::Akamai => VendorMask::AKAMAI,
Vendor::Discord => VendorMask::DISCORD,
Vendor::Github => VendorMask::GITHUB,
Vendor::Gitlab => VendorMask::GITLAB,
Vendor::Linear => VendorMask::LINEAR,
Vendor::OpenAI => VendorMask::OPENAI,
Vendor::PolliDraft => VendorMask::POLLI_DRAFT,
Vendor::Reddit => VendorMask::REDDIT,
Vendor::Twilio => VendorMask::TWILIO,
Vendor::Twitter => VendorMask::TWITTER,
Vendor::Vimeo => VendorMask::VIMEO,
})
}
pub(crate) const fn identifiable() -> &'static [Vendor] {
&[
Vendor::Akamai,
Vendor::Discord,
Vendor::Github,
Vendor::Gitlab,
Vendor::Linear,
Vendor::OpenAI,
Vendor::PolliDraft,
Vendor::Reddit,
Vendor::Twilio,
Vendor::Twitter,
Vendor::Vimeo,
]
}
}
bitflags::bitflags! {
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Default)]
pub struct VendorMask: u64 {
const AKAMAI = 1 << 0;
const DISCORD = 1 << 1;
const GITHUB = 1 << 2;
const GITLAB = 1 << 3;
const LINEAR = 1 << 4;
const OPENAI = 1 << 5;
const POLLI_DRAFT = 1 << 6;
const REDDIT = 1 << 7;
const TWILIO = 1 << 8;
const TWITTER = 1 << 9;
const VIMEO = 1 << 10;
}
}
impl VendorMask {
#[inline]
#[must_use]
pub const fn count(self) -> u32 {
self.bits().count_ones()
}
#[inline]
#[must_use]
pub fn single(self) -> Option<Vendor> {
if self.count() == 1 {
self.vendors().next()
} else {
None
}
}
#[inline]
#[must_use]
pub const fn vendors(self) -> VendorMaskIter {
VendorMaskIter {
mask: self,
index: 0,
}
}
}
impl From<Vendor> for VendorMask {
#[inline]
fn from(vendor: Vendor) -> Self {
vendor.bit().unwrap_or_else(Self::empty)
}
}
impl FromIterator<Vendor> for VendorMask {
fn from_iter<I: IntoIterator<Item = Vendor>>(iter: I) -> Self {
iter.into_iter()
.fold(Self::empty(), |acc, v| acc | Self::from(v))
}
}
#[derive(Debug)]
pub struct VendorMaskIter {
mask: VendorMask,
index: usize,
}
impl Iterator for VendorMaskIter {
type Item = Vendor;
fn next(&mut self) -> Option<Self::Item> {
let vendors = Vendor::identifiable();
while self.index < vendors.len() {
let vendor = vendors[self.index];
self.index += 1;
if let Some(bit) = vendor.bit()
&& self.mask.contains(bit)
{
return Some(vendor);
}
}
None
}
}
#[derive(Clone, Debug)]
pub(crate) struct VendorSpec {
pub vendor: Vendor,
pub limit_header: Option<&'static str>,
pub used_header: Option<&'static str>,
pub remaining_header: &'static str,
pub reset_header: &'static str,
pub extra_headers: &'static [&'static str],
pub reset_kind: ResetTimeKind,
pub duration: Option<Duration>,
}
impl VendorSpec {
#[allow(clippy::too_many_arguments)]
const fn new(
vendor: Vendor,
limit_header: Option<&'static str>,
used_header: Option<&'static str>,
remaining_header: &'static str,
reset_header: &'static str,
extra_headers: &'static [&'static str],
reset_kind: ResetTimeKind,
duration: Option<Duration>,
) -> Self {
Self {
vendor,
limit_header,
used_header,
remaining_header,
reset_header,
extra_headers,
reset_kind,
duration,
}
}
}
pub(crate) static VENDORS: &[VendorSpec] = &[
VendorSpec::new(
Vendor::PolliDraft,
Some("RateLimit-Limit"),
None,
"RateLimit-Remaining",
"RateLimit-Reset",
&[],
ResetTimeKind::Seconds,
None,
),
VendorSpec::new(
Vendor::Reddit,
None,
Some("X-Ratelimit-Used"),
"X-Ratelimit-Remaining",
"X-Ratelimit-Reset",
&[],
ResetTimeKind::Seconds,
Some(Duration::from_secs(600)),
),
VendorSpec::new(
Vendor::Akamai,
Some("X-RateLimit-Limit"),
None,
"X-RateLimit-Remaining",
"X-RateLimit-Next",
&[],
ResetTimeKind::Iso8601,
Some(Duration::from_secs(60)),
),
VendorSpec::new(
Vendor::Discord,
Some("X-RateLimit-Limit"),
None,
"X-RateLimit-Remaining",
"X-RateLimit-Reset",
&[
"X-RateLimit-Reset-After",
"X-RateLimit-Bucket",
"X-RateLimit-Global",
"X-RateLimit-Scope",
],
ResetTimeKind::Timestamp,
None,
),
VendorSpec::new(
Vendor::Github,
Some("x-ratelimit-limit"),
Some("x-ratelimit-used"),
"x-ratelimit-remaining",
"x-ratelimit-reset",
&["x-ratelimit-resource"],
ResetTimeKind::Timestamp,
Some(Duration::from_secs(3600)),
),
VendorSpec::new(
Vendor::Gitlab,
Some("RateLimit-Limit"),
Some("RateLimit-Observed"),
"RateLimit-Remaining",
"RateLimit-Reset",
&["RateLimit-ResetTime", "RateLimit-Name"],
ResetTimeKind::Timestamp,
Some(Duration::from_secs(60)),
),
VendorSpec::new(
Vendor::Linear,
Some("X-RateLimit-Requests-Limit"),
None,
"X-RateLimit-Requests-Remaining",
"X-RateLimit-Requests-Reset",
&[
"X-RateLimit-Complexity-Limit",
"X-RateLimit-Complexity-Remaining",
"X-RateLimit-Complexity-Reset",
],
ResetTimeKind::TimestampMillis,
Some(Duration::from_secs(3600)),
),
VendorSpec::new(
Vendor::OpenAI,
Some("x-ratelimit-limit-requests"),
None,
"x-ratelimit-remaining-requests",
"x-ratelimit-reset-requests",
&[
"x-ratelimit-limit-tokens",
"x-ratelimit-remaining-tokens",
"x-ratelimit-reset-tokens",
],
ResetTimeKind::OpenAiDuration,
None,
),
VendorSpec::new(
Vendor::Twilio,
Some("X-RateLimit-Limit"),
None,
"X-RateLimit-Remaining",
"X-RateLimit-Reset",
&[],
ResetTimeKind::Timestamp,
None,
),
VendorSpec::new(
Vendor::Twitter,
Some("x-rate-limit-limit"),
None,
"x-rate-limit-remaining",
"x-rate-limit-reset",
&[],
ResetTimeKind::Timestamp,
Some(Duration::from_secs(900)),
),
VendorSpec::new(
Vendor::Vimeo,
Some("X-RateLimit-Limit"),
None,
"X-RateLimit-Remaining",
"X-RateLimit-Reset",
&[],
ResetTimeKind::ImfFixdate,
Some(Duration::from_secs(60)),
),
];