use std::time::Duration;
pub type Nanos = u64;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Quota {
max_burst: u32,
window: Duration,
}
impl Quota {
pub fn per_second(count: u32) -> Self {
Self::with_window(count, Duration::from_secs(1))
}
pub fn per_minute(count: u32) -> Self {
Self::with_window(count, Duration::from_secs(60))
}
pub fn per_hour(count: u32) -> Self {
Self::with_window(count, Duration::from_secs(3600))
}
pub fn per_day(count: u32) -> Self {
Self::with_window(count, Duration::from_secs(86_400))
}
pub fn with_window(count: u32, window: Duration) -> Self {
assert!(count > 0, "quota count must be greater than 0");
assert!(!window.is_zero(), "window must be non-zero");
assert!(
window.as_nanos() >= u128::from(count),
"quota cannot allow more than one request per nanosecond"
);
Self {
max_burst: count,
window,
}
}
pub fn unlimited() -> Self {
Self {
max_burst: 0,
window: Duration::ZERO,
}
}
pub fn is_unlimited(&self) -> bool {
self.max_burst == 0
}
pub fn max_burst(&self) -> u32 {
self.max_burst
}
pub fn replenish_interval(&self) -> Duration {
if self.is_unlimited() {
return Duration::ZERO;
}
self.window / self.max_burst
}
pub fn window(&self) -> Duration {
self.window
}
pub fn emission_interval_nanos(&self) -> Nanos {
let nanos_u128 = self.replenish_interval().as_nanos();
if nanos_u128 > u64::MAX as u128 {
u64::MAX
} else {
nanos_u128 as Nanos
}
}
pub fn burst_offset_nanos(&self) -> Nanos {
self.emission_interval_nanos()
.saturating_mul(self.max_burst as Nanos)
}
}