Skip to main content

ntex_util/time/
types.rs

1use std::ops;
2
3/// A span of time in milliseconds, for timeouts.
4///
5/// The value is a `u32`, so the longest representable span is about 49 days.
6/// Arithmetic saturates instead of overflowing.
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    /// Converts seconds to milliseconds, saturating at [`u32::MAX`].
19    pub const fn from_secs(secs: u32) -> Millis {
20        Millis(secs.saturating_mul(1000))
21    }
22
23    #[inline]
24    /// Returns `true` if the duration is zero.
25    pub const fn is_zero(&self) -> bool {
26        self.0 == 0
27    }
28
29    #[inline]
30    /// Returns `true` if the duration is not zero.
31    pub const fn non_zero(self) -> bool {
32        self.0 != 0
33    }
34
35    /// Calls `f` if the duration is not zero.
36    #[inline]
37    pub fn map<F, R>(&self, f: F) -> Option<R>
38    where
39        F: FnOnce(Millis) -> R,
40    {
41        if self.0 == 0 { None } else { Some(f(*self)) }
42    }
43}
44
45impl Default for Millis {
46    #[inline]
47    fn default() -> Millis {
48        Millis::ZERO
49    }
50}
51
52impl ops::Add<Millis> for Millis {
53    type Output = Millis;
54
55    #[inline]
56    fn add(self, other: Millis) -> Millis {
57        Millis(self.0.saturating_add(other.0))
58    }
59}
60
61impl ops::Add<Seconds> for Millis {
62    type Output = Millis;
63
64    #[inline]
65    #[allow(clippy::suspicious_arithmetic_impl)]
66    fn add(self, other: Seconds) -> Millis {
67        Millis(
68            self.0
69                .saturating_add((u32::from(other.0)).saturating_mul(1000)),
70        )
71    }
72}
73
74impl ops::Add<std::time::Duration> for Millis {
75    type Output = Millis;
76
77    #[inline]
78    fn add(self, other: std::time::Duration) -> Millis {
79        self + Millis::from(other)
80    }
81}
82
83impl ops::Add<Millis> for std::time::Duration {
84    type Output = std::time::Duration;
85
86    #[inline]
87    fn add(self, other: Millis) -> std::time::Duration {
88        self + Self::from(other)
89    }
90}
91
92impl From<u32> for Millis {
93    #[inline]
94    fn from(s: u32) -> Millis {
95        Millis(s)
96    }
97}
98
99impl From<i32> for Millis {
100    #[inline]
101    fn from(i: i32) -> Millis {
102        Millis(if i < 0 { 0 } else { i as u32 })
103    }
104}
105
106impl From<usize> for Millis {
107    #[inline]
108    fn from(s: usize) -> Millis {
109        Self(s.try_into().unwrap_or_else(|_| {
110            log::error!("Values is too large {s:?}");
111            u32::MAX
112        }))
113    }
114}
115
116impl From<Seconds> for Millis {
117    #[inline]
118    fn from(s: Seconds) -> Millis {
119        Millis((u32::from(s.0)).saturating_mul(1000))
120    }
121}
122
123impl From<std::time::Duration> for Millis {
124    #[inline]
125    fn from(d: std::time::Duration) -> Millis {
126        Self(d.as_millis().try_into().unwrap_or_else(|_| {
127            log::error!("Duration is too large for Millis: {d:?}");
128            u32::MAX
129        }))
130    }
131}
132
133impl From<Millis> for std::time::Duration {
134    #[inline]
135    fn from(d: Millis) -> std::time::Duration {
136        std::time::Duration::from_millis(u64::from(d.0))
137    }
138}
139
140/// A span of time in whole seconds.
141///
142/// The value is a `u16`, so the longest representable span is about 18 hours.
143#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
144pub struct Seconds(pub u16);
145
146impl Seconds {
147    /// Zero seconds value
148    pub const ZERO: Seconds = Seconds(0);
149
150    /// One second value
151    pub const ONE: Seconds = Seconds(1);
152
153    #[inline]
154    /// Creates a duration of `secs` seconds.
155    pub const fn new(secs: u16) -> Seconds {
156        Seconds(secs)
157    }
158
159    #[inline]
160    /// Creates a duration of `secs` seconds, saturating at [`u16::MAX`].
161    pub const fn checked_new(secs: usize) -> Seconds {
162        let secs = if (u16::MAX as usize) < secs {
163            u16::MAX
164        } else {
165            secs as u16
166        };
167        Seconds(secs)
168    }
169
170    #[inline]
171    /// Returns `true` if the duration is zero.
172    pub const fn is_zero(self) -> bool {
173        self.0 == 0
174    }
175
176    #[inline]
177    /// Returns `true` if the duration is not zero.
178    pub const fn non_zero(self) -> bool {
179        self.0 != 0
180    }
181
182    #[inline]
183    /// Returns the number of seconds.
184    pub const fn seconds(self) -> u64 {
185        self.0 as u64
186    }
187
188    /// Calls `f` with the duration in milliseconds if it is not zero.
189    #[inline]
190    pub fn map<F, R>(&self, f: F) -> Option<R>
191    where
192        F: FnOnce(Millis) -> R,
193    {
194        if self.0 == 0 {
195            None
196        } else {
197            Some(f(Millis::from(*self)))
198        }
199    }
200}
201
202impl Default for Seconds {
203    #[inline]
204    fn default() -> Seconds {
205        Seconds::ZERO
206    }
207}
208
209impl ops::Add<Seconds> for Seconds {
210    type Output = Seconds;
211
212    #[inline]
213    fn add(self, other: Seconds) -> Seconds {
214        Seconds(self.0.saturating_add(other.0))
215    }
216}
217
218impl From<Seconds> for std::time::Duration {
219    #[inline]
220    fn from(d: Seconds) -> std::time::Duration {
221        std::time::Duration::from_secs(u64::from(d.0))
222    }
223}
224
225impl From<u16> for Seconds {
226    #[inline]
227    fn from(secs: u16) -> Seconds {
228        Self(secs)
229    }
230}
231
232impl From<i32> for Seconds {
233    #[inline]
234    fn from(i: i32) -> Seconds {
235        Seconds(if i < 0 {
236            0
237        } else {
238            (i as u32).min(u32::from(u16::MAX)) as u16
239        })
240    }
241}
242
243impl From<usize> for Seconds {
244    #[inline]
245    fn from(secs: usize) -> Seconds {
246        Self(secs.try_into().unwrap_or_else(|_| {
247            log::error!("Seconds value is too large {secs:?}");
248            u16::MAX
249        }))
250    }
251}
252
253#[cfg(test)]
254mod tests {
255    use super::*;
256    use std::time::Duration;
257
258    #[test]
259    fn time_types() {
260        let m = Millis::default();
261        assert_eq!(m.0, 0);
262
263        let m = Millis::from(10u32);
264        assert_eq!(m.0, 10);
265
266        let m = Millis::from(10usize);
267        assert_eq!(m.0, 10);
268
269        let m = Millis(10) + Millis(20);
270        assert_eq!(m.0, 30);
271
272        let m = Millis(10) + Millis(u32::MAX);
273        assert_eq!(m.0, u32::MAX);
274
275        let m = Millis(10) + Seconds(1);
276        assert_eq!(m.0, 1010);
277
278        let m = Millis(u32::MAX) + Seconds(1);
279        assert_eq!(m.0, u32::MAX);
280
281        let m = Millis(10) + Duration::from_millis(100);
282        assert_eq!(m.0, 110);
283
284        let m = Duration::from_millis(100) + Millis(10);
285        assert_eq!(m, Duration::from_millis(110));
286
287        let m = Millis::from(Seconds(1));
288        assert_eq!(m.0, 1000);
289
290        let m = Millis::from(Duration::from_secs(1));
291        assert_eq!(m.0, 1000);
292
293        let m = Millis::from(Duration::from_secs(u64::MAX));
294        assert_eq!(m.0, u32::MAX);
295
296        let m = Millis::from_secs(1);
297        assert_eq!(m.0, 1000);
298        assert_eq!(Millis::from_secs(u32::MAX).0, u32::MAX);
299
300        let m = Millis(0);
301        assert_eq!(m.map(|m| m + Millis(1)), None);
302
303        let m = Millis(1);
304        assert_eq!(m.map(|m| m + Millis(1)), Some(Millis(2)));
305
306        let s = Seconds::new(10);
307        assert_eq!(s.0, 10);
308
309        let s = Seconds::from(10u16);
310        assert_eq!(s.0, 10);
311
312        let s = Seconds::from(10usize);
313        assert_eq!(s.0, 10);
314
315        let s = Seconds::from(10i32);
316        assert_eq!(s.0, 10);
317
318        let s = Seconds::from(-10i32);
319        assert_eq!(s.0, 0);
320        assert_eq!(Seconds::from(i32::MAX).0, u16::MAX);
321
322        let s = Seconds::checked_new(10);
323        assert_eq!(s.0, 10);
324
325        let s = Seconds::checked_new(u16::MAX as usize + 10);
326        assert_eq!(s.0, u16::MAX);
327
328        assert!(Seconds::ZERO.is_zero());
329        assert!(!Seconds::ZERO.non_zero());
330
331        let s = Seconds::new(10);
332        assert_eq!(s.seconds(), 10);
333
334        let s = Seconds::default();
335        assert_eq!(s.0, 0);
336
337        let s = Seconds::new(10) + Seconds::new(10);
338        assert_eq!(s.seconds(), 20);
339
340        assert_eq!(Seconds(0).map(|_| 1usize), None);
341        assert_eq!(Seconds(2).map(|_| 1usize), Some(1));
342
343        let d = Duration::from(Seconds(100));
344        assert_eq!(d.as_secs(), 100);
345    }
346
347    #[test]
348    fn time_types_conversions() {
349        assert!(Millis(1).non_zero());
350        assert!(!Millis::ZERO.non_zero());
351        assert_eq!(Millis::from(-5i32), Millis::ZERO);
352        assert_eq!(Millis::from(5i32), Millis(5));
353        assert_eq!(Millis::from(7usize), Millis(7));
354        assert_eq!(Seconds::from(7usize), Seconds(7));
355
356        #[cfg(target_pointer_width = "64")]
357        {
358            assert_eq!(Millis::from(usize::MAX), Millis(u32::MAX));
359            assert_eq!(Seconds::from(usize::MAX), Seconds(u16::MAX));
360        }
361    }
362}