1use std::ops;
2
3#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
8pub struct Millis(pub u32);
9
10impl Millis {
11 pub const ZERO: Millis = Millis(0);
13
14 pub const ONE_SEC: Millis = Millis(1_000);
16
17 #[inline]
18 pub const fn from_secs(secs: u32) -> Millis {
20 Millis(secs.saturating_mul(1000))
21 }
22
23 #[inline]
24 pub const fn is_zero(&self) -> bool {
26 self.0 == 0
27 }
28
29 #[inline]
30 pub const fn non_zero(self) -> bool {
32 self.0 != 0
33 }
34
35 #[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#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
144pub struct Seconds(pub u16);
145
146impl Seconds {
147 pub const ZERO: Seconds = Seconds(0);
149
150 pub const ONE: Seconds = Seconds(1);
152
153 #[inline]
154 pub const fn new(secs: u16) -> Seconds {
156 Seconds(secs)
157 }
158
159 #[inline]
160 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 pub const fn is_zero(self) -> bool {
173 self.0 == 0
174 }
175
176 #[inline]
177 pub const fn non_zero(self) -> bool {
179 self.0 != 0
180 }
181
182 #[inline]
183 pub const fn seconds(self) -> u64 {
185 self.0 as u64
186 }
187
188 #[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]
264 fn time_types() {
265 let m = Millis::default();
266 assert_eq!(m.0, 0);
267
268 let m = Millis::from(10u32);
269 assert_eq!(m.0, 10);
270
271 let m = Millis::from(10usize);
272 assert_eq!(m.0, 10);
273
274 let m = Millis(10) + Millis(20);
275 assert_eq!(m.0, 30);
276
277 let m = Millis(10) + Millis(u32::MAX);
278 assert_eq!(m.0, u32::MAX);
279
280 let m = Millis(10) + Seconds(1);
281 assert_eq!(m.0, 1010);
282
283 let m = Millis(u32::MAX) + Seconds(1);
284 assert_eq!(m.0, u32::MAX);
285
286 let m = Millis(10) + Duration::from_millis(100);
287 assert_eq!(m.0, 110);
288
289 let m = Duration::from_millis(100) + Millis(10);
290 assert_eq!(m, Duration::from_millis(110));
291
292 let m = Millis::from(Seconds(1));
293 assert_eq!(m.0, 1000);
294
295 let m = Millis::from(Duration::from_secs(1));
296 assert_eq!(m.0, 1000);
297
298 let m = Millis::from(Duration::from_secs(u64::MAX));
299 assert_eq!(m.0, u32::MAX);
300
301 let m = Millis::from_secs(1);
302 assert_eq!(m.0, 1000);
303 assert_eq!(Millis::from_secs(u32::MAX).0, u32::MAX);
304
305 let m = Millis(0);
306 assert_eq!(m.map(|m| m + Millis(1)), None);
307
308 let m = Millis(1);
309 assert_eq!(m.map(|m| m + Millis(1)), Some(Millis(2)));
310
311 let s = Seconds::new(10);
312 assert_eq!(s.0, 10);
313
314 let s = Seconds::from(10u16);
315 assert_eq!(s.0, 10);
316
317 let s = Seconds::from(10usize);
318 assert_eq!(s.0, 10);
319
320 let s = Seconds::from(10i32);
321 assert_eq!(s.0, 10);
322
323 let s = Seconds::from(-10i32);
324 assert_eq!(s.0, 0);
325 assert_eq!(Seconds::from(i32::MAX).0, u16::MAX);
326
327 let s = Seconds::checked_new(10);
328 assert_eq!(s.0, 10);
329
330 let s = Seconds::checked_new(u16::MAX as usize + 10);
331 assert_eq!(s.0, u16::MAX);
332
333 assert!(Seconds::ZERO.is_zero());
334 assert!(!Seconds::ZERO.non_zero());
335
336 let s = Seconds::new(10);
337 assert_eq!(s.seconds(), 10);
338
339 let s = Seconds::default();
340 assert_eq!(s.0, 0);
341
342 let s = Seconds::new(10) + Seconds::new(10);
343 assert_eq!(s.seconds(), 20);
344
345 assert_eq!(Seconds(0).map(|_| 1usize), None);
346 assert_eq!(Seconds(2).map(|_| 1usize), Some(1));
347
348 let d = Duration::from(Seconds(100));
349 assert_eq!(d.as_secs(), 100);
350 }
351
352 #[test]
358 fn time_types_conversions() {
359 assert!(Millis(1).non_zero());
360 assert!(!Millis::ZERO.non_zero());
361 assert_eq!(Millis::from(-5i32), Millis::ZERO);
362 assert_eq!(Millis::from(5i32), Millis(5));
363 assert_eq!(Millis::from(7usize), Millis(7));
364 assert_eq!(Seconds::from(7usize), Seconds(7));
365
366 #[cfg(target_pointer_width = "64")]
367 {
368 assert_eq!(Millis::from(usize::MAX), Millis(u32::MAX));
369 assert_eq!(Seconds::from(usize::MAX), Seconds(u16::MAX));
370 }
371 }
372}