ntex_util/time/
types.rs

1use std::ops;
2
3/// A Duration type to represent a span of time.
4///
5/// This type is designed for timeouts. Milliseconds resolution
6/// is too small to keep generic time.
7#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
8pub struct Millis(pub u32);
9
10impl Millis {
11    /// Zero milliseconds value
12    pub const ZERO: Millis = Millis(0);
13
14    /// One second value
15    pub const ONE_SEC: Millis = Millis(1_000);
16
17    #[inline]
18    pub const fn from_secs(secs: u32) -> Millis {
19        Millis(secs * 1000)
20    }
21
22    #[inline]
23    pub const fn is_zero(&self) -> bool {
24        self.0 == 0
25    }
26
27    #[inline]
28    pub const fn non_zero(self) -> bool {
29        self.0 != 0
30    }
31
32    /// Call function `f` if duration is none zero.
33    #[inline]
34    pub fn map<F, R>(&self, f: F) -> Option<R>
35    where
36        F: FnOnce(Millis) -> R,
37    {
38        if self.0 == 0 {
39            None
40        } else {
41            Some(f(*self))
42        }
43    }
44}
45
46impl Default for Millis {
47    #[inline]
48    fn default() -> Millis {
49        Millis::ZERO
50    }
51}
52
53impl ops::Add<Millis> for Millis {
54    type Output = Millis;
55
56    #[inline]
57    fn add(self, other: Millis) -> Millis {
58        Millis(self.0.checked_add(other.0).unwrap_or(u32::MAX))
59    }
60}
61
62impl ops::Add<Seconds> for Millis {
63    type Output = Millis;
64
65    #[inline]
66    #[allow(clippy::suspicious_arithmetic_impl)]
67    fn add(self, other: Seconds) -> Millis {
68        Millis(
69            self.0
70                .checked_add((other.0 as u32).checked_mul(1000).unwrap_or(u32::MAX))
71                .unwrap_or(u32::MAX),
72        )
73    }
74}
75
76impl ops::Add<std::time::Duration> for Millis {
77    type Output = Millis;
78
79    #[inline]
80    fn add(self, other: std::time::Duration) -> Millis {
81        self + Millis::from(other)
82    }
83}
84
85impl ops::Add<Millis> for std::time::Duration {
86    type Output = std::time::Duration;
87
88    #[inline]
89    fn add(self, other: Millis) -> std::time::Duration {
90        self + Self::from(other)
91    }
92}
93
94impl From<Seconds> for Millis {
95    #[inline]
96    fn from(s: Seconds) -> Millis {
97        Millis((s.0 as u32).checked_mul(1000).unwrap_or(u32::MAX))
98    }
99}
100
101impl From<std::time::Duration> for Millis {
102    #[inline]
103    fn from(d: std::time::Duration) -> Millis {
104        Self(d.as_millis().try_into().unwrap_or_else(|_| {
105            log::error!("time Duration is too large {:?}", d);
106            1 << 31
107        }))
108    }
109}
110
111impl From<Millis> for std::time::Duration {
112    #[inline]
113    fn from(d: Millis) -> std::time::Duration {
114        std::time::Duration::from_millis(d.0 as u64)
115    }
116}
117
118/// A Seconds type to represent a span of time in seconds.
119#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
120pub struct Seconds(pub u16);
121
122impl Seconds {
123    /// Zero seconds value
124    pub const ZERO: Seconds = Seconds(0);
125
126    /// One second value
127    pub const ONE: Seconds = Seconds(1);
128
129    #[inline]
130    pub const fn new(secs: u16) -> Seconds {
131        Seconds(secs)
132    }
133
134    #[inline]
135    pub const fn checked_new(secs: usize) -> Seconds {
136        let secs = if (u16::MAX as usize) < secs {
137            u16::MAX
138        } else {
139            secs as u16
140        };
141        Seconds(secs)
142    }
143
144    #[inline]
145    pub const fn is_zero(self) -> bool {
146        self.0 == 0
147    }
148
149    #[inline]
150    pub const fn non_zero(self) -> bool {
151        self.0 != 0
152    }
153
154    #[inline]
155    pub const fn seconds(self) -> u64 {
156        self.0 as u64
157    }
158
159    /// Call function `f` if seconds is none zero.
160    #[inline]
161    pub fn map<F, R>(&self, f: F) -> Option<R>
162    where
163        F: FnOnce(Millis) -> R,
164    {
165        if self.0 == 0 {
166            None
167        } else {
168            Some(f(Millis::from(*self)))
169        }
170    }
171}
172
173impl Default for Seconds {
174    #[inline]
175    fn default() -> Seconds {
176        Seconds::ZERO
177    }
178}
179
180impl ops::Add<Seconds> for Seconds {
181    type Output = Seconds;
182
183    #[inline]
184    fn add(self, other: Seconds) -> Seconds {
185        Seconds(self.0.checked_add(other.0).unwrap_or(u16::MAX))
186    }
187}
188
189impl From<Seconds> for std::time::Duration {
190    #[inline]
191    fn from(d: Seconds) -> std::time::Duration {
192        std::time::Duration::from_secs(d.0 as u64)
193    }
194}
195
196#[cfg(test)]
197mod tests {
198    use super::*;
199    use std::time::Duration;
200
201    #[test]
202    fn time_types() {
203        let m = Millis::default();
204        assert_eq!(m.0, 0);
205
206        let m = Millis(10) + Millis(20);
207        assert_eq!(m.0, 30);
208
209        let m = Millis(10) + Millis(u32::MAX);
210        assert_eq!(m.0, u32::MAX);
211
212        let m = Millis(10) + Seconds(1);
213        assert_eq!(m.0, 1010);
214
215        let m = Millis(u32::MAX) + Seconds(1);
216        assert_eq!(m.0, u32::MAX);
217
218        let m = Millis(10) + Duration::from_millis(100);
219        assert_eq!(m.0, 110);
220
221        let m = Duration::from_millis(100) + Millis(10);
222        assert_eq!(m, Duration::from_millis(110));
223
224        let m = Millis::from(Seconds(1));
225        assert_eq!(m.0, 1000);
226
227        let m = Millis::from(Duration::from_secs(1));
228        assert_eq!(m.0, 1000);
229
230        let m = Millis::from(Duration::from_secs(u64::MAX));
231        assert_eq!(m.0, 2_147_483_648);
232
233        let m = Millis::from_secs(1);
234        assert_eq!(m.0, 1000);
235
236        let m = Millis(0);
237        assert_eq!(m.map(|m| m + Millis(1)), None);
238
239        let m = Millis(1);
240        assert_eq!(m.map(|m| m + Millis(1)), Some(Millis(2)));
241
242        let s = Seconds::new(10);
243        assert_eq!(s.0, 10);
244
245        let s = Seconds::checked_new(10);
246        assert_eq!(s.0, 10);
247
248        let s = Seconds::checked_new(u16::MAX as usize + 10);
249        assert_eq!(s.0, u16::MAX);
250
251        assert!(Seconds::ZERO.is_zero());
252        assert!(!Seconds::ZERO.non_zero());
253
254        let s = Seconds::new(10);
255        assert_eq!(s.seconds(), 10);
256
257        let s = Seconds::default();
258        assert_eq!(s.0, 0);
259
260        let s = Seconds::new(10) + Seconds::new(10);
261        assert_eq!(s.seconds(), 20);
262
263        assert_eq!(Seconds(0).map(|_| 1usize), None);
264        assert_eq!(Seconds(2).map(|_| 1usize), Some(1));
265
266        let d = Duration::from(Seconds(100));
267        assert_eq!(d.as_secs(), 100);
268    }
269}