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]
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}