cratefield_core/ports/rate_limiter.rs
1//! The `RateLimiter` port (architecture section 5).
2
3use async_trait::async_trait;
4use std::time::Duration;
5use thiserror::Error;
6
7/// The budget a key spends against, when the limiter knows one (issue
8/// #538). A Workers Rate Limiting binding does not; a D1-backed fixed
9/// window does. `limit` is the budget per window, `remaining` what is
10/// left of the current one, and `reset` how long until it turns over —
11/// the three numbers `rate_limited` puts on the `RateLimit-*` headers.
12#[derive(Debug, Clone, PartialEq, Eq)]
13pub struct Quota {
14 pub limit: u32,
15 pub remaining: u32,
16 pub reset: Duration,
17}
18
19#[derive(Debug, Clone, PartialEq, Eq)]
20pub struct Decision {
21 pub ok: bool,
22 pub retry_after: Option<Duration>,
23 /// The quota this decision was taken against, when the limiter
24 /// reports one. `None` for limiters that cannot know (the Workers
25 /// binding, Redis sliding windows without a count).
26 pub quota: Option<Quota>,
27}
28
29#[derive(Debug, Clone, Error)]
30pub enum RateLimitError {
31 #[error("rate limiter transport error: {0}")]
32 Transport(String),
33}
34
35#[async_trait]
36pub trait RateLimiter: Send + Sync {
37 async fn limit(&self, key: &str) -> Result<Decision, RateLimitError>;
38}