veecle_osal_api/time/duration.rs
1//! This module implements a [`Duration`] with microsecond precision.
2
3use core::fmt;
4use core::num::TryFromIntError;
5use core::ops::{Add, Div, Mul, Sub};
6
7/// Duration represents a span of time.
8///
9/// Negative durations are not supported. [`Duration`] is not meant to be used
10/// for math operations.
11#[derive(
12 Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, serde::Serialize, serde::Deserialize,
13)]
14pub struct Duration {
15 micros: u64,
16}
17
18impl Duration {
19 /// The largest value that can be represented by the `Duration` type.
20 ///
21 /// # Examples
22 ///
23 /// ```
24 /// use veecle_osal_api::time::Duration;
25 ///
26 /// assert_eq!(Duration::MAX, Duration::from_micros(u64::MAX));
27 /// ```
28 pub const MAX: Duration = Duration { micros: u64::MAX };
29
30 /// A duration of zero time.
31 ///
32 /// # Examples
33 ///
34 /// ```
35 /// use veecle_osal_api::time::Duration;
36 ///
37 /// assert_eq!(Duration::ZERO, Duration::from_micros(0));
38 /// ```
39 pub const ZERO: Duration = Duration { micros: 0 };
40
41 /// Factor of microseconds per second.
42 const MICROS_PER_SECOND: u64 = 1_000_000;
43 /// Factor of milliseconds per second.
44 const MILLIS_PER_SECOND: u64 = 1_000;
45
46 /// Creates a duration from the specified number of seconds.
47 ///
48 /// # Examples
49 ///
50 /// ```
51 /// use veecle_osal_api::time::Duration;
52 ///
53 /// assert_eq!(Duration::from_secs(1), Duration::from_millis(1000));
54 /// ```
55 pub const fn from_secs(secs: u64) -> Duration {
56 Duration {
57 micros: secs * Self::MICROS_PER_SECOND,
58 }
59 }
60
61 /// Creates a duration from the specified number of milliseconds.
62 ///
63 /// # Examples
64 ///
65 /// ```
66 /// use veecle_osal_api::time::Duration;
67 ///
68 /// assert_eq!(Duration::from_secs(1), Duration::from_millis(1000));
69 /// ```
70 pub const fn from_millis(millis: u64) -> Duration {
71 Duration {
72 micros: millis * Self::MILLIS_PER_SECOND,
73 }
74 }
75
76 /// Creates a duration from the specified number of microseconds.
77 ///
78 /// # Examples
79 ///
80 /// ```
81 /// use veecle_osal_api::time::Duration;
82 ///
83 /// assert_eq!(Duration::from_secs(1), Duration::from_micros(1000000));
84 /// ```
85 pub const fn from_micros(micros: u64) -> Duration {
86 Duration { micros }
87 }
88
89 /// Returns the total amount of seconds, rounded down.
90 ///
91 /// # Examples
92 ///
93 /// ```
94 /// use veecle_osal_api::time::Duration;
95 ///
96 /// assert_eq!(Duration::from_millis(1980).as_secs(), 1);
97 /// ```
98 pub const fn as_secs(&self) -> u64 {
99 self.micros / Self::MICROS_PER_SECOND
100 }
101
102 /// Returns the total amount of milliseconds, rounded down.
103 ///
104 /// # Examples
105 ///
106 /// ```
107 /// use veecle_osal_api::time::Duration;
108 ///
109 /// assert_eq!(Duration::from_millis(1980).as_millis(), 1980);
110 /// ```
111 pub const fn as_millis(&self) -> u64 {
112 self.micros / Self::MILLIS_PER_SECOND
113 }
114
115 /// Returns the total amount of microseconds.
116 ///
117 /// # Examples
118 ///
119 /// ```
120 /// use veecle_osal_api::time::Duration;
121 ///
122 /// assert_eq!(Duration::from_millis(1980).as_micros(), 1980000);
123 /// ```
124 pub const fn as_micros(&self) -> u64 {
125 self.micros
126 }
127
128 /// Adds one Duration to another, returning a new Duration or None in the event of an overflow.
129 ///
130 /// # Examples
131 ///
132 /// ```
133 /// use veecle_osal_api::time::Duration;
134 ///
135 /// assert_eq!(
136 /// Duration::from_secs(1).checked_add(Duration::from_secs(1)),
137 /// Some(Duration::from_secs(2))
138 /// );
139 /// assert_eq!(Duration::MAX.checked_add(Duration::from_secs(1)), None);
140 /// ```
141 pub fn checked_add(self, rhs: Duration) -> Option<Duration> {
142 self.micros
143 .checked_add(rhs.micros)
144 .map(|micros| Duration { micros })
145 }
146
147 /// Subtracts one Duration from another, returning a new Duration or None in the event of an underflow.
148 ///
149 /// # Examples
150 ///
151 /// ```
152 /// use veecle_osal_api::time::Duration;
153 ///
154 /// assert_eq!(
155 /// Duration::from_secs(2).checked_sub(Duration::from_secs(1)),
156 /// Some(Duration::from_secs(1))
157 /// );
158 /// assert_eq!(Duration::from_secs(1).checked_sub(Duration::from_secs(2)), None);
159 /// ```
160 pub fn checked_sub(self, rhs: Duration) -> Option<Duration> {
161 self.micros
162 .checked_sub(rhs.micros)
163 .map(|micros| Duration { micros })
164 }
165
166 /// Multiplies one Duration by a scalar `u32`, returning a new Duration or None in the event of an overflow.
167 ///
168 /// # Examples
169 ///
170 /// ```
171 /// use veecle_osal_api::time::Duration;
172 ///
173 /// assert_eq!(Duration::from_secs(1).checked_mul(2), Some(Duration::from_secs(2)));
174 /// assert_eq!(Duration::MAX.checked_mul(2), None);
175 /// ```
176 pub fn checked_mul(self, rhs: u32) -> Option<Duration> {
177 self.micros
178 .checked_mul(rhs as _)
179 .map(|micros| Duration { micros })
180 }
181
182 /// Divides one Duration by a scalar `u32`, returning a new Duration or None if `rhs == 0`.
183 ///
184 /// # Examples
185 ///
186 /// ```
187 /// use veecle_osal_api::time::Duration;
188 ///
189 /// assert_eq!(Duration::from_secs(1).checked_div(2), Some(Duration::from_millis(500)));
190 /// assert_eq!(Duration::from_secs(1).checked_div(0), None);
191 /// ```
192 pub fn checked_div(self, rhs: u32) -> Option<Duration> {
193 self.micros
194 .checked_div(rhs as _)
195 .map(|micros| Duration { micros })
196 }
197
198 /// Returns the absolute difference between self and rhs.
199 ///
200 /// # Examples
201 ///
202 /// ```
203 /// use veecle_osal_api::time::Duration;
204 ///
205 /// assert_eq!(Duration::from_secs(1).abs_diff(Duration::from_secs(2)), Duration::from_secs(1));
206 /// assert_eq!(Duration::from_secs(2).abs_diff(Duration::from_secs(1)), Duration::from_secs(1));
207 /// ```
208 pub fn abs_diff(self, rhs: Self) -> Duration {
209 Self {
210 micros: self.micros.abs_diff(rhs.micros),
211 }
212 }
213
214 /// Returns a duration of approximately 50 years.
215 ///
216 /// No leap-seconds/days have been taken into account.
217 ///
218 /// Can be used in place of [MAX][Self::MAX] to avoid overflows.
219 pub(crate) fn max_no_overflow_alias() -> Self {
220 // seconds * minutes * hours * days * weeks * years
221 Self::from_secs(60 * 60 * 24 * 7 * 52 * 50)
222 }
223}
224
225impl Add for Duration {
226 type Output = Self;
227
228 /// # Panics
229 ///
230 /// This function may panic if the resulting duration overflows. See [`Duration::checked_add`] for a version
231 /// without panic.
232 ///
233 /// # Examples
234 ///
235 /// ```
236 /// use veecle_osal_api::time::Duration;
237 ///
238 /// assert_eq!(Duration::from_secs(1) + Duration::from_secs(1), Duration::from_secs(2));
239 /// ```
240 ///
241 /// ```should_panic
242 /// use veecle_osal_api::time::Duration;
243 ///
244 /// let _ = Duration::MAX + Duration::from_secs(1);
245 /// ```
246 fn add(self, rhs: Self) -> Self::Output {
247 let Some(result) = self.checked_add(rhs) else {
248 panic!("overflow when adding two durations");
249 };
250
251 result
252 }
253}
254
255impl Sub for Duration {
256 type Output = Self;
257
258 /// # Panics
259 ///
260 /// This function may panic if the resulting duration underflows. See [`Duration::checked_sub`] for a
261 /// version without panic.
262 ///
263 /// # Examples
264 ///
265 /// ```
266 /// use veecle_osal_api::time::Duration;
267 ///
268 /// assert_eq!(Duration::from_secs(2) - Duration::from_secs(1), Duration::from_secs(1));
269 /// ```
270 ///
271 /// ```should_panic
272 /// use veecle_osal_api::time::Duration;
273 ///
274 /// let _ = Duration::from_secs(1) - Duration::from_secs(2);
275 /// ```
276 fn sub(self, rhs: Self) -> Self::Output {
277 let Some(result) = self.checked_sub(rhs) else {
278 panic!("underflow when subtracting two durations");
279 };
280
281 result
282 }
283}
284
285impl Mul<u32> for Duration {
286 type Output = Self;
287
288 /// # Panics
289 ///
290 /// This function may panic if the resulting duration overflows. See [`Duration::checked_mul`] for a version
291 /// without panic.
292 ///
293 /// # Examples
294 ///
295 /// ```
296 /// use veecle_osal_api::time::Duration;
297 ///
298 /// assert_eq!(Duration::from_secs(1) * 2, Duration::from_secs(2));
299 /// ```
300 ///
301 /// ```should_panic
302 /// use veecle_osal_api::time::Duration;
303 ///
304 /// let _ = Duration::MAX * 2;
305 /// ```
306 fn mul(self, rhs: u32) -> Self::Output {
307 let Some(result) = self.checked_mul(rhs) else {
308 panic!("overflow when multiplying a duration by a scalar");
309 };
310
311 result
312 }
313}
314
315impl Mul<Duration> for u32 {
316 type Output = Duration;
317
318 /// # Panics
319 ///
320 /// This function may panic if the resulting duration overflows. See [`Duration::checked_mul`] for a version
321 /// without panic.
322 ///
323 /// # Examples
324 ///
325 /// ```
326 /// use veecle_osal_api::time::Duration;
327 ///
328 /// assert_eq!(2 * Duration::from_secs(1), Duration::from_secs(2));
329 /// ```
330 ///
331 /// ```should_panic
332 /// use veecle_osal_api::time::Duration;
333 ///
334 /// let _ = 2 * Duration::MAX;
335 /// ```
336 fn mul(self, rhs: Duration) -> Self::Output {
337 rhs * self
338 }
339}
340
341impl Div<u32> for Duration {
342 type Output = Self;
343
344 /// # Panics
345 ///
346 /// This function may panic if the duration is divided by zero. See [`Duration::checked_div`] for a version
347 /// without panic.
348 ///
349 /// # Examples
350 ///
351 /// ```
352 /// use veecle_osal_api::time::Duration;
353 ///
354 /// assert_eq!(Duration::from_secs(1) / 2, Duration::from_millis(500));
355 /// ```
356 ///
357 /// ```should_panic
358 /// use veecle_osal_api::time::Duration;
359 ///
360 /// let _ = Duration::from_secs(1) / 0;
361 /// ```
362 fn div(self, rhs: u32) -> Self::Output {
363 let Some(result) = self.checked_div(rhs) else {
364 panic!("divided a duration by zero");
365 };
366
367 result
368 }
369}
370
371impl fmt::Debug for Duration {
372 /// # Examples
373 ///
374 /// ```
375 /// use veecle_osal_api::time::Duration;
376 ///
377 /// let duration = Duration::from_millis(1980);
378 /// assert_eq!(format!("{duration:?}"), "1s.980000us");
379 /// ```
380 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
381 write!(
382 f,
383 "{}s.{}us",
384 self.as_secs(),
385 self.as_micros() % Self::MICROS_PER_SECOND
386 )
387 }
388}
389
390impl TryFrom<core::time::Duration> for Duration {
391 type Error = TryFromIntError;
392
393 /// # Examples
394 ///
395 /// ```
396 /// use veecle_osal_api::time::Duration;
397 ///
398 /// assert_eq!(Duration::try_from(core::time::Duration::from_secs(1)), Ok(Duration::from_secs(1)));
399 /// ```
400 fn try_from(value: core::time::Duration) -> Result<Self, Self::Error> {
401 value.as_micros().try_into().map(Self::from_micros)
402 }
403}
404
405impl From<Duration> for core::time::Duration {
406 /// # Examples
407 ///
408 /// ```
409 /// use veecle_osal_api::time::Duration;
410 ///
411 /// assert_eq!(
412 /// core::time::Duration::from(Duration::from_secs(1)),
413 /// core::time::Duration::from_secs(1)
414 /// );
415 /// ```
416 fn from(value: Duration) -> Self {
417 Self::from_micros(value.as_micros())
418 }
419}