use std::{
num::NonZeroU32,
sync::{Arc, OnceLock},
};
use governor::{
Quota, RateLimiter,
clock::DefaultClock,
state::{InMemoryState, NotKeyed},
};
pub type ForensicLimiter =
RateLimiter<NotKeyed, InMemoryState, DefaultClock, governor::middleware::NoOpMiddleware>;
#[must_use]
pub fn default_forensic_quota() -> Quota {
#[allow(clippy::unwrap_used)]
let per_sec = NonZeroU32::new(1).unwrap();
#[allow(clippy::unwrap_used)]
let burst = NonZeroU32::new(5).unwrap();
Quota::per_second(per_sec).allow_burst(burst)
}
pub fn ensure_limiter(
slot: &'static OnceLock<Arc<ForensicLimiter>>,
) -> &'static Arc<ForensicLimiter> {
slot.get_or_init(|| Arc::new(RateLimiter::direct(default_forensic_quota())))
}
pub fn try_acquire_forensic(slot: &'static OnceLock<Arc<ForensicLimiter>>) -> bool {
let l = ensure_limiter(slot);
l.check().is_ok()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_default_quota_should_allow_burst() {
static SLOT: OnceLock<Arc<ForensicLimiter>> = OnceLock::new();
assert!(try_acquire_forensic(&SLOT));
}
#[test]
fn test_per_callsite_isolation() {
static A: OnceLock<Arc<ForensicLimiter>> = OnceLock::new();
static B: OnceLock<Arc<ForensicLimiter>> = OnceLock::new();
let mut fires_a = 0;
for _ in 0..5 {
if try_acquire_forensic(&A) {
fires_a += 1;
}
}
let mut fires_b = 0;
for _ in 0..5 {
if try_acquire_forensic(&B) {
fires_b += 1;
}
}
assert_eq!(fires_a, 5);
assert_eq!(fires_b, 5);
}
}