use core::marker::PhantomData;
pub mod direct;
mod in_memory;
pub mod keyed;
pub use self::in_memory::InMemoryState;
use crate::nanos::Nanos;
use crate::{clock, Quota};
use crate::{
gcra::Gcra,
middleware::{NoOpMiddleware, RateLimitingMiddleware},
};
pub use direct::*;
pub trait StateStore {
type Key;
fn measure_and_replace<T, F, E>(&self, key: &Self::Key, f: F) -> Result<T, E>
where
F: Fn(Option<Nanos>) -> Result<(T, Nanos), E>;
}
#[derive(Debug)]
pub struct RateLimiter<K, S, C, MW = NoOpMiddleware>
where
S: StateStore<Key = K>,
C: clock::Clock,
MW: RateLimitingMiddleware<C::Instant>,
{
state: S,
gcra: Gcra,
clock: C,
start: C::Instant,
middleware: PhantomData<MW>,
}
impl<K, S, C, MW> RateLimiter<K, S, C, MW>
where
S: StateStore<Key = K>,
C: clock::Clock,
MW: RateLimitingMiddleware<C::Instant>,
{
pub fn new(quota: Quota, state: S, clock: C) -> Self {
let gcra = Gcra::new(quota);
let start = clock.now();
RateLimiter {
state,
clock,
gcra,
start,
middleware: PhantomData,
}
}
pub fn into_state_store(self) -> S {
self.state
}
pub fn clock(&self) -> &C {
&self.clock
}
}
impl<K, S, C, MW> RateLimiter<K, S, C, MW>
where
S: StateStore<Key = K>,
C: clock::Clock,
MW: RateLimitingMiddleware<C::Instant>,
{
pub fn with_middleware<Outer: RateLimitingMiddleware<C::Instant>>(
self,
) -> RateLimiter<K, S, C, Outer> {
RateLimiter {
middleware: PhantomData,
state: self.state,
gcra: self.gcra,
clock: self.clock,
start: self.start,
}
}
}
#[cfg(feature = "std")]
impl<K, S, C, MW> RateLimiter<K, S, C, MW>
where
S: StateStore<Key = K>,
C: clock::ReasonablyRealtime,
MW: RateLimitingMiddleware<C::Instant>,
{
pub(crate) fn reference_reading(&self) -> C::Instant {
self.clock.reference_point()
}
}
#[cfg(all(feature = "std", test))]
mod test {
use super::*;
use crate::Quota;
use assertables::assert_gt;
use nonzero_ext::nonzero;
#[test]
fn ratelimiter_impl_coverage() {
let lim = RateLimiter::direct(Quota::per_second(nonzero!(3u32)));
assert_gt!(format!("{lim:?}").len(), 0);
}
}