use std::time::Instant;
pub use fixed_window::{FixedWindowRateLimiter, InMemoryFixedWindowStore};
pub use gcra::{GcraRateLimiter, InMemoryGcraStore};
pub use sliding_window::{InMemorySlidingWindowStore, SlidingWindowRateLimiter};
pub use token_bucket::{InMemoryTokenBucketStore, TokenBucketRateLimiter};
mod fixed_window;
mod gcra;
mod sliding_window;
mod store;
mod token_bucket;
pub use store::{
FixedWindowParams, FixedWindowStore, GcraParams, GcraStore, SlidingWindowParams,
SlidingWindowStore, TokenBucketParams, TokenBucketStore,
};
const MICROS_PER_SEC: u64 = 1_000_000;
pub trait RateLimiter {
fn check(&self, key: u64) -> bool;
}
pub trait TimeSource: Send + Sync {
fn now_micros(&self) -> u64;
#[inline(always)]
fn now_secs(&self) -> u64 {
self.now_micros() / MICROS_PER_SEC
}
}
#[derive(Debug, Default, Clone, Copy)]
pub struct SystemTimeSource;
impl SystemTimeSource {
#[inline]
fn anchor() -> Instant {
static START: std::sync::OnceLock<Instant> = std::sync::OnceLock::new();
*START.get_or_init(Instant::now)
}
}
impl TimeSource for SystemTimeSource {
#[inline]
fn now_micros(&self) -> u64 {
let elapsed = Self::anchor().elapsed();
elapsed.as_micros().try_into().unwrap_or(u64::MAX)
}
}
#[cfg(test)]
pub(super) mod test_utils {
use super::{MICROS_PER_SEC, TimeSource};
use std::sync::{Arc, Mutex};
#[derive(Clone)]
pub(super) struct MockTimeSource {
current_time: Arc<Mutex<u64>>,
}
impl MockTimeSource {
pub(super) fn new(initial_time: u64) -> Self {
Self {
current_time: Arc::new(Mutex::new(initial_time * MICROS_PER_SEC)),
}
}
pub(super) fn advance(&self, seconds: u64) {
let mut time = self.current_time.lock().unwrap();
*time += seconds * MICROS_PER_SEC;
}
}
impl TimeSource for MockTimeSource {
fn now_micros(&self) -> u64 {
*self.current_time.lock().unwrap()
}
}
}