use std::fmt;
pub mod guard;
pub mod store;
pub use crate::session::StoreError;
pub use guard::Throttle;
pub use store::{MemoryThrottleStore, ThrottleStore};
#[derive(Debug, Clone)]
pub struct ThrottleConfig {
pub threshold: u32,
pub window_secs: u64,
pub ban_secs: u64,
}
impl Default for ThrottleConfig {
fn default() -> Self {
Self {
threshold: 5,
window_secs: 60,
ban_secs: 900,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ThrottleDecision {
Allow { remaining: u32 },
Banned { until: u64 },
Unavailable,
}
impl fmt::Display for ThrottleDecision {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ThrottleDecision::Allow { .. } => write!(f, "ALLOW"),
ThrottleDecision::Banned { .. } => write!(f, "BANNED"),
ThrottleDecision::Unavailable => write!(f, "UNAVAILABLE"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ThrottleOutcome {
Allow { remaining: u32 },
Banned { until: u64 },
}
impl fmt::Display for ThrottleOutcome {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ThrottleOutcome::Allow { .. } => write!(f, "ALLOW"),
ThrottleOutcome::Banned { .. } => write!(f, "BANNED"),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn config_defaults_match_spec() {
let c = ThrottleConfig::default();
assert_eq!(c.threshold, 5, "got {:?}", c);
assert_eq!(c.window_secs, 60, "got {:?}", c);
assert_eq!(c.ban_secs, 900, "got {:?}", c);
}
#[test]
fn decision_and_outcome_display_uppercase() {
assert_eq!(
ThrottleDecision::Allow { remaining: 3 }.to_string(),
"ALLOW"
);
assert_eq!(ThrottleDecision::Banned { until: 7 }.to_string(), "BANNED");
assert_eq!(ThrottleDecision::Unavailable.to_string(), "UNAVAILABLE");
assert_eq!(ThrottleOutcome::Allow { remaining: 3 }.to_string(), "ALLOW");
assert_eq!(ThrottleOutcome::Banned { until: 7 }.to_string(), "BANNED");
}
}