use std::time::Duration;
#[derive(Debug, Clone)]
pub struct AutoReconnect {
pub enabled: bool,
pub max_attempts: Option<u32>,
pub initial_backoff: Duration,
pub max_backoff: Duration,
pub backoff_multiplier: f64,
}
impl Default for AutoReconnect {
fn default() -> Self {
Self {
enabled: true,
max_attempts: Some(5),
initial_backoff: Duration::from_secs(1),
max_backoff: Duration::from_secs(30),
backoff_multiplier: 2.0,
}
}
}
impl AutoReconnect {
pub(crate) fn backoff_for_attempt(&self, attempt: u32) -> Duration {
let base_secs = self.initial_backoff.as_secs_f64();
let factor = self
.backoff_multiplier
.powi(attempt.saturating_sub(1).cast_signed());
let raw = base_secs * factor;
let capped = raw.min(self.max_backoff.as_secs_f64());
Duration::from_secs_f64(capped)
}
}
#[derive(Debug, Clone)]
pub enum StreamingEvent {
Reconnected {
attempt: u32,
},
ReconnectFailed {
attempts: u32,
error: String,
},
Disconnected {
reason: Option<String>,
},
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn default_policy_is_sensible() {
let p = AutoReconnect::default();
assert!(p.enabled);
assert_eq!(p.max_attempts, Some(5));
assert_eq!(p.initial_backoff, Duration::from_secs(1));
assert_eq!(p.max_backoff, Duration::from_secs(30));
assert!((p.backoff_multiplier - 2.0).abs() < f64::EPSILON);
}
#[test]
fn backoff_grows_and_caps() {
let p = AutoReconnect {
initial_backoff: Duration::from_secs(1),
max_backoff: Duration::from_secs(30),
backoff_multiplier: 2.0,
..AutoReconnect::default()
};
assert_eq!(p.backoff_for_attempt(1), Duration::from_secs(1));
assert_eq!(p.backoff_for_attempt(2), Duration::from_secs(2));
assert_eq!(p.backoff_for_attempt(3), Duration::from_secs(4));
assert_eq!(p.backoff_for_attempt(4), Duration::from_secs(8));
assert_eq!(p.backoff_for_attempt(5), Duration::from_secs(16));
assert_eq!(p.backoff_for_attempt(6), Duration::from_secs(30));
assert_eq!(p.backoff_for_attempt(10), Duration::from_secs(30));
}
#[test]
fn disabled_policy_compiles() {
let p = AutoReconnect {
enabled: false,
..AutoReconnect::default()
};
assert!(!p.enabled);
}
}