use std::time::Duration;
#[derive(Clone, Debug)]
pub struct VibeRetryPolicy {
max_retries: u32,
initial_backoff: Duration,
max_backoff: Duration,
backoff_multiplier: f64,
retry_on_status: bool,
jitter: bool,
}
impl VibeRetryPolicy {
pub fn none() -> Self {
Self {
max_retries: 0,
initial_backoff: Duration::from_millis(0),
max_backoff: Duration::from_millis(0),
backoff_multiplier: 1.0,
retry_on_status: false,
jitter: false,
}
}
pub fn max_retries(mut self, value: u32) -> Self {
self.max_retries = value;
self
}
pub fn initial_backoff(mut self, value: Duration) -> Self {
self.initial_backoff = value;
self
}
pub fn max_backoff(mut self, value: Duration) -> Self {
self.max_backoff = value;
self
}
pub fn backoff_multiplier(mut self, value: f64) -> Self {
self.backoff_multiplier = value;
self
}
pub fn retry_on_status(mut self, value: bool) -> Self {
self.retry_on_status = value;
self
}
pub fn jitter(mut self, value: bool) -> Self {
self.jitter = value;
self
}
pub fn max_retries_value(&self) -> u32 {
self.max_retries
}
pub fn jitter_enabled(&self) -> bool {
self.jitter
}
pub fn max_backoff_value(&self) -> Duration {
self.max_backoff
}
pub fn should_retry_status(&self, status: u16) -> bool {
self.retry_on_status && matches!(status, 408 | 429 | 500 | 502 | 503 | 504)
}
pub fn backoff_for(&self, attempt: u32) -> Duration {
if self.initial_backoff.is_zero() {
return Duration::from_millis(0);
}
let factor = self.backoff_multiplier.max(1.0).powi(attempt as i32);
let computed = self.initial_backoff.as_millis() as f64 * factor;
let capped = computed.min(self.max_backoff.as_millis() as f64);
Duration::from_millis(capped as u64)
}
pub fn apply_jitter(&self, base: Duration, seed: u64) -> Duration {
if !self.jitter || base.is_zero() {
return base;
}
let mut x = seed | 1;
x ^= x << 13;
x ^= x >> 7;
x ^= x << 17;
let frac = (x % 1000) as f64 / 1000.0;
Duration::from_millis((base.as_millis() as f64 * frac) as u64)
}
}
impl Default for VibeRetryPolicy {
fn default() -> Self {
Self {
max_retries: 2,
initial_backoff: Duration::from_millis(200),
max_backoff: Duration::from_secs(5),
backoff_multiplier: 2.0,
retry_on_status: true,
jitter: true,
}
}
}
#[cfg(test)]
mod strict_tests {
use super::*;
include!(concat!(
env!("CARGO_MANIFEST_DIR"),
"/test/unit/net/retry_tests.rs"
));
}