use std::{sync::Arc, time::Duration};
use crate::{BucketSize, HardLimitFactor, SuppressionFactorCachePeriod, TrypemaError, WindowSize};
pub trait RateLimiterBuilder: Sized {
type Provider;
fn window_size(self, value: WindowSize) -> Self;
fn bucket_size(self, value: BucketSize) -> Self;
fn hard_limit_factor(self, value: HardLimitFactor) -> Self;
fn suppression_factor_cache_period(self, value: SuppressionFactorCachePeriod) -> Self;
fn stale_after(self, value: Duration) -> Self;
fn cleanup_interval(self, value: Duration) -> Self;
fn disable_cleanup(self) -> Self {
self.cleanup_enabled(false)
}
fn enable_cleanup(self) -> Self {
self.cleanup_enabled(true)
}
fn cleanup_enabled(self, enabled: bool) -> Self;
fn build(self) -> Result<Arc<Self::Provider>, TrypemaError>;
}
#[derive(Clone, Copy, Debug, Default)]
pub(crate) struct ProviderConfig {
pub window_size: WindowSize,
pub bucket_size: BucketSize,
pub hard_limit_factor: HardLimitFactor,
pub suppression_factor_cache_period: SuppressionFactorCachePeriod,
}
impl ProviderConfig {
pub(crate) fn validate(self) -> Result<Self, TrypemaError> {
if u128::from(self.bucket_size.as_milliseconds()) > self.window_size.as_milliseconds() {
return Err(TrypemaError::InvalidBucketSize(
"bucket size must be less than or equal to window size".to_string(),
));
}
Ok(self)
}
}
#[derive(Clone, Copy, Debug)]
pub(crate) struct CleanupConfig {
pub enabled: bool,
pub stale_after: Duration,
pub interval: Duration,
}
impl Default for CleanupConfig {
fn default() -> Self {
Self {
enabled: true,
stale_after: Duration::from_secs(10 * 60),
interval: Duration::from_secs(30),
}
}
}
impl CleanupConfig {
pub(crate) fn validate(self) -> Result<Self, TrypemaError> {
validate_cleanup_duration("stale-after duration", self.stale_after)?;
validate_cleanup_duration("cleanup interval", self.interval)?;
Ok(self)
}
pub(crate) fn stale_after_ms(self) -> u64 {
self.stale_after.as_millis() as u64
}
}
fn validate_cleanup_duration(name: &str, value: Duration) -> Result<(), TrypemaError> {
if value.as_millis() == 0 {
return Err(TrypemaError::InvalidCleanupConfiguration(format!(
"{name} must be at least 1 millisecond"
)));
}
if value.as_millis() > u64::MAX as u128 {
return Err(TrypemaError::InvalidCleanupConfiguration(format!(
"{name} must fit in u64 milliseconds"
)));
}
Ok(())
}