fugit/
instant.rs

1use crate::duration::Duration;
2use crate::helpers::{self, Helpers};
3use core::cmp::Ordering;
4use core::ops;
5
6/// Represents an instant in time.
7///
8/// The generic `T` can either be `u32` or `u64`, and the const generics represent the ratio of the
9/// ticks contained within the instant: `instant in seconds = NOM / DENOM * ticks`
10#[derive(Clone, Copy, Debug)]
11pub struct Instant<T, const NOM: u32, const DENOM: u32> {
12    ticks: T,
13}
14
15macro_rules! impl_instant_for_integer {
16    ($i:ty) => {
17        impl<const NOM: u32, const DENOM: u32> Instant<$i, NOM, DENOM> {
18            /// Create an `Instant` from a ticks value.
19            ///
20            /// ```
21            /// # use fugit::*;
22            #[doc = concat!("let _i = Instant::<", stringify!($i), ", 1, 1_000>::from_ticks(1);")]
23            /// ```
24            #[inline]
25            pub const fn from_ticks(ticks: $i) -> Self {
26                helpers::greater_than_0::<NOM>();
27                helpers::greater_than_0::<DENOM>();
28
29                Instant { ticks }
30            }
31
32            /// Extract the ticks from an `Instant`.
33            ///
34            /// ```
35            /// # use fugit::*;
36            #[doc = concat!("let i = Instant::<", stringify!($i), ", 1, 1_000>::from_ticks(234);")]
37            ///
38            /// assert_eq!(i.ticks(), 234);
39            /// ```
40            #[inline]
41            pub const fn ticks(&self) -> $i {
42                self.ticks
43            }
44
45            /// Const comparison of `Instant`s.
46            ///
47            /// ```
48            /// # use fugit::*;
49            #[doc = concat!("let i1 = Instant::<", stringify!($i), ", 1, 1_000>::from_ticks(1);")]
50            #[doc = concat!("let i2 = Instant::<", stringify!($i), ", 1, 1_000>::from_ticks(2);")]
51            ///
52            /// assert_eq!(i1.const_cmp(i2), core::cmp::Ordering::Less);
53            /// ```
54            ///
55            /// This function takes into account that ticks might wrap around. If the absolute
56            /// values of `self` and `other` differ by more than half the possible range, it is
57            /// assumed that an overflow occured and the result is reversed:
58            ///
59            /// ```
60            /// # use fugit::*;
61            #[doc = concat!("let i1 = Instant::<", stringify!($i), ", 1, 1_000>::from_ticks(", stringify!($i),"::MAX);")]
62            #[doc = concat!("let i2 = Instant::<", stringify!($i), ", 1, 1_000>::from_ticks(1);")]
63            ///
64            /// assert_eq!(i1.const_cmp(i2), core::cmp::Ordering::Less);
65            /// ```
66            #[inline]
67            pub const fn const_cmp(self, other: Self) -> Ordering {
68                if self.ticks == other.ticks {
69                    Ordering::Equal
70                } else {
71                    let v = self.ticks.wrapping_sub(other.ticks);
72
73                    // not using `v.cmp(<$i>::MAX / 2).reverse()` due to `cmp` being non-const
74                    if v > <$i>::MAX / 2 {
75                        Ordering::Less
76                    } else if v < <$i>::MAX / 2 {
77                        Ordering::Greater
78                    } else {
79                        Ordering::Equal
80                    }
81                }
82            }
83
84            /// Duration between since the start of the `Instant`. This assumes an instant which
85            /// won't wrap within the execution of the program.
86            ///
87            /// ```
88            /// # use fugit::*;
89            #[doc = concat!("let i = Instant::<", stringify!($i), ", 1, 1_000>::from_ticks(11);")]
90            ///
91            /// assert_eq!(i.duration_since_epoch().ticks(), 11);
92            /// ```
93            #[inline]
94            pub const fn duration_since_epoch(self) -> Duration<$i, NOM, DENOM> {
95                Duration::<$i, NOM, DENOM>::from_ticks(self.ticks())
96            }
97
98            /// Duration between `Instant`s.
99            ///
100            /// ```
101            /// # use fugit::*;
102            #[doc = concat!("let i1 = Instant::<", stringify!($i), ", 1, 1_000>::from_ticks(1);")]
103            #[doc = concat!("let i2 = Instant::<", stringify!($i), ", 1, 1_000>::from_ticks(2);")]
104            ///
105            /// assert_eq!(i1.checked_duration_since(i2), None);
106            /// assert_eq!(i2.checked_duration_since(i1).unwrap().ticks(), 1);
107            /// ```
108            #[inline]
109            pub const fn checked_duration_since(
110                self,
111                other: Self,
112            ) -> Option<Duration<$i, NOM, DENOM>> {
113                match self.const_cmp(other) {
114                    Ordering::Greater | Ordering::Equal => {
115                        Some(Duration::<$i, NOM, DENOM>::from_ticks(
116                            self.ticks.wrapping_sub(other.ticks),
117                        ))
118                    }
119                    Ordering::Less => None,
120                }
121            }
122
123            /// Subtract a `Duration` from an `Instant` while checking for overflow.
124            ///
125            /// ```
126            /// # use fugit::*;
127            #[doc = concat!("let i = Instant::<", stringify!($i), ", 1, 1_000>::from_ticks(1);")]
128            #[doc = concat!("let d = Duration::<", stringify!($i), ", 1, 1_000>::from_ticks(1);")]
129            ///
130            /// assert_eq!(i.checked_sub_duration(d).unwrap().ticks(), 0);
131            /// ```
132            pub const fn checked_sub_duration<const O_NOM: u32, const O_DENOM: u32>(
133                self,
134                other: Duration<$i, O_NOM, O_DENOM>,
135            ) -> Option<Self> {
136                if Helpers::<NOM, DENOM, O_NOM, O_DENOM>::SAME_BASE {
137                    Some(Instant::<$i, NOM, DENOM>::from_ticks(
138                        self.ticks.wrapping_sub(other.ticks()),
139                    ))
140                } else {
141                    if let Some(lh) = other
142                        .ticks()
143                        .checked_mul(Helpers::<NOM, DENOM, O_NOM, O_DENOM>::LD_TIMES_RN as $i)
144                    {
145                        let ticks = lh / Helpers::<NOM, DENOM, O_NOM, O_DENOM>::RD_TIMES_LN as $i;
146
147                        Some(Instant::<$i, NOM, DENOM>::from_ticks(
148                            self.ticks.wrapping_sub(ticks),
149                        ))
150                    } else {
151                        None
152                    }
153                }
154            }
155
156            /// Add a `Duration` to an `Instant` while checking for overflow.
157            ///
158            /// ```
159            /// # use fugit::*;
160            #[doc = concat!("let i = Instant::<", stringify!($i), ", 1, 1_000>::from_ticks(1);")]
161            #[doc = concat!("let d = Duration::<", stringify!($i), ", 1, 1_000>::from_ticks(1);")]
162            ///
163            /// assert_eq!(i.checked_add_duration(d).unwrap().ticks(), 2);
164            /// ```
165            pub const fn checked_add_duration<const O_NOM: u32, const O_DENOM: u32>(
166                self,
167                other: Duration<$i, O_NOM, O_DENOM>,
168            ) -> Option<Self> {
169                if Helpers::<NOM, DENOM, O_NOM, O_DENOM>::SAME_BASE {
170                    Some(Instant::<$i, NOM, DENOM>::from_ticks(
171                        self.ticks.wrapping_add(other.ticks()),
172                    ))
173                } else {
174                    if let Some(lh) = other
175                        .ticks()
176                        .checked_mul(Helpers::<NOM, DENOM, O_NOM, O_DENOM>::LD_TIMES_RN as $i)
177                    {
178                        let ticks = lh / Helpers::<NOM, DENOM, O_NOM, O_DENOM>::RD_TIMES_LN as $i;
179
180                        Some(Instant::<$i, NOM, DENOM>::from_ticks(
181                            self.ticks.wrapping_add(ticks),
182                        ))
183                    } else {
184                        None
185                    }
186                }
187            }
188        }
189
190        impl<const NOM: u32, const DENOM: u32> PartialOrd for Instant<$i, NOM, DENOM> {
191            /// This implementation deviates from the definition of
192            /// [PartialOrd::partial_cmp](core::cmp::PartialOrd::partial_cmp):
193            ///
194            /// It takes into account that ticks might wrap around. If the absolute
195            /// values of `self` and `other` differ by more than half the possible range, it is
196            /// assumed that an overflow occured and the result is reversed.
197            ///
198            /// That breaks the transitivity invariant: a < b and b < c no longer implies a < c.
199            #[inline]
200            fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
201                Some(self.const_cmp(*other))
202            }
203        }
204
205        impl<const NOM: u32, const DENOM: u32> Ord for Instant<$i, NOM, DENOM> {
206            /// This implementation deviates from the definition of
207            /// [Ord::cmp](core::cmp::Ord::cmp):
208            ///
209            /// It takes into account that ticks might wrap around. If the absolute
210            /// values of `self` and `other` differ by more than half the possible range, it is
211            /// assumed that an overflow occured and the result is reversed.
212            ///
213            /// That breaks the transitivity invariant: a < b and b < c no longer implies a < c.
214            #[inline]
215            fn cmp(&self, other: &Self) -> Ordering {
216                self.const_cmp(*other)
217            }
218        }
219
220        impl<const NOM: u32, const DENOM: u32> PartialEq for Instant<$i, NOM, DENOM> {
221            #[inline]
222            fn eq(&self, other: &Self) -> bool {
223                self.ticks.eq(&other.ticks)
224            }
225        }
226
227        impl<const NOM: u32, const DENOM: u32> Eq for Instant<$i, NOM, DENOM> {}
228
229        // Instant - Instant = Duration
230        // We have limited this to use same numerator and denominator in both left and right hand sides,
231        // this allows for the extension traits to work. For usage with different fraction, use
232        // `checked_duration_since`.
233        impl<const NOM: u32, const DENOM: u32> ops::Sub<Instant<$i, NOM, DENOM>>
234            for Instant<$i, NOM, DENOM>
235        {
236            type Output = Duration<$i, NOM, DENOM>;
237
238            #[inline]
239            fn sub(self, other: Self) -> Self::Output {
240                if let Some(v) = self.checked_duration_since(other) {
241                    v
242                } else {
243                    panic!("Sub failed! Other > self");
244                }
245            }
246        }
247
248        // Instant - Duration = Instant
249        // We have limited this to use same numerator and denominator in both left and right hand sides,
250        // this allows for the extension traits to work. For usage with different fraction, use
251        // `checked_sub_duration`.
252        impl<const NOM: u32, const DENOM: u32> ops::Sub<Duration<$i, NOM, DENOM>>
253            for Instant<$i, NOM, DENOM>
254        {
255            type Output = Instant<$i, NOM, DENOM>;
256
257            #[inline]
258            fn sub(self, other: Duration<$i, NOM, DENOM>) -> Self::Output {
259                if let Some(v) = self.checked_sub_duration(other) {
260                    v
261                } else {
262                    panic!("Sub failed! Overflow");
263                }
264            }
265        }
266
267        // Instant -= Duration
268        // We have limited this to use same numerator and denominator in both left and right hand sides,
269        // this allows for the extension traits to work. For usage with different fraction, use
270        // `checked_sub_duration`.
271        impl<const NOM: u32, const DENOM: u32> ops::SubAssign<Duration<$i, NOM, DENOM>>
272            for Instant<$i, NOM, DENOM>
273        {
274            #[inline]
275            fn sub_assign(&mut self, other: Duration<$i, NOM, DENOM>) {
276                *self = *self - other;
277            }
278        }
279
280        // Instant + Duration = Instant
281        // We have limited this to use same numerator and denominator in both left and right hand sides,
282        // this allows for the extension traits to work. For usage with different fraction, use
283        // `checked_add_duration`.
284        impl<const NOM: u32, const DENOM: u32> ops::Add<Duration<$i, NOM, DENOM>>
285            for Instant<$i, NOM, DENOM>
286        {
287            type Output = Instant<$i, NOM, DENOM>;
288
289            #[inline]
290            fn add(self, other: Duration<$i, NOM, DENOM>) -> Self::Output {
291                if let Some(v) = self.checked_add_duration(other) {
292                    v
293                } else {
294                    panic!("Add failed! Overflow");
295                }
296            }
297        }
298
299        // Instant += Duration
300        // We have limited this to use same numerator and denominator in both left and right hand sides,
301        // this allows for the extension traits to work. For usage with different fraction, use
302        // `checked_add_duration`.
303        impl<const NOM: u32, const DENOM: u32> ops::AddAssign<Duration<$i, NOM, DENOM>>
304            for Instant<$i, NOM, DENOM>
305        {
306            #[inline]
307            fn add_assign(&mut self, other: Duration<$i, NOM, DENOM>) {
308                *self = *self + other;
309            }
310        }
311
312        #[cfg(feature = "defmt")]
313        impl<const NOM: u32, const DENOM: u32> defmt::Format for Instant<$i, NOM, DENOM> {
314            fn format(&self, f: defmt::Formatter) {
315                if NOM == 3_600 && DENOM == 1 {
316                    defmt::write!(f, "{} h", self.ticks)
317                } else if NOM == 60 && DENOM == 1 {
318                    defmt::write!(f, "{} min", self.ticks)
319                } else if NOM == 1 && DENOM == 1 {
320                    defmt::write!(f, "{} s", self.ticks)
321                } else if NOM == 1 && DENOM == 1_000 {
322                    defmt::write!(f, "{} ms", self.ticks)
323                } else if NOM == 1 && DENOM == 1_000_000 {
324                    defmt::write!(f, "{} us", self.ticks)
325                } else if NOM == 1 && DENOM == 1_000_000_000 {
326                    defmt::write!(f, "{} ns", self.ticks)
327                } else {
328                    defmt::write!(f, "{} ticks @ ({}/{})", self.ticks, NOM, DENOM)
329                }
330            }
331        }
332
333        impl<const NOM: u32, const DENOM: u32> core::fmt::Display for Instant<$i, NOM, DENOM> {
334            fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
335                if NOM == 3_600 && DENOM == 1 {
336                    write!(f, "{} h", self.ticks)
337                } else if NOM == 60 && DENOM == 1 {
338                    write!(f, "{} min", self.ticks)
339                } else if NOM == 1 && DENOM == 1 {
340                    write!(f, "{} s", self.ticks)
341                } else if NOM == 1 && DENOM == 1_000 {
342                    write!(f, "{} ms", self.ticks)
343                } else if NOM == 1 && DENOM == 1_000_000 {
344                    write!(f, "{} us", self.ticks)
345                } else if NOM == 1 && DENOM == 1_000_000_000 {
346                    write!(f, "{} ns", self.ticks)
347                } else {
348                    write!(f, "{} ticks @ ({}/{})", self.ticks, NOM, DENOM)
349                }
350            }
351        }
352    };
353}
354
355impl_instant_for_integer!(u32);
356impl_instant_for_integer!(u64);
357
358//
359// Operations between u32 Duration and u64 Instant
360//
361
362// Instant - Duration = Instant
363// We have limited this to use same numerator and denominator in both left and right hand sides,
364// this allows for the extension traits to work. For usage with different fraction, use
365// `checked_sub_duration`.
366impl<const NOM: u32, const DENOM: u32> ops::Sub<Duration<u32, NOM, DENOM>>
367    for Instant<u64, NOM, DENOM>
368{
369    type Output = Instant<u64, NOM, DENOM>;
370
371    #[inline]
372    fn sub(self, other: Duration<u32, NOM, DENOM>) -> Self::Output {
373        if let Some(v) = self.checked_sub_duration(other.into()) {
374            v
375        } else {
376            panic!("Sub failed! Overflow");
377        }
378    }
379}
380
381// Instant -= Duration
382// We have limited this to use same numerator and denominator in both left and right hand sides,
383// this allows for the extension traits to work. For usage with different fraction, use
384// `checked_sub_duration`.
385impl<const NOM: u32, const DENOM: u32> ops::SubAssign<Duration<u32, NOM, DENOM>>
386    for Instant<u64, NOM, DENOM>
387{
388    #[inline]
389    fn sub_assign(&mut self, other: Duration<u32, NOM, DENOM>) {
390        *self = *self - other;
391    }
392}
393
394// Instant + Duration = Instant
395// We have limited this to use same numerator and denominator in both left and right hand sides,
396// this allows for the extension traits to work. For usage with different fraction, use
397// `checked_add_duration`.
398impl<const NOM: u32, const DENOM: u32> ops::Add<Duration<u32, NOM, DENOM>>
399    for Instant<u64, NOM, DENOM>
400{
401    type Output = Instant<u64, NOM, DENOM>;
402
403    #[inline]
404    fn add(self, other: Duration<u32, NOM, DENOM>) -> Self::Output {
405        if let Some(v) = self.checked_add_duration(other.into()) {
406            v
407        } else {
408            panic!("Add failed! Overflow");
409        }
410    }
411}
412
413// Instant += Duration
414// We have limited this to use same numerator and denominator in both left and right hand sides,
415// this allows for the extension traits to work. For usage with different fraction, use
416// `checked_add_duration`.
417impl<const NOM: u32, const DENOM: u32> ops::AddAssign<Duration<u32, NOM, DENOM>>
418    for Instant<u64, NOM, DENOM>
419{
420    #[inline]
421    fn add_assign(&mut self, other: Duration<u32, NOM, DENOM>) {
422        *self = *self + other;
423    }
424}
425
426// impl<const L_NOM: u32, const L_DENOM: u32, const R_NOM: u32, const R_DENOM: u32>
427//     ops::Add<Duration<u32, R_NOM, R_DENOM>> for Duration<u64, L_NOM, L_DENOM>
428// {
429//     type Output = Duration<u64, L_NOM, L_DENOM>;
430//
431//     #[inline]
432//     fn add(self, other: Duration<u32, R_NOM, R_DENOM>) -> Self::Output {
433//         self.add(Duration::<u64, L_NOM, L_DENOM>::from_ticks(
434//             other.ticks() as u64
435//         ))
436//     }
437// }