use super::{EndpointRateLimit, RateLimitDecision};
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Step {
Blocked { retry_after: f64 },
Allowed { count: f64, reset: bool },
}
impl From<Step> for Option<RateLimitDecision> {
fn from(step: Step) -> Self {
match step {
Step::Blocked { retry_after } => Some(RateLimitDecision::Blocked { retry_after }),
Step::Allowed { .. } => None,
}
}
}
#[must_use]
pub fn step(count: f64, last_request: i64, now: i64, rule: &EndpointRateLimit) -> Step {
let elapsed = chrono::Duration::milliseconds(now.saturating_sub(last_request))
.to_std()
.map_or(0.0, |duration| duration.as_secs_f64());
let expired = elapsed >= rule.window_seconds;
if !expired
&& (rule.window_seconds.is_nan()
|| rule.max_requests.is_nan()
|| count >= rule.max_requests)
{
return Step::Blocked {
retry_after: (rule.window_seconds - elapsed).ceil(),
};
}
Step::Allowed {
count: if expired { 1.0 } else { count + 1.0 },
reset: expired,
}
}