use crate::client::ReconnectionConfig;
use rand::{RngExt, rng};
use std::cmp;
pub(crate) struct ReconnectionState {
config: ReconnectionConfig,
attempts: u32,
}
impl ReconnectionState {
pub(crate) fn new(config: ReconnectionConfig) -> Self {
Self {
config,
attempts: 0,
}
}
pub(crate) fn reset_attempts(&mut self) {
self.attempts = 0;
}
#[expect(
clippy::arithmetic_side_effects,
reason = "`incr_with_max` answered `Some` on the line above, so the attempt \
count is at least 1."
)]
pub(crate) fn next_delay(&mut self) -> Option<u64> {
match &self.config {
ReconnectionConfig::Constant {
delay,
max_attempts,
jitter,
} => {
self.attempts = incr_with_max(self.attempts, *max_attempts)?;
Some(add_jitter(u64::from(*delay), *jitter))
}
ReconnectionConfig::Linear {
max_delay,
max_attempts,
delay,
jitter,
} => {
self.attempts = incr_with_max(self.attempts, *max_attempts)?;
let delay = u64::from(*delay).saturating_mul(u64::from(self.attempts));
Some(cmp::min(u64::from(*max_delay), add_jitter(delay, *jitter)))
}
ReconnectionConfig::Exponential {
min_delay,
max_delay,
max_attempts,
multiplicative_factor,
jitter,
} => {
self.attempts = incr_with_max(self.attempts, *max_attempts)?;
let delay = u64::from(*multiplicative_factor)
.saturating_pow(self.attempts - 1)
.saturating_mul(u64::from(*min_delay));
Some(cmp::min(u64::from(*max_delay), add_jitter(delay, *jitter)))
}
}
}
}
fn incr_with_max(curr: u32, max: u32) -> Option<u32> {
if max != 0 && curr >= max {
None
} else {
Some(curr.saturating_add(1))
}
}
fn add_jitter(delay: u64, jitter: u32) -> u64 {
if jitter == 0 {
delay
} else {
delay.saturating_add(rng().random_range(0..u64::from(jitter)))
}
}