use super::{FetchRequest, Middleware};
use crate::error::KumoError;
use governor::{
Quota, RateLimiter as GovernorLimiter,
clock::DefaultClock,
middleware::NoOpMiddleware,
state::{InMemoryState, NotKeyed},
};
use std::num::NonZeroU32;
pub struct RateLimiter {
inner: GovernorLimiter<NotKeyed, InMemoryState, DefaultClock, NoOpMiddleware>,
}
impl RateLimiter {
pub fn per_second(rps: f64) -> Self {
let rate = NonZeroU32::new((rps.ceil() as u32).max(1)).unwrap();
let quota = Quota::per_second(rate);
Self {
inner: GovernorLimiter::direct(quota),
}
}
}
#[async_trait::async_trait]
impl Middleware for RateLimiter {
async fn before_request(&self, request: &mut FetchRequest) -> Result<(), KumoError> {
let start = std::time::Instant::now();
self.inner.until_ready().await;
let delay_ms = start.elapsed().as_millis();
if delay_ms > 0 {
tracing::debug!(
target: "kumo::request",
url = %request.url(),
delay_ms,
"request.rate_limit"
);
}
Ok(())
}
}