use std::ops::RangeInclusive;
use std::time::Duration;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct HostDelay {
min: Duration,
max: Duration,
}
impl HostDelay {
pub const DEFAULT_MAX: Duration = Duration::from_mins(1);
#[must_use]
pub fn new(min: Duration, max: Duration) -> Self {
Self {
min,
max: max.max(min),
}
}
#[must_use]
pub const fn min(self) -> Duration {
self.min
}
#[must_use]
pub const fn max(self) -> Duration {
self.max
}
#[cfg(feature = "robots")]
pub(crate) fn raise(&mut self, min: Duration) {
*self = Self::new(self.min.max(min), self.max);
}
pub(crate) fn grow(self, gap: Duration) -> Duration {
(gap * 2).max(self.base()).min(self.max)
}
pub(crate) fn shrink(self, gap: Duration) -> Duration {
let half = gap / 2;
if half < self.base() { self.min } else { half }
}
fn base(self) -> Duration {
if self.min.is_zero() {
Duration::from_secs(1)
} else {
self.min
}
}
}
impl Default for HostDelay {
fn default() -> Self {
Self::from(Duration::ZERO)
}
}
impl From<Duration> for HostDelay {
fn from(min: Duration) -> Self {
Self::new(min, Self::DEFAULT_MAX)
}
}
impl From<RangeInclusive<Duration>> for HostDelay {
fn from(range: RangeInclusive<Duration>) -> Self {
Self::new(*range.start(), *range.end())
}
}
#[cfg(test)]
mod tests {
use super::*;
const fn ms(ms: u64) -> Duration {
Duration::from_millis(ms)
}
#[test]
fn converts_from_a_minimum_or_a_range() {
assert_eq!(
HostDelay::from(ms(200)),
HostDelay::new(ms(200), HostDelay::DEFAULT_MAX)
);
assert_eq!(HostDelay::from(ms(200)..=ms(300)).max(), ms(300));
assert_eq!(HostDelay::from(ms(500)..=ms(300)).max(), ms(500), "fixed");
assert_eq!(HostDelay::default(), HostDelay::from(Duration::ZERO));
}
#[test]
fn grows_from_the_minimum_and_shrinks_back() {
let delay = HostDelay::from(ms(200)..=ms(1000));
let mut gap = delay.min();
let mut gaps = Vec::new();
for _ in 0..4 {
gap = delay.grow(gap);
gaps.push(gap);
}
assert_eq!(gaps, [ms(400), ms(800), ms(1000), ms(1000)]);
gaps.clear();
for _ in 0..4 {
gap = delay.shrink(gap);
gaps.push(gap);
}
assert_eq!(gaps, [ms(500), ms(250), ms(200), ms(200)]);
}
#[test]
fn grows_from_a_second_without_a_minimum() {
let delay = HostDelay::default();
assert_eq!(delay.grow(Duration::ZERO), Duration::from_secs(1));
assert_eq!(delay.grow(Duration::from_secs(1)), Duration::from_secs(2));
assert_eq!(delay.grow(Duration::from_secs(50)), HostDelay::DEFAULT_MAX);
assert_eq!(delay.shrink(Duration::from_secs(1)), Duration::ZERO);
let fixed = HostDelay::from(ms(200)..=ms(200));
assert_eq!(fixed.grow(ms(200)), ms(200));
}
}