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 {
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 #[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 { None } else { Some(f(*self)) }
39 }
40}
41
42impl Default for Millis {
43 #[inline]
44 fn default() -> Millis {
45 Millis::ZERO
46 }
47}
48
49impl ops::Add<Millis> for Millis {
50 type Output = Millis;
51
52 #[inline]
53 fn add(self, other: Millis) -> Millis {
54 Millis(self.0.saturating_add(other.0))
55 }
56}
57
58impl ops::Add<Seconds> for Millis {
59 type Output = Millis;
60
61 #[inline]
62 #[allow(clippy::suspicious_arithmetic_impl)]
63 fn add(self, other: Seconds) -> Millis {
64 Millis(self.0.saturating_add((other.0 as u32).saturating_mul(1000)))
65 }
66}
67
68impl ops::Add<std::time::Duration> for Millis {
69 type Output = Millis;
70
71 #[inline]
72 fn add(self, other: std::time::Duration) -> Millis {
73 self + Millis::from(other)
74 }
75}
76
77impl ops::Add<Millis> for std::time::Duration {
78 type Output = std::time::Duration;
79
80 #[inline]
81 fn add(self, other: Millis) -> std::time::Duration {
82 self + Self::from(other)
83 }
84}
85
86impl From<u32> for Millis {
87 #[inline]
88 fn from(s: u32) -> Millis {
89 Millis(s)
90 }
91}
92
93impl From<i32> for Millis {
94 #[inline]
95 fn from(i: i32) -> Millis {
96 Millis(if i < 0 { 0 } else { i as u32 })
97 }
98}
99
100impl From<usize> for Millis {
101 #[inline]
102 fn from(s: usize) -> Millis {
103 Self(s.try_into().unwrap_or_else(|_| {
104 log::error!("Values is too large {s:?}");
105 u32::MAX
106 }))
107 }
108}
109
110impl From<Seconds> for Millis {
111 #[inline]
112 fn from(s: Seconds) -> Millis {
113 Millis((s.0 as u32).saturating_mul(1000))
114 }
115}
116
117impl From<std::time::Duration> for Millis {
118 #[inline]
119 fn from(d: std::time::Duration) -> Millis {
120 Self(d.as_millis().try_into().unwrap_or_else(|_| {
121 log::error!("time Duration is too large {d:?}");
122 1 << 31
123 }))
124 }
125}
126
127impl From<Millis> for std::time::Duration {
128 #[inline]
129 fn from(d: Millis) -> std::time::Duration {
130 std::time::Duration::from_millis(d.0 as u64)
131 }
132}
133
134#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
136pub struct Seconds(pub u16);
137
138impl Seconds {
139 pub const ZERO: Seconds = Seconds(0);
141
142 pub const ONE: Seconds = Seconds(1);
144
145 #[inline]
146 pub const fn new(secs: u16) -> Seconds {
147 Seconds(secs)
148 }
149
150 #[inline]
151 pub const fn checked_new(secs: usize) -> Seconds {
152 let secs = if (u16::MAX as usize) < secs {
153 u16::MAX
154 } else {
155 secs as u16
156 };
157 Seconds(secs)
158 }
159
160 #[inline]
161 pub const fn is_zero(self) -> bool {
162 self.0 == 0
163 }
164
165 #[inline]
166 pub const fn non_zero(self) -> bool {
167 self.0 != 0
168 }
169
170 #[inline]
171 pub const fn seconds(self) -> u64 {
172 self.0 as u64
173 }
174
175 #[inline]
177 pub fn map<F, R>(&self, f: F) -> Option<R>
178 where
179 F: FnOnce(Millis) -> R,
180 {
181 if self.0 == 0 {
182 None
183 } else {
184 Some(f(Millis::from(*self)))
185 }
186 }
187}
188
189impl Default for Seconds {
190 #[inline]
191 fn default() -> Seconds {
192 Seconds::ZERO
193 }
194}
195
196impl ops::Add<Seconds> for Seconds {
197 type Output = Seconds;
198
199 #[inline]
200 fn add(self, other: Seconds) -> Seconds {
201 Seconds(self.0.saturating_add(other.0))
202 }
203}
204
205impl From<Seconds> for std::time::Duration {
206 #[inline]
207 fn from(d: Seconds) -> std::time::Duration {
208 std::time::Duration::from_secs(d.0 as u64)
209 }
210}
211
212impl From<u16> for Seconds {
213 #[inline]
214 fn from(secs: u16) -> Seconds {
215 Self(secs)
216 }
217}
218
219impl From<i32> for Seconds {
220 #[inline]
221 fn from(i: i32) -> Seconds {
222 Seconds(if i < 0 { 0 } else { i as u16 })
223 }
224}
225
226impl From<usize> for Seconds {
227 #[inline]
228 fn from(secs: usize) -> Seconds {
229 Self(secs.try_into().unwrap_or_else(|_| {
230 log::error!("Seconds value is too large {secs:?}");
231 u16::MAX
232 }))
233 }
234}
235
236#[cfg(test)]
237mod tests {
238 use super::*;
239 use std::time::Duration;
240
241 #[test]
242 fn time_types() {
243 let m = Millis::default();
244 assert_eq!(m.0, 0);
245
246 let m = Millis(10) + Millis(20);
247 assert_eq!(m.0, 30);
248
249 let m = Millis(10) + Millis(u32::MAX);
250 assert_eq!(m.0, u32::MAX);
251
252 let m = Millis(10) + Seconds(1);
253 assert_eq!(m.0, 1010);
254
255 let m = Millis(u32::MAX) + Seconds(1);
256 assert_eq!(m.0, u32::MAX);
257
258 let m = Millis(10) + Duration::from_millis(100);
259 assert_eq!(m.0, 110);
260
261 let m = Duration::from_millis(100) + Millis(10);
262 assert_eq!(m, Duration::from_millis(110));
263
264 let m = Millis::from(Seconds(1));
265 assert_eq!(m.0, 1000);
266
267 let m = Millis::from(Duration::from_secs(1));
268 assert_eq!(m.0, 1000);
269
270 let m = Millis::from(Duration::from_secs(u64::MAX));
271 assert_eq!(m.0, 2_147_483_648);
272
273 let m = Millis::from_secs(1);
274 assert_eq!(m.0, 1000);
275
276 let m = Millis(0);
277 assert_eq!(m.map(|m| m + Millis(1)), None);
278
279 let m = Millis(1);
280 assert_eq!(m.map(|m| m + Millis(1)), Some(Millis(2)));
281
282 let s = Seconds::new(10);
283 assert_eq!(s.0, 10);
284
285 let s = Seconds::checked_new(10);
286 assert_eq!(s.0, 10);
287
288 let s = Seconds::checked_new(u16::MAX as usize + 10);
289 assert_eq!(s.0, u16::MAX);
290
291 assert!(Seconds::ZERO.is_zero());
292 assert!(!Seconds::ZERO.non_zero());
293
294 let s = Seconds::new(10);
295 assert_eq!(s.seconds(), 10);
296
297 let s = Seconds::default();
298 assert_eq!(s.0, 0);
299
300 let s = Seconds::new(10) + Seconds::new(10);
301 assert_eq!(s.seconds(), 20);
302
303 assert_eq!(Seconds(0).map(|_| 1usize), None);
304 assert_eq!(Seconds(2).map(|_| 1usize), Some(1));
305
306 let d = Duration::from(Seconds(100));
307 assert_eq!(d.as_secs(), 100);
308 }
309}