Skip to main content

connpool/core/
timeouts.rs

1//! The per-call acquire timeout argument and the deadline math.
2
3/// The per-call acquire timeout. One of three things:
4///
5/// - [`Timeout::Unset`] — use the pool's configured `acquire_timeout` default.
6/// - [`Timeout::Infinite`] — wait forever.
7/// - [`Timeout::Of`] — wait that many seconds (`0.0` = don't wait).
8#[derive(Debug, Clone, Copy, PartialEq)]
9pub enum Timeout {
10    /// Use the configured default (`config.acquire_timeout`).
11    Unset,
12    /// Wait forever.
13    Infinite,
14    /// Wait this many seconds (`0.0` = don't wait).
15    Of(f64),
16}
17
18impl Timeout {
19    /// Maps `None` -> [`Timeout::Infinite`] and `Some(s)` -> [`Timeout::Of`].
20    pub fn from_option(seconds: Option<f64>) -> Self {
21        match seconds {
22            None => Timeout::Infinite,
23            Some(s) => Timeout::Of(s),
24        }
25    }
26
27    /// Resolves this timeout against a default (`None` = forever) at time `now`, returning the
28    /// absolute deadline (`None` = no deadline / wait forever).
29    pub(crate) fn compute_deadline(self, default: Option<f64>, now: f64) -> Option<f64> {
30        match self {
31            Timeout::Unset => default.map(|d| now + d),
32            Timeout::Infinite => None,
33            Timeout::Of(s) => Some(now + s),
34        }
35    }
36}
37
38/// Seconds left until `deadline` (`None` = no deadline).
39pub(crate) fn remaining(deadline: Option<f64>, now: f64) -> Option<f64> {
40    deadline.map(|d| d - now)
41}
42
43#[cfg(test)]
44mod tests {
45    use super::*;
46
47    #[test]
48    fn from_option_maps_none_to_infinite() {
49        assert_eq!(Timeout::from_option(None), Timeout::Infinite);
50        assert_eq!(Timeout::from_option(Some(3.0)), Timeout::Of(3.0));
51    }
52
53    #[test]
54    fn compute_deadline_covers_all_kinds() {
55        // Unset with a default -> now + default; without -> None.
56        assert_eq!(
57            Timeout::Unset.compute_deadline(Some(30.0), 100.0),
58            Some(130.0)
59        );
60        assert_eq!(Timeout::Unset.compute_deadline(None, 100.0), None);
61        // Infinite -> None regardless.
62        assert_eq!(Timeout::Infinite.compute_deadline(Some(30.0), 100.0), None);
63        // Of -> now + seconds (0 = immediate).
64        assert_eq!(Timeout::Of(5.0).compute_deadline(None, 100.0), Some(105.0));
65        assert_eq!(
66            Timeout::Of(0.0).compute_deadline(Some(30.0), 100.0),
67            Some(100.0)
68        );
69    }
70
71    #[test]
72    fn remaining_math() {
73        assert_eq!(remaining(None, 100.0), None);
74        assert_eq!(remaining(Some(130.0), 100.0), Some(30.0));
75        assert_eq!(remaining(Some(100.0), 130.0), Some(-30.0));
76        // Debug/Clone/PartialEq are derived; touch them.
77        let t = Timeout::Of(1.0);
78        assert_eq!(t, t.clone());
79        assert!(format!("{t:?}").contains("Of"));
80    }
81}