1 | use std::time::Instant as StdInstant; |
2 | |
3 | use crate::Duration; |
4 | |
5 | /// Sealed trait to prevent downstream implementations. |
6 | mod sealed { |
7 | /// A trait that cannot be implemented by downstream users. |
8 | pub trait Sealed: Sized {} |
9 | impl Sealed for std::time::Instant {} |
10 | } |
11 | |
12 | /// An extension trait for [`std::time::Instant`] that adds methods for |
13 | /// [`time::Duration`](Duration)s. |
14 | pub trait InstantExt: sealed::Sealed { |
15 | /// # Panics |
16 | /// |
17 | /// This function may panic if the resulting point in time cannot be represented by the |
18 | /// underlying data structure. See [`InstantExt::checked_add_signed`] for a non-panicking |
19 | /// version. |
20 | fn add_signed(self, duration: Duration) -> Self { |
21 | self.checked_add_signed(duration) |
22 | .expect("overflow when adding duration to instant" ) |
23 | } |
24 | |
25 | /// # Panics |
26 | /// |
27 | /// This function may panic if the resulting point in time cannot be represented by the |
28 | /// underlying data structure. See [`InstantExt::checked_sub_signed`] for a non-panicking |
29 | /// version. |
30 | fn sub_signed(self, duration: Duration) -> Self { |
31 | self.checked_sub_signed(duration) |
32 | .expect("overflow when subtracting duration from instant" ) |
33 | } |
34 | |
35 | /// Returns `Some(t)` where `t` is the time `self.checked_add_signed(duration)` if `t` can be |
36 | /// represented as `Instant` (which means it's inside the bounds of the underlying data |
37 | /// structure), `None` otherwise. |
38 | fn checked_add_signed(&self, duration: Duration) -> Option<Self>; |
39 | |
40 | /// Returns `Some(t)` where `t` is the time `self.checked_sub_signed(duration)` if `t` can be |
41 | /// represented as `Instant` (which means it's inside the bounds of the underlying data |
42 | /// structure), `None` otherwise. |
43 | fn checked_sub_signed(&self, duration: Duration) -> Option<Self>; |
44 | |
45 | /// Returns the amount of time elapsed from another instant to this one. This will be negative |
46 | /// if `earlier` is later than `self`. |
47 | /// |
48 | /// # Example |
49 | /// |
50 | /// ```rust |
51 | /// # use std::thread::sleep; |
52 | /// # use std::time::{Duration, Instant}; |
53 | /// # use time::ext::InstantExt; |
54 | /// let now = Instant::now(); |
55 | /// sleep(Duration::new(1, 0)); |
56 | /// let new_now = Instant::now(); |
57 | /// println!("{:?}" , new_now.signed_duration_since(now)); // positive |
58 | /// println!("{:?}" , now.signed_duration_since(new_now)); // negative |
59 | /// ``` |
60 | fn signed_duration_since(&self, earlier: Self) -> Duration; |
61 | } |
62 | |
63 | impl InstantExt for StdInstant { |
64 | fn checked_add_signed(&self, duration: Duration) -> Option<Self> { |
65 | if duration.is_positive() { |
66 | self.checked_add(duration.unsigned_abs()) |
67 | } else if duration.is_negative() { |
68 | #[allow (clippy::unchecked_duration_subtraction)] |
69 | self.checked_sub(duration.unsigned_abs()) |
70 | } else { |
71 | debug_assert!(duration.is_zero()); |
72 | Some(*self) |
73 | } |
74 | } |
75 | |
76 | fn checked_sub_signed(&self, duration: Duration) -> Option<Self> { |
77 | if duration.is_positive() { |
78 | #[allow (clippy::unchecked_duration_subtraction)] |
79 | self.checked_sub(duration.unsigned_abs()) |
80 | } else if duration.is_negative() { |
81 | self.checked_add(duration.unsigned_abs()) |
82 | } else { |
83 | debug_assert!(duration.is_zero()); |
84 | Some(*self) |
85 | } |
86 | } |
87 | |
88 | fn signed_duration_since(&self, earlier: Self) -> Duration { |
89 | if *self > earlier { |
90 | self.saturating_duration_since(earlier) |
91 | .try_into() |
92 | .unwrap_or(Duration::MAX) |
93 | } else { |
94 | earlier |
95 | .saturating_duration_since(*self) |
96 | .try_into() |
97 | .map_or(Duration::MIN, |d: Duration| -d) |
98 | } |
99 | } |
100 | } |
101 | |