use std::{
collections::VecDeque,
time::{Duration, Instant},
};
pub(super) const COOL_DOWN: Duration = Duration::from_secs(30);
pub(super) const MAX_ATTEMPTS_PER_WINDOW: usize = 6;
pub(super) const WINDOW: Duration = Duration::from_hours(1);
#[derive(Debug, Default)]
pub(super) struct RecoveryPolicy {
cool_down_started: Option<Instant>,
attempts: VecDeque<Instant>,
}
impl RecoveryPolicy {
pub(super) const fn new() -> Self {
Self {
cool_down_started: None,
attempts: VecDeque::new(),
}
}
pub(super) const fn record_loss(&mut self, now: Instant) {
self.cool_down_started = Some(now);
}
pub(super) fn may_attempt(&mut self, now: Instant) -> bool {
while self
.attempts
.front()
.is_some_and(|&started| now.saturating_duration_since(started) >= WINDOW)
{
self.attempts.pop_front();
}
let cooled = self
.cool_down_started
.is_some_and(|started| now.saturating_duration_since(started) >= COOL_DOWN);
cooled && self.attempts.len() < MAX_ATTEMPTS_PER_WINDOW
}
pub(super) fn record_attempt(&mut self, now: Instant) {
self.attempts.push_back(now);
self.cool_down_started = Some(now);
}
pub(super) const fn record_recovered(&mut self) {
self.cool_down_started = None;
}
}