use std::time::Duration;
pub fn capped_exponential_delay(base: Duration, maximum: Duration, attempt: usize) -> Duration {
if base >= maximum {
return maximum;
}
let Ok(shift) = u32::try_from(attempt) else {
return maximum;
};
let Some(multiplier) = 1_u32.checked_shl(shift) else {
return maximum;
};
base.checked_mul(multiplier).unwrap_or(maximum).min(maximum)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn caps_large_attempts_without_overflow() {
let base = Duration::from_secs(1);
let maximum = Duration::from_secs(60);
assert_eq!(
capped_exponential_delay(base, maximum, 0),
Duration::from_secs(1)
);
assert_eq!(
capped_exponential_delay(base, maximum, 1),
Duration::from_secs(2)
);
assert_eq!(capped_exponential_delay(base, maximum, 31), maximum);
assert_eq!(capped_exponential_delay(base, maximum, 32), maximum);
assert_eq!(capped_exponential_delay(base, maximum, usize::MAX), maximum);
}
#[test]
fn caps_when_base_is_not_smaller_than_maximum() {
assert_eq!(
capped_exponential_delay(Duration::from_secs(90), Duration::from_secs(60), 0,),
Duration::from_secs(60)
);
}
}