#![deny(missing_docs)]
use std::time::Duration;
#[derive(Debug, Clone, Copy)]
pub struct Policy {
pub max_attempts: u32,
pub base: Duration,
pub max: Duration,
pub jitter: bool,
}
impl Default for Policy {
fn default() -> Self {
Self {
max_attempts: 3,
base: Duration::from_millis(250),
max: Duration::from_secs(8),
jitter: true,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Decision {
Retry(Duration),
GiveUp,
}
impl Policy {
pub fn next(&self, attempt: u32) -> Decision {
if attempt >= self.max_attempts {
return Decision::GiveUp;
}
let exp_factor = 2u32.saturating_pow(attempt.saturating_sub(1));
let nanos = self
.base
.as_nanos()
.saturating_mul(exp_factor as u128);
let nanos = nanos.min(self.max.as_nanos());
let mut d = Duration::from_nanos(nanos.min(u128::from(u64::MAX)) as u64);
if self.jitter {
let mut s = (attempt as u64).wrapping_mul(0x9E3779B97F4A7C15);
s ^= d.as_nanos() as u64;
s = (s ^ (s >> 30)).wrapping_mul(0xBF58476D1CE4E5B9);
s = (s ^ (s >> 27)).wrapping_mul(0x94D049BB133111EB);
s = s ^ (s >> 31);
let frac = (s % 1000) as f64 / 1000.0; let jitter_factor = 0.75 + frac * 0.5; let scaled = (d.as_nanos() as f64 * jitter_factor) as u64;
d = Duration::from_nanos(scaled);
}
Decision::Retry(d)
}
}