regy 0.1.4

Private-by-default desktop agent for the Regy web interface
use std::time::Duration;

pub struct ReconnectBackoff {
    current: u64,
    max: u64,
}

impl ReconnectBackoff {
    pub fn new(max_seconds: u64) -> Self {
        Self {
            current: 1,
            max: max_seconds.max(1),
        }
    }

    pub fn reset(&mut self) {
        self.current = 1;
    }

    pub fn next_delay(&mut self) -> Duration {
        let delay = self.current;
        self.current = self.current.saturating_mul(2).min(self.max);
        Duration::from_secs(delay)
    }
}

#[cfg(test)]
mod tests {
    use super::ReconnectBackoff;
    use std::time::Duration;

    #[test]
    fn exponential_backoff_caps_at_max() {
        let mut backoff = ReconnectBackoff::new(5);

        let delays = [
            backoff.next_delay(),
            backoff.next_delay(),
            backoff.next_delay(),
            backoff.next_delay(),
            backoff.next_delay(),
        ];

        assert_eq!(
            delays,
            [
                Duration::from_secs(1),
                Duration::from_secs(2),
                Duration::from_secs(4),
                Duration::from_secs(5),
                Duration::from_secs(5),
            ]
        );
    }

    #[test]
    fn reset_starts_again_at_one_second() {
        let mut backoff = ReconnectBackoff::new(8);

        assert_eq!(backoff.next_delay(), Duration::from_secs(1));
        assert_eq!(backoff.next_delay(), Duration::from_secs(2));
        backoff.reset();
        assert_eq!(backoff.next_delay(), Duration::from_secs(1));
    }

    #[test]
    fn max_seconds_below_one_defaults_to_one() {
        let mut backoff = ReconnectBackoff::new(0);

        assert_eq!(backoff.next_delay(), Duration::from_secs(1));
        assert_eq!(backoff.next_delay(), Duration::from_secs(1));
    }

    #[test]
    fn large_max_saturates_without_overflow() {
        let mut backoff = ReconnectBackoff::new(u64::MAX);
        backoff.current = (u64::MAX / 2) + 1;

        assert_eq!(
            backoff.next_delay(),
            Duration::from_secs((u64::MAX / 2) + 1)
        );
        assert_eq!(backoff.next_delay(), Duration::from_secs(u64::MAX));
        assert_eq!(backoff.next_delay(), Duration::from_secs(u64::MAX));
    }
}