use std::sync::Mutex;
use std::time::{Duration, Instant};
use super::errors::McpifyError;
pub struct RateLimiter {
limit: usize,
window: Duration,
timestamps: Mutex<Vec<Instant>>,
}
impl RateLimiter {
pub fn new(limit: usize, window: Duration) -> Self {
Self {
limit,
window,
timestamps: Mutex::new(Vec::new()),
}
}
pub fn acquire(&self) -> Result<(), McpifyError> {
let now = Instant::now();
let mut timestamps = self.timestamps.lock().unwrap();
timestamps.retain(|&t| now.duration_since(t) < self.window);
if timestamps.len() >= self.limit {
return Err(McpifyError::RateLimitExceeded);
}
timestamps.push(now);
Ok(())
}
}
impl Default for RateLimiter {
fn default() -> Self {
Self::new(100, Duration::from_secs(1))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn allows_calls_up_to_the_limit() {
let limiter = RateLimiter::new(3, Duration::from_secs(1));
for _ in 0..3 {
assert!(limiter.acquire().is_ok());
}
}
#[test]
fn rejects_calls_beyond_the_limit_within_the_window() {
let limiter = RateLimiter::new(1, Duration::from_secs(60));
limiter.acquire().unwrap();
let err = limiter.acquire().unwrap_err();
assert_eq!(err.code(), "RATE_LIMIT_EXCEEDED");
}
}