use std::sync::Arc;
use std::time::Instant;
use crate::metrics::{AtomicRateMetrics, RateMetrics};
use crate::outcome::RateLimitResult;
use crate::quota::Quota;
use crate::state::AtomicGcraState;
struct RateLimiterShared {
state: AtomicGcraState,
quota: Quota,
epoch: Instant,
metrics: AtomicRateMetrics,
}
#[derive(Clone)]
pub struct RateLimiter {
inner: Arc<RateLimiterShared>,
}
impl RateLimiter {
pub fn new(quota: Quota) -> Self {
let epoch = Instant::now();
Self {
inner: Arc::new(RateLimiterShared {
state: AtomicGcraState::new(0),
quota,
epoch,
metrics: AtomicRateMetrics::new(),
}),
}
}
pub fn per_second(requests: u32) -> Self {
Self::new(Quota::per_second(requests))
}
pub fn per_minute(requests: u32) -> Self {
Self::new(Quota::per_minute(requests))
}
#[inline]
fn now_nanos(&self) -> u64 {
self.inner.epoch.elapsed().as_nanos() as u64
}
#[inline]
pub fn check(&self) -> RateLimitResult {
self.check_n(1)
}
pub fn check_n(&self, count: u32) -> RateLimitResult {
let outcome = self
.inner
.state
.check(self.now_nanos(), &self.inner.quota, count);
if outcome.is_allowed() {
self.inner.metrics.record_allowed();
} else {
self.inner.metrics.record_limited();
}
outcome
}
#[inline]
pub fn quota(&self) -> &Quota {
&self.inner.quota
}
#[inline]
pub fn metrics(&self) -> RateMetrics {
self.inner.metrics.snapshot()
}
#[inline]
pub fn reset_metrics(&self) {
self.inner.metrics.reset();
}
}