fugit/
duration.rs

1use crate::helpers::{self, Helpers};
2use crate::Rate;
3use core::cmp::Ordering;
4use core::convert;
5use core::ops;
6
7/// Represents a duration of time.
8///
9/// The generic `T` can either be `u32` or `u64`, and the const generics represent the ratio of the
10/// ticks contained within the duration: `duration in seconds = NOM / DENOM * ticks`
11#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
12#[cfg_attr(
13    feature = "postcard_max_size",
14    derive(postcard::experimental::max_size::MaxSize)
15)]
16#[derive(Clone, Copy, Debug)]
17pub struct Duration<T, const NOM: u32, const DENOM: u32> {
18    pub(crate) ticks: T,
19}
20
21macro_rules! shorthand {
22    ($i:ty, $nom:literal, $denum:literal, $unit:ident, $to_unit:ident, $unital:ident, $unitstr:literal) => {
23        #[doc = concat!("Convert the Duration to an integer number of ", $unitstr, ".")]
24        #[inline]
25        pub const fn $to_unit(&self) -> $i {
26            (Helpers::<$nom, $denum, NOM, DENOM>::LD_TIMES_RN as $i * self.ticks)
27                / Helpers::<$nom, $denum, NOM, DENOM>::RD_TIMES_LN as $i
28        }
29
30        #[doc = concat!("Shorthand for creating a duration which represents ", $unitstr, ".")]
31        #[inline]
32        pub const fn $unit(val: $i) -> Self {
33            Self::from_ticks(
34                (Helpers::<$nom, $denum, NOM, DENOM>::RD_TIMES_LN as $i * val)
35                    / Helpers::<$nom, $denum, NOM, DENOM>::LD_TIMES_RN as $i
36            )
37        }
38
39        #[doc = concat!("Shorthand for creating a duration which represents ", $unitstr, " (ceil rounded).")]
40        #[inline]
41        pub const fn $unital(val: $i) -> Self {
42            let mul = Helpers::<$nom, $denum, NOM, DENOM>::RD_TIMES_LN as $i * val;
43            let ld_times_rn = Helpers::<$nom, $denum, NOM, DENOM>::LD_TIMES_RN as $i;
44            Self::from_ticks(if mul % ld_times_rn == 0 {
45                mul / ld_times_rn
46            } else {
47                mul / ld_times_rn + 1
48            })
49        }
50    };
51}
52
53macro_rules! impl_duration_for_integer {
54    ($i:ty) => {
55        impl<const NOM: u32, const DENOM: u32> Duration<$i, NOM, DENOM> {
56            /// Create a `Duration` from a ticks value.
57            ///
58            /// ```
59            /// # use fugit::*;
60            #[doc = concat!("let _d = Duration::<", stringify!($i), ", 1, 1_000>::from_ticks(1);")]
61            /// ```
62            #[inline]
63            pub const fn from_ticks(ticks: $i) -> Self {
64                helpers::greater_than_0::<NOM>();
65                helpers::greater_than_0::<DENOM>();
66
67                Duration { ticks }
68            }
69
70            /// Extract the ticks from a `Duration`.
71            ///
72            /// ```
73            /// # use fugit::*;
74            #[doc = concat!("let d = Duration::<", stringify!($i), ", 1, 1_000>::from_ticks(234);")]
75            ///
76            /// assert_eq!(d.ticks(), 234);
77            /// ```
78            #[inline]
79            pub const fn ticks(&self) -> $i {
80                self.ticks
81            }
82
83            /// Returns true if this `Duration` spans no time
84            ///
85            /// ```
86            /// # use fugit::*;
87            #[doc = concat!("let zero = Duration::<", stringify!($i), ", 1, 1_000>::from_ticks(0);")]
88            #[doc = concat!("let one = Duration::<", stringify!($i), ", 1, 1_000>::from_ticks(1);")]
89            ///
90            /// assert_eq!(zero.is_zero(), true);
91            /// assert_eq!(one.is_zero(), false);
92            /// ```
93            #[inline]
94            pub const fn is_zero(&self) -> bool {
95                self.ticks == 0
96            }
97
98            /// Add two durations while checking for overflow.
99            ///
100            /// ```
101            /// # use fugit::*;
102            #[doc = concat!("let d1 = Duration::<", stringify!($i), ", 1, 1_000>::from_ticks(1);")]
103            #[doc = concat!("let d2 = Duration::<", stringify!($i), ", 1, 1_000>::from_ticks(2);")]
104            #[doc = concat!("let d3 = Duration::<", stringify!($i), ", 1, 1_000>::from_ticks(", stringify!($i), "::MAX);")]
105            ///
106            /// assert_eq!(d1.checked_add(d2).unwrap().ticks(), 3);
107            /// assert_eq!(d1.checked_add(d3), None);
108            /// ```
109            pub const fn checked_add<const O_NOM: u32, const O_DENOM: u32>(
110                self,
111                other: Duration<$i, O_NOM, O_DENOM>,
112            ) -> Option<Self> {
113                if Helpers::<NOM, DENOM, O_NOM, O_DENOM>::SAME_BASE {
114                    if let Some(ticks) = self.ticks.checked_add(other.ticks) {
115                        Some(Duration::<$i, NOM, DENOM>::from_ticks(ticks))
116                    } else {
117                        None
118                    }
119                } else {
120                    if let Some(lh) = other
121                        .ticks
122                        .checked_mul(Helpers::<NOM, DENOM, O_NOM, O_DENOM>::LD_TIMES_RN as $i)
123                    {
124                        let ticks = lh / Helpers::<NOM, DENOM, O_NOM, O_DENOM>::RD_TIMES_LN as $i;
125
126                        if let Some(ticks) = self.ticks.checked_add(ticks) {
127                            Some(Duration::<$i, NOM, DENOM>::from_ticks(ticks))
128                        } else {
129                            None
130                        }
131                    } else {
132                        None
133                    }
134                }
135            }
136
137            /// Subtract two durations while checking for overflow.
138            ///
139            /// ```
140            /// # use fugit::*;
141            #[doc = concat!("let d1 = Duration::<", stringify!($i), ", 1, 1_000>::from_ticks(1);")]
142            #[doc = concat!("let d2 = Duration::<", stringify!($i), ", 1, 1_000>::from_ticks(2);")]
143            #[doc = concat!("let d3 = Duration::<", stringify!($i), ", 1, 1_000>::from_ticks(", stringify!($i), "::MAX);")]
144            ///
145            /// assert_eq!(d2.checked_sub(d1).unwrap().ticks(), 1);
146            /// assert_eq!(d1.checked_sub(d3), None);
147            /// ```
148            pub const fn checked_sub<const O_NOM: u32, const O_DENOM: u32>(
149                self,
150                other: Duration<$i, O_NOM, O_DENOM>,
151            ) -> Option<Self> {
152                if Helpers::<NOM, DENOM, O_NOM, O_DENOM>::SAME_BASE {
153                    if let Some(ticks) = self.ticks.checked_sub(other.ticks) {
154                        Some(Duration::<$i, NOM, DENOM>::from_ticks(ticks))
155                    } else {
156                        None
157                    }
158                } else {
159                    if let Some(lh) = other
160                        .ticks
161                        .checked_mul(Helpers::<NOM, DENOM, O_NOM, O_DENOM>::LD_TIMES_RN as $i)
162                    {
163                        let ticks = lh / Helpers::<NOM, DENOM, O_NOM, O_DENOM>::RD_TIMES_LN as $i;
164
165                        if let Some(ticks) = self.ticks.checked_sub(ticks) {
166                            Some(Duration::<$i, NOM, DENOM>::from_ticks(ticks))
167                        } else {
168                            None
169                        }
170                    } else {
171                        None
172                    }
173                }
174            }
175
176            #[doc = concat!("Const `cmp` for ", stringify!($i))]
177            #[inline(always)]
178            const fn _const_cmp(a: $i, b: $i) -> Ordering {
179                if a < b {
180                    Ordering::Less
181                } else if a > b {
182                    Ordering::Greater
183                } else {
184                    Ordering::Equal
185                }
186            }
187
188            /// Const partial comparison.
189            ///
190            /// ```
191            /// # use fugit::*;
192            #[doc = concat!("let d1 = Duration::<", stringify!($i), ", 1, 1_00>::from_ticks(1);")]
193            #[doc = concat!("let d2 = Duration::<", stringify!($i), ", 1, 1_000>::from_ticks(1);")]
194            ///
195            /// assert_eq!(d1.const_partial_cmp(d2), Some(core::cmp::Ordering::Greater));
196            /// ```
197            #[inline]
198            pub const fn const_partial_cmp<const R_NOM: u32, const R_DENOM: u32>(
199                self,
200                other: Duration<$i, R_NOM, R_DENOM>
201            ) -> Option<Ordering> {
202                //
203                // We want to check:
204                //
205                // n_lh / d_lh * lh_ticks {cmp} n_rh / d_rh * rh_ticks
206                //
207                // simplify to
208                //
209                // n_lh * d_rh * lh_ticks {cmp} n_rh * d_lh * rh_ticks
210                //
211                // find gdc(n_lh * d_rh, n_rh * d_lh) and use that to make the constants minimal (done
212                // with the `helpers::Helpers` struct)
213                //
214                // then perform the comparison in a comparable basis
215                //
216
217                if Helpers::<NOM, DENOM, R_NOM, R_DENOM>::SAME_BASE {
218                    // If we are in the same base, comparison in trivial
219                    Some(Self::_const_cmp(self.ticks, other.ticks))
220                } else {
221                    let lh = self
222                        .ticks
223                        .checked_mul(Helpers::<NOM, DENOM, R_NOM, R_DENOM>::RD_TIMES_LN as $i);
224                    let rh = other
225                        .ticks
226                        .checked_mul(Helpers::<NOM, DENOM, R_NOM, R_DENOM>::LD_TIMES_RN as $i);
227
228                    if let (Some(lh), Some(rh)) = (lh, rh) {
229                        Some(Self::_const_cmp(lh, rh))
230                    } else {
231                        None
232                    }
233                }
234            }
235
236            /// Const equality check.
237            ///
238            /// ```
239            /// # use fugit::*;
240            #[doc = concat!("let d1 = Duration::<", stringify!($i), ", 1, 1_00>::from_ticks(1);")]
241            #[doc = concat!("let d2 = Duration::<", stringify!($i), ", 1, 1_000>::from_ticks(10);")]
242            ///
243            /// assert!(d1.const_eq(d2));
244            /// ```
245            #[inline]
246            pub const fn const_eq<const R_NOM: u32, const R_DENOM: u32>(
247                self,
248                other: Duration<$i, R_NOM, R_DENOM>
249            ) -> bool {
250                if Helpers::<NOM, DENOM, R_NOM, R_DENOM>::SAME_BASE {
251                    // If we are in the same base, comparison in trivial
252                    self.ticks == other.ticks
253                } else {
254                    let lh = self
255                        .ticks
256                        .checked_mul(Helpers::<NOM, DENOM, R_NOM, R_DENOM>::RD_TIMES_LN as $i);
257                    let rh = other
258                        .ticks
259                        .checked_mul(Helpers::<NOM, DENOM, R_NOM, R_DENOM>::LD_TIMES_RN as $i);
260
261                    if let (Some(lh), Some(rh)) = (lh, rh) {
262                        lh == rh
263                    } else {
264                        false
265                    }
266                }
267            }
268
269            /// Const try from, checking for overflow.
270            ///
271            /// ```
272            /// # use fugit::*;
273            #[doc = concat!("let d1 = Duration::<", stringify!($i), ", 1, 1_00>::from_ticks(1);")]
274            #[doc = concat!("let d2 = Duration::<", stringify!($i), ", 1, 1_000>::const_try_from(d1);")]
275            ///
276            /// assert_eq!(d2.unwrap().ticks(), 10);
277            /// ```
278            pub const fn const_try_from<const I_NOM: u32, const I_DENOM: u32>(
279                duration: Duration<$i, I_NOM, I_DENOM>,
280            ) -> Option<Self> {
281                if Helpers::<I_NOM, I_DENOM, NOM, DENOM>::SAME_BASE {
282                    Some(Self::from_ticks(duration.ticks))
283                } else {
284                    if let Some(lh) = (duration.ticks as u64)
285                        .checked_mul(Helpers::<I_NOM, I_DENOM, NOM, DENOM>::RD_TIMES_LN as u64)
286                    {
287                        let ticks = lh / Helpers::<I_NOM, I_DENOM, NOM, DENOM>::LD_TIMES_RN as u64;
288
289                        if ticks <= <$i>::MAX as u64 {
290                            Some(Self::from_ticks(ticks as $i))
291                        } else {
292                            None
293                        }
294                    } else {
295                        None
296                    }
297                }
298            }
299
300            /// Const try into, checking for overflow.
301            ///
302            /// ```
303            /// # use fugit::*;
304            #[doc = concat!("let d1 = Duration::<", stringify!($i), ", 1, 1_00>::from_ticks(1);")]
305            #[doc = concat!("let d2: Option<Duration::<", stringify!($i), ", 1, 1_000>> = d1.const_try_into();")]
306            ///
307            /// assert_eq!(d2.unwrap().ticks(), 10);
308            /// ```
309            #[inline]
310            pub const fn const_try_into<const O_NOM: u32, const O_DENOM: u32>(
311                self,
312            ) -> Option<Duration<$i, O_NOM, O_DENOM>> {
313                Duration::<$i, O_NOM, O_DENOM>::const_try_from(self)
314            }
315
316            /// Const try into rate, checking for divide-by-zero.
317            ///
318            /// ```
319            /// # use fugit::*;
320            #[doc = concat!("let d1 = Duration::<", stringify!($i), ", 1, 1_000>::from_ticks(2);")]
321            #[doc = concat!("let r1: Option<Rate::<", stringify!($i), ", 1, 1>> = d1.try_into_rate();")]
322            ///
323            /// assert_eq!(r1.unwrap().raw(), 500);
324            /// ```
325            #[inline]
326            pub const fn try_into_rate<const O_NOM: u32, const O_DENOM: u32>(
327                self,
328            ) -> Option<Rate<$i, O_NOM, O_DENOM>> {
329                Rate::<$i, O_NOM, O_DENOM>::try_from_duration(self)
330            }
331
332            /// Convert from duration to rate.
333            #[inline]
334            pub const fn into_rate<const O_NOM: u32, const O_DENOM: u32>(
335                self,
336            ) -> Rate<$i, O_NOM, O_DENOM> {
337                if let Some(v) = self.try_into_rate() {
338                    v
339                } else {
340                    panic!("Into rate failed, divide-by-zero!");
341                }
342            }
343
344            /// Const try from rate, checking for divide-by-zero.
345            ///
346            /// ```
347            /// # use fugit::*;
348            #[doc = concat!("let r1 = Rate::<", stringify!($i), ", 1, 1>::from_raw(1);")]
349            #[doc = concat!("let d1 = Duration::<", stringify!($i), ", 1, 1_000>::try_from_rate(r1);")]
350            ///
351            /// assert_eq!(d1.unwrap().ticks(), 1_000);
352            /// ```
353            #[inline]
354            pub const fn try_from_rate<const I_NOM: u32, const I_DENOM: u32>(
355                rate: Rate<$i, I_NOM, I_DENOM>,
356            ) -> Option<Self> {
357                if rate.raw > 0 {
358                    Some(Self::from_ticks(
359                        Helpers::<I_NOM, I_DENOM, NOM, DENOM>::RATE_TO_DURATION_NUMERATOR as $i
360                        / rate.raw
361                    ))
362                } else {
363                    None
364                }
365            }
366
367            /// Convert from rate to duration.
368            #[inline]
369            pub const fn from_rate<const I_NOM: u32, const I_DENOM: u32>(
370                rate: Rate<$i, I_NOM, I_DENOM>,
371            ) -> Self {
372                if let Some(v) = Self::try_from_rate(rate) {
373                    v
374                } else {
375                    panic!("From rate failed, divide-by-zero!");
376                }
377            }
378
379            /// Convert between bases for a duration.
380            ///
381            /// Unfortunately not a `From` impl due to collision with the std lib.
382            ///
383            /// ```
384            /// # use fugit::*;
385            #[doc = concat!("let d1 = Duration::<", stringify!($i), ", 1, 100>::from_ticks(1);")]
386            #[doc = concat!("let d2: Duration::<", stringify!($i), ", 1, 1_000> = d1.convert();")]
387            ///
388            /// assert_eq!(d2.ticks(), 10);
389            /// ```
390            /// Can be used in const contexts. Compilation will fail if the conversion causes overflow
391            /// ```compile_fail
392            /// # use fugit::*;
393            #[doc = concat!("const TICKS: ", stringify!($i), "= ", stringify!($i), "::MAX - 10;")]
394            #[doc = concat!("const D1: Duration::<", stringify!($i), ", 1, 100> = Duration::<", stringify!($i), ", 1, 100>::from_ticks(TICKS);")]
395            /// // Fails conversion due to tick overflow
396            #[doc = concat!("const D2: Duration::<", stringify!($i), ", 1, 200> = D1.convert();")]
397            #[inline]
398            pub const fn convert<const O_NOM: u32, const O_DENOM: u32>(
399                self,
400            ) -> Duration<$i, O_NOM, O_DENOM> {
401                if let Some(v) = self.const_try_into() {
402                    v
403                } else {
404                    panic!("Convert failed!");
405                }
406            }
407
408            shorthand!($i, 1, 1_000_000_000, nanos, to_nanos, nanos_at_least, "nanoseconds");
409            shorthand!($i, 1, 1_000_000, micros, to_micros, micros_at_least, "microseconds");
410            shorthand!($i, 1, 1_000, millis, to_millis, millis_at_least, "milliseconds");
411            shorthand!($i, 1, 1, secs, to_secs, secs_at_least, "seconds");
412            shorthand!($i, 60, 1, minutes, to_minutes, minutes_at_least, "minutes");
413            shorthand!($i, 3600, 1, hours, to_hours, hours_at_least, "hours");
414
415            /// Shorthand for creating a duration which represents hertz.
416            #[inline]
417            #[allow(non_snake_case)]
418            pub const fn Hz(val: $i) -> Self {
419                Self::from_rate(crate::Hertz::<$i>::from_raw(val))
420            }
421
422            /// Shorthand for creating a duration which represents kilohertz.
423            #[inline]
424            #[allow(non_snake_case)]
425            pub const fn kHz(val: $i) -> Self {
426                Self::from_rate(crate::Kilohertz::<$i>::from_raw(val))
427            }
428
429            /// Shorthand for creating a duration which represents megahertz.
430            #[inline]
431            #[allow(non_snake_case)]
432            pub const fn MHz(val: $i) -> Self {
433                Self::from_rate(crate::Megahertz::<$i>::from_raw(val))
434            }
435        }
436
437        impl<const L_NOM: u32, const L_DENOM: u32, const R_NOM: u32, const R_DENOM: u32>
438            PartialOrd<Duration<$i, R_NOM, R_DENOM>> for Duration<$i, L_NOM, L_DENOM>
439        {
440            #[inline]
441            fn partial_cmp(&self, other: &Duration<$i, R_NOM, R_DENOM>) -> Option<Ordering> {
442                self.const_partial_cmp(*other)
443            }
444        }
445
446        impl<const NOM: u32, const DENOM: u32> Ord for Duration<$i, NOM, DENOM> {
447            #[inline]
448            fn cmp(&self, other: &Self) -> Ordering {
449                Self::_const_cmp(self.ticks, other.ticks)
450            }
451        }
452
453        impl<const L_NOM: u32, const L_DENOM: u32, const R_NOM: u32, const R_DENOM: u32>
454            PartialEq<Duration<$i, R_NOM, R_DENOM>> for Duration<$i, L_NOM, L_DENOM>
455        {
456            #[inline]
457            fn eq(&self, other: &Duration<$i, R_NOM, R_DENOM>) -> bool {
458                self.const_eq(*other)
459            }
460        }
461
462        impl<const NOM: u32, const DENOM: u32> Eq for Duration<$i, NOM, DENOM> {}
463
464        // Duration - Duration = Duration (only same base until const_generics_defaults is
465        // stabilized)
466        impl<const NOM: u32, const DENOM: u32> ops::Sub<Duration<$i, NOM, DENOM>>
467            for Duration<$i, NOM, DENOM>
468        {
469            type Output = Duration<$i, NOM, DENOM>;
470
471            #[inline]
472            fn sub(self, other: Duration<$i, NOM, DENOM>) -> Self::Output {
473                if let Some(v) = self.checked_sub(other) {
474                    v
475                } else {
476                    panic!("Sub failed!");
477                }
478            }
479        }
480
481        // Duration -= Duration
482        impl<const NOM: u32, const DENOM: u32> ops::SubAssign<Duration<$i, NOM, DENOM>>
483            for Duration<$i, NOM, DENOM>
484        {
485            #[inline]
486            fn sub_assign(&mut self, other: Self) {
487                *self = *self - other;
488            }
489        }
490
491        // Duration + Duration = Duration (only same base until const_generics_defaults is
492        // stabilized)
493        impl<const NOM: u32, const DENOM: u32> ops::Add<Duration<$i, NOM, DENOM>>
494            for Duration<$i, NOM, DENOM>
495        {
496            type Output = Duration<$i, NOM, DENOM>;
497
498            #[inline]
499            fn add(self, other: Duration<$i, NOM, DENOM>) -> Self::Output {
500                if let Some(v) = self.checked_add(other) {
501                    v
502                } else {
503                    panic!("Add failed!");
504                }
505            }
506        }
507
508        // Duration += Duration
509        impl<const NOM: u32, const DENOM: u32> ops::AddAssign<Duration<$i, NOM, DENOM>>
510            for Duration<$i, NOM, DENOM>
511        {
512            #[inline]
513            fn add_assign(&mut self, other: Self) {
514                *self = *self + other;
515            }
516        }
517
518        // integer * Duration = Duration
519        impl<const NOM: u32, const DENOM: u32> ops::Mul<Duration<$i, NOM, DENOM>> for u32 {
520            type Output = Duration<$i, NOM, DENOM>;
521
522            #[inline]
523            fn mul(self, mut other: Duration<$i, NOM, DENOM>) -> Self::Output {
524                other.ticks *= self as $i;
525                other
526            }
527        }
528
529        // Duration * integer = Duration
530        impl<const NOM: u32, const DENOM: u32> ops::Mul<u32> for Duration<$i, NOM, DENOM> {
531            type Output = Duration<$i, NOM, DENOM>;
532
533            #[inline]
534            fn mul(mut self, other: u32) -> Self::Output {
535                self.ticks *= other as $i;
536                self
537            }
538        }
539
540        // Duration *= integer
541        impl<const NOM: u32, const DENOM: u32> ops::MulAssign<u32>
542            for Duration<$i, NOM, DENOM>
543        {
544            #[inline]
545            fn mul_assign(&mut self, other: u32) {
546                *self = *self * other;
547            }
548        }
549
550        // Duration / integer = Duration
551        impl<const NOM: u32, const DENOM: u32> ops::Div<u32> for Duration<$i, NOM, DENOM> {
552            type Output = Duration<$i, NOM, DENOM>;
553
554            #[inline]
555            fn div(mut self, other: u32) -> Self::Output {
556                self.ticks /= other as $i;
557                self
558            }
559        }
560
561        // Duration /= integer
562        impl<const NOM: u32, const DENOM: u32> ops::DivAssign<u32>
563            for Duration<$i, NOM, DENOM>
564        {
565            #[inline]
566            fn div_assign(&mut self, other: u32) {
567                *self = *self / other;
568            }
569        }
570
571        // Duration / Duration = integer
572        impl<const L_NOM: u32, const L_DENOM: u32, const R_NOM: u32, const R_DENOM: u32> ops::Div<Duration<$i, R_NOM, R_DENOM>>
573            for Duration<$i, L_NOM, L_DENOM>
574        {
575            type Output = $i;
576
577            #[inline]
578            fn div(self, other: Duration<$i, R_NOM, R_DENOM>) -> Self::Output {
579                let conv: Duration<$i, R_NOM, R_DENOM> = self.convert();
580                conv.ticks / other.ticks
581            }
582        }
583
584        #[cfg(feature = "defmt")]
585        impl<const NOM: u32, const DENOM: u32> defmt::Format for Duration<$i, NOM, DENOM>
586        {
587            fn format(&self, f: defmt::Formatter) {
588                if NOM == 3_600 && DENOM == 1 {
589                    defmt::write!(f, "{} h", self.ticks)
590                } else if NOM == 60 && DENOM == 1 {
591                    defmt::write!(f, "{} min", self.ticks)
592                } else if NOM == 1 && DENOM == 1 {
593                    defmt::write!(f, "{} s", self.ticks)
594                } else if NOM == 1 && DENOM == 1_000 {
595                    defmt::write!(f, "{} ms", self.ticks)
596                } else if NOM == 1 && DENOM == 1_000_000 {
597                    defmt::write!(f, "{} us", self.ticks)
598                } else if NOM == 1 && DENOM == 1_000_000_000 {
599                    defmt::write!(f, "{} ns", self.ticks)
600                } else {
601                    defmt::write!(f, "{} ticks @ ({}/{})", self.ticks, NOM, DENOM)
602                }
603            }
604        }
605
606        impl<const NOM: u32, const DENOM: u32> core::fmt::Display for Duration<$i, NOM, DENOM> {
607            fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
608                if NOM == 3_600 && DENOM == 1 {
609                    write!(f, "{} h", self.ticks)
610                } else if NOM == 60 && DENOM == 1 {
611                    write!(f, "{} min", self.ticks)
612                } else if NOM == 1 && DENOM == 1 {
613                    write!(f, "{} s", self.ticks)
614                } else if NOM == 1 && DENOM == 1_000 {
615                    write!(f, "{} ms", self.ticks)
616                } else if NOM == 1 && DENOM == 1_000_000 {
617                    write!(f, "{} us", self.ticks)
618                } else if NOM == 1 && DENOM == 1_000_000_000 {
619                    write!(f, "{} ns", self.ticks)
620                } else {
621                    write!(f, "{} ticks @ ({}/{})", self.ticks, NOM, DENOM)
622                }
623            }
624        }
625    };
626}
627
628impl_duration_for_integer!(u32);
629impl_duration_for_integer!(u64);
630
631//
632// Operations between u32 and u64 Durations
633//
634
635impl<const NOM: u32, const DENOM: u32> From<Duration<u32, NOM, DENOM>>
636    for Duration<u64, NOM, DENOM>
637{
638    #[inline]
639    fn from(val: Duration<u32, NOM, DENOM>) -> Duration<u64, NOM, DENOM> {
640        Duration::<u64, NOM, DENOM>::from_ticks(val.ticks() as u64)
641    }
642}
643
644impl<const NOM: u32, const DENOM: u32> convert::TryFrom<Duration<u64, NOM, DENOM>>
645    for Duration<u32, NOM, DENOM>
646{
647    type Error = ();
648
649    #[inline]
650    fn try_from(val: Duration<u64, NOM, DENOM>) -> Result<Duration<u32, NOM, DENOM>, ()> {
651        Ok(Duration::<u32, NOM, DENOM>::from_ticks(
652            val.ticks().try_into().map_err(|_| ())?,
653        ))
654    }
655}
656
657// Duration - Duration = Duration (to make shorthands work, until const_generics_defaults is
658// stabilized)
659impl<const NOM: u32, const DENOM: u32> ops::Sub<Duration<u32, NOM, DENOM>>
660    for Duration<u64, NOM, DENOM>
661{
662    type Output = Duration<u64, NOM, DENOM>;
663
664    #[inline]
665    fn sub(self, other: Duration<u32, NOM, DENOM>) -> Self::Output {
666        if let Some(v) =
667            self.checked_sub(Duration::<u64, NOM, DENOM>::from_ticks(other.ticks() as u64))
668        {
669            v
670        } else {
671            panic!("Sub failed!");
672        }
673    }
674}
675
676// Duration -= Duration (to make shorthands work, until const_generics_defaults is stabilized)
677impl<const NOM: u32, const DENOM: u32> ops::SubAssign<Duration<u32, NOM, DENOM>>
678    for Duration<u64, NOM, DENOM>
679{
680    #[inline]
681    fn sub_assign(&mut self, other: Duration<u32, NOM, DENOM>) {
682        *self = *self - other;
683    }
684}
685
686// Duration + Duration = Duration (to make shorthands work, until const_generics_defaults is
687// stabilized)
688impl<const NOM: u32, const DENOM: u32> ops::Add<Duration<u32, NOM, DENOM>>
689    for Duration<u64, NOM, DENOM>
690{
691    type Output = Duration<u64, NOM, DENOM>;
692
693    #[inline]
694    fn add(self, other: Duration<u32, NOM, DENOM>) -> Self::Output {
695        if let Some(v) =
696            self.checked_add(Duration::<u64, NOM, DENOM>::from_ticks(other.ticks() as u64))
697        {
698            v
699        } else {
700            panic!("Add failed!");
701        }
702    }
703}
704
705// Duration += Duration (to make shorthands work, until const_generics_defaults is stabilized)
706impl<const NOM: u32, const DENOM: u32> ops::AddAssign<Duration<u32, NOM, DENOM>>
707    for Duration<u64, NOM, DENOM>
708{
709    #[inline]
710    fn add_assign(&mut self, other: Duration<u32, NOM, DENOM>) {
711        *self = *self + other;
712    }
713}
714
715impl<const L_NOM: u32, const L_DENOM: u32, const R_NOM: u32, const R_DENOM: u32>
716    PartialOrd<Duration<u32, R_NOM, R_DENOM>> for Duration<u64, L_NOM, L_DENOM>
717{
718    #[inline]
719    fn partial_cmp(&self, other: &Duration<u32, R_NOM, R_DENOM>) -> Option<Ordering> {
720        self.partial_cmp(&Duration::<u64, R_NOM, R_DENOM>::from_ticks(
721            other.ticks() as u64
722        ))
723    }
724}
725
726impl<const L_NOM: u32, const L_DENOM: u32, const R_NOM: u32, const R_DENOM: u32>
727    PartialEq<Duration<u32, R_NOM, R_DENOM>> for Duration<u64, L_NOM, L_DENOM>
728{
729    #[inline]
730    fn eq(&self, other: &Duration<u32, R_NOM, R_DENOM>) -> bool {
731        self.eq(&Duration::<u64, R_NOM, R_DENOM>::from_ticks(
732            other.ticks() as u64
733        ))
734    }
735}
736
737impl<const L_NOM: u32, const L_DENOM: u32, const R_NOM: u32, const R_DENOM: u32>
738    PartialOrd<Duration<u64, R_NOM, R_DENOM>> for Duration<u32, L_NOM, L_DENOM>
739{
740    #[inline]
741    fn partial_cmp(&self, other: &Duration<u64, R_NOM, R_DENOM>) -> Option<Ordering> {
742        Duration::<u64, L_NOM, L_DENOM>::from_ticks(self.ticks as u64).partial_cmp(other)
743    }
744}
745
746impl<const L_NOM: u32, const L_DENOM: u32, const R_NOM: u32, const R_DENOM: u32>
747    PartialEq<Duration<u64, R_NOM, R_DENOM>> for Duration<u32, L_NOM, L_DENOM>
748{
749    #[inline]
750    fn eq(&self, other: &Duration<u64, R_NOM, R_DENOM>) -> bool {
751        Duration::<u64, L_NOM, L_DENOM>::from_ticks(self.ticks as u64).eq(other)
752    }
753}
754
755/// Extension trait for simple short-hands for u32 Durations
756pub trait ExtU32 {
757    /// Shorthand for creating a duration which represents nanoseconds.
758    fn nanos<const NOM: u32, const DENOM: u32>(self) -> Duration<u32, NOM, DENOM>;
759
760    /// Shorthand for creating a duration which represents microseconds.
761    fn micros<const NOM: u32, const DENOM: u32>(self) -> Duration<u32, NOM, DENOM>;
762
763    /// Shorthand for creating a duration which represents milliseconds.
764    fn millis<const NOM: u32, const DENOM: u32>(self) -> Duration<u32, NOM, DENOM>;
765
766    /// Shorthand for creating a duration which represents seconds.
767    fn secs<const NOM: u32, const DENOM: u32>(self) -> Duration<u32, NOM, DENOM>;
768
769    /// Shorthand for creating a duration which represents minutes.
770    fn minutes<const NOM: u32, const DENOM: u32>(self) -> Duration<u32, NOM, DENOM>;
771
772    /// Shorthand for creating a duration which represents hours.
773    fn hours<const NOM: u32, const DENOM: u32>(self) -> Duration<u32, NOM, DENOM>;
774}
775
776impl ExtU32 for u32 {
777    #[inline]
778    fn nanos<const NOM: u32, const DENOM: u32>(self) -> Duration<u32, NOM, DENOM> {
779        Duration::<u32, NOM, DENOM>::nanos(self)
780    }
781
782    #[inline]
783    fn micros<const NOM: u32, const DENOM: u32>(self) -> Duration<u32, NOM, DENOM> {
784        Duration::<u32, NOM, DENOM>::micros(self)
785    }
786
787    #[inline]
788    fn millis<const NOM: u32, const DENOM: u32>(self) -> Duration<u32, NOM, DENOM> {
789        Duration::<u32, NOM, DENOM>::millis(self)
790    }
791
792    #[inline]
793    fn secs<const NOM: u32, const DENOM: u32>(self) -> Duration<u32, NOM, DENOM> {
794        Duration::<u32, NOM, DENOM>::secs(self)
795    }
796
797    #[inline]
798    fn minutes<const NOM: u32, const DENOM: u32>(self) -> Duration<u32, NOM, DENOM> {
799        Duration::<u32, NOM, DENOM>::minutes(self)
800    }
801
802    #[inline]
803    fn hours<const NOM: u32, const DENOM: u32>(self) -> Duration<u32, NOM, DENOM> {
804        Duration::<u32, NOM, DENOM>::hours(self)
805    }
806}
807
808/// Extension trait for simple short-hands for u32 Durations (ceil rounded)
809pub trait ExtU32Ceil {
810    /// Shorthand for creating a duration which represents nanoseconds.
811    fn nanos_at_least<const NOM: u32, const DENOM: u32>(self) -> Duration<u32, NOM, DENOM>;
812
813    /// Shorthand for creating a duration which represents microseconds.
814    fn micros_at_least<const NOM: u32, const DENOM: u32>(self) -> Duration<u32, NOM, DENOM>;
815
816    /// Shorthand for creating a duration which represents milliseconds.
817    fn millis_at_least<const NOM: u32, const DENOM: u32>(self) -> Duration<u32, NOM, DENOM>;
818
819    /// Shorthand for creating a duration which represents seconds.
820    fn secs_at_least<const NOM: u32, const DENOM: u32>(self) -> Duration<u32, NOM, DENOM>;
821
822    /// Shorthand for creating a duration which represents minutes.
823    fn minutes_at_least<const NOM: u32, const DENOM: u32>(self) -> Duration<u32, NOM, DENOM>;
824
825    /// Shorthand for creating a duration which represents hours.
826    fn hours_at_least<const NOM: u32, const DENOM: u32>(self) -> Duration<u32, NOM, DENOM>;
827}
828
829impl ExtU32Ceil for u32 {
830    #[inline]
831    fn nanos_at_least<const NOM: u32, const DENOM: u32>(self) -> Duration<u32, NOM, DENOM> {
832        Duration::<u32, NOM, DENOM>::nanos_at_least(self)
833    }
834
835    #[inline]
836    fn micros_at_least<const NOM: u32, const DENOM: u32>(self) -> Duration<u32, NOM, DENOM> {
837        Duration::<u32, NOM, DENOM>::micros_at_least(self)
838    }
839
840    #[inline]
841    fn millis_at_least<const NOM: u32, const DENOM: u32>(self) -> Duration<u32, NOM, DENOM> {
842        Duration::<u32, NOM, DENOM>::millis_at_least(self)
843    }
844
845    #[inline]
846    fn secs_at_least<const NOM: u32, const DENOM: u32>(self) -> Duration<u32, NOM, DENOM> {
847        Duration::<u32, NOM, DENOM>::secs_at_least(self)
848    }
849
850    #[inline]
851    fn minutes_at_least<const NOM: u32, const DENOM: u32>(self) -> Duration<u32, NOM, DENOM> {
852        Duration::<u32, NOM, DENOM>::minutes_at_least(self)
853    }
854
855    #[inline]
856    fn hours_at_least<const NOM: u32, const DENOM: u32>(self) -> Duration<u32, NOM, DENOM> {
857        Duration::<u32, NOM, DENOM>::hours_at_least(self)
858    }
859}
860
861/// Extension trait for simple short-hands for u64 Durations
862pub trait ExtU64 {
863    /// Shorthand for creating a duration which represents nanoseconds.
864    fn nanos<const NOM: u32, const DENOM: u32>(self) -> Duration<u64, NOM, DENOM>;
865
866    /// Shorthand for creating a duration which represents microseconds.
867    fn micros<const NOM: u32, const DENOM: u32>(self) -> Duration<u64, NOM, DENOM>;
868
869    /// Shorthand for creating a duration which represents milliseconds.
870    fn millis<const NOM: u32, const DENOM: u32>(self) -> Duration<u64, NOM, DENOM>;
871
872    /// Shorthand for creating a duration which represents seconds.
873    fn secs<const NOM: u32, const DENOM: u32>(self) -> Duration<u64, NOM, DENOM>;
874
875    /// Shorthand for creating a duration which represents minutes.
876    fn minutes<const NOM: u32, const DENOM: u32>(self) -> Duration<u64, NOM, DENOM>;
877
878    /// Shorthand for creating a duration which represents hours.
879    fn hours<const NOM: u32, const DENOM: u32>(self) -> Duration<u64, NOM, DENOM>;
880}
881
882impl ExtU64 for u64 {
883    #[inline]
884    fn nanos<const NOM: u32, const DENOM: u32>(self) -> Duration<u64, NOM, DENOM> {
885        Duration::<u64, NOM, DENOM>::nanos(self)
886    }
887
888    #[inline]
889    fn micros<const NOM: u32, const DENOM: u32>(self) -> Duration<u64, NOM, DENOM> {
890        Duration::<u64, NOM, DENOM>::micros(self)
891    }
892
893    #[inline]
894    fn millis<const NOM: u32, const DENOM: u32>(self) -> Duration<u64, NOM, DENOM> {
895        Duration::<u64, NOM, DENOM>::millis(self)
896    }
897
898    #[inline]
899    fn secs<const NOM: u32, const DENOM: u32>(self) -> Duration<u64, NOM, DENOM> {
900        Duration::<u64, NOM, DENOM>::secs(self)
901    }
902
903    #[inline]
904    fn minutes<const NOM: u32, const DENOM: u32>(self) -> Duration<u64, NOM, DENOM> {
905        Duration::<u64, NOM, DENOM>::minutes(self)
906    }
907
908    #[inline]
909    fn hours<const NOM: u32, const DENOM: u32>(self) -> Duration<u64, NOM, DENOM> {
910        Duration::<u64, NOM, DENOM>::hours(self)
911    }
912}
913
914/// Extension trait for simple short-hands for u64 Durations (ceil rounded)
915pub trait ExtU64Ceil {
916    /// Shorthand for creating a duration which represents nanoseconds.
917    fn nanos_at_least<const NOM: u32, const DENOM: u32>(self) -> Duration<u64, NOM, DENOM>;
918
919    /// Shorthand for creating a duration which represents microseconds.
920    fn micros_at_least<const NOM: u32, const DENOM: u32>(self) -> Duration<u64, NOM, DENOM>;
921
922    /// Shorthand for creating a duration which represents milliseconds.
923    fn millis_at_least<const NOM: u32, const DENOM: u32>(self) -> Duration<u64, NOM, DENOM>;
924
925    /// Shorthand for creating a duration which represents seconds.
926    fn secs_at_least<const NOM: u32, const DENOM: u32>(self) -> Duration<u64, NOM, DENOM>;
927
928    /// Shorthand for creating a duration which represents minutes.
929    fn minutes_at_least<const NOM: u32, const DENOM: u32>(self) -> Duration<u64, NOM, DENOM>;
930
931    /// Shorthand for creating a duration which represents hours.
932    fn hours_at_least<const NOM: u32, const DENOM: u32>(self) -> Duration<u64, NOM, DENOM>;
933}
934
935impl ExtU64Ceil for u64 {
936    #[inline]
937    fn nanos_at_least<const NOM: u32, const DENOM: u32>(self) -> Duration<u64, NOM, DENOM> {
938        Duration::<u64, NOM, DENOM>::nanos_at_least(self)
939    }
940
941    #[inline]
942    fn micros_at_least<const NOM: u32, const DENOM: u32>(self) -> Duration<u64, NOM, DENOM> {
943        Duration::<u64, NOM, DENOM>::micros_at_least(self)
944    }
945
946    #[inline]
947    fn millis_at_least<const NOM: u32, const DENOM: u32>(self) -> Duration<u64, NOM, DENOM> {
948        Duration::<u64, NOM, DENOM>::millis_at_least(self)
949    }
950
951    #[inline]
952    fn secs_at_least<const NOM: u32, const DENOM: u32>(self) -> Duration<u64, NOM, DENOM> {
953        Duration::<u64, NOM, DENOM>::secs_at_least(self)
954    }
955
956    #[inline]
957    fn minutes_at_least<const NOM: u32, const DENOM: u32>(self) -> Duration<u64, NOM, DENOM> {
958        Duration::<u64, NOM, DENOM>::minutes_at_least(self)
959    }
960
961    #[inline]
962    fn hours_at_least<const NOM: u32, const DENOM: u32>(self) -> Duration<u64, NOM, DENOM> {
963        Duration::<u64, NOM, DENOM>::hours_at_least(self)
964    }
965}