use std::time::Duration;
#[derive(Debug, Clone, PartialEq)]
pub struct SupervisionConfig {
pub max_attempts: Option<u32>,
pub initial_delay: Duration,
pub backoff_multiplier: f64,
pub max_delay: Duration,
}
impl SupervisionConfig {
pub fn next_delay(&self, attempt: u32) -> Duration {
if attempt == 0 {
return self.initial_delay;
}
let exp = ((attempt - 1) as i32).min(63);
let exp_value = self.backoff_multiplier.powi(exp);
let millis = (self.initial_delay.as_millis() as f64 * exp_value) as u128;
let delay = Duration::from_millis(millis as u64);
delay.min(self.max_delay)
}
}
impl Default for SupervisionConfig {
fn default() -> Self {
Self {
max_attempts: Some(5),
initial_delay: Duration::from_secs(1),
backoff_multiplier: 2.0,
max_delay: Duration::from_secs(60),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::time::Duration;
#[test]
fn test_supervision_config_defaults() {
let cfg = SupervisionConfig::default();
assert_eq!(cfg.max_attempts, Some(5));
assert_eq!(cfg.initial_delay, Duration::from_secs(1));
assert_eq!(cfg.backoff_multiplier, 2.0);
assert_eq!(cfg.max_delay, Duration::from_secs(60));
}
#[test]
fn test_supervision_config_infinite() {
let cfg = SupervisionConfig {
max_attempts: None,
..Default::default()
};
assert!(cfg.max_attempts.is_none());
}
#[test]
fn test_supervision_config_next_delay_growth() {
let cfg = SupervisionConfig::default();
let d1 = cfg.next_delay(1); let d2 = cfg.next_delay(2); let d3 = cfg.next_delay(3); assert_eq!(d1, Duration::from_secs(1));
assert_eq!(d2, Duration::from_secs(2));
assert_eq!(d3, Duration::from_secs(4));
}
#[test]
fn test_supervision_config_next_delay_capped() {
let cfg = SupervisionConfig {
max_delay: Duration::from_secs(5),
..Default::default()
};
let d = cfg.next_delay(10); assert_eq!(d, Duration::from_secs(5));
}
#[test]
fn test_next_delay_attempt_zero_returns_initial_delay() {
let cfg = SupervisionConfig::default();
let d = cfg.next_delay(0);
assert_eq!(d, cfg.initial_delay);
}
}