1use std::fmt::Debug;
18use std::ops::{Add, AddAssign};
19use std::time::{Duration, Instant as StdInstant};
20
21#[cfg(feature = "_internal_testkit_backend")]
22#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
23pub struct Instant(StdInstant);
24#[cfg(not(feature = "_internal_testkit_backend"))]
25#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
26pub(crate) struct Instant(StdInstant);
27
28impl Debug for Instant {
29 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
30 self.0.fmt(f)
31 }
32}
33
34impl Add<Duration> for Instant {
35 type Output = Self;
36
37 fn add(self, other: Duration) -> Self::Output {
38 Self(self.0 + other)
39 }
40}
41
42impl AddAssign<Duration> for Instant {
43 fn add_assign(&mut self, other: Duration) {
44 self.0 += other;
45 }
46}
47
48#[allow(dead_code)] impl Instant {
50 #[inline]
51 pub fn new(inner: StdInstant) -> Self {
52 Self(inner)
53 }
54
55 #[inline]
56 pub fn unmockable_now() -> Self {
57 Self(StdInstant::now())
58 }
59
60 #[inline]
61 pub fn raw(&self) -> StdInstant {
62 self.0
63 }
64
65 #[inline]
66 pub fn saturating_duration_since(&self, earlier: Self) -> Duration {
67 self.0.saturating_duration_since(earlier.0)
68 }
69
70 #[inline]
71 pub fn checked_duration_since(&self, earlier: Self) -> Option<Duration> {
72 self.0.checked_duration_since(earlier.0)
73 }
74
75 #[inline]
76 pub fn checked_add(&self, duration: Duration) -> Option<Self> {
77 self.0.checked_add(duration).map(Self)
78 }
79
80 #[inline]
81 pub fn checked_sub(&self, duration: Duration) -> Option<Self> {
82 self.0.checked_sub(duration).map(Self)
83 }
84}
85
86#[cfg(not(feature = "_internal_testkit_backend"))]
87impl Instant {
88 #[inline]
89 pub fn now() -> Self {
90 Self(StdInstant::now())
91 }
92
93 #[inline]
94 pub fn elapsed(&self) -> Duration {
95 self.0.elapsed()
96 }
97}
98
99#[cfg(feature = "_internal_testkit_backend")]
100mod mockable_time {
101 use super::*;
102 use std::sync::OnceLock;
103
104 use parking_lot::RwLock;
105
106 static MOCKED_TIME: OnceLock<RwLock<Option<StdInstant>>> = OnceLock::new();
107
108 #[inline]
109 fn mocked_time() -> &'static RwLock<Option<StdInstant>> {
110 MOCKED_TIME.get_or_init(Default::default)
111 }
112
113 impl Instant {
114 pub fn now() -> Self {
115 Self(mocked_time().read().unwrap_or_else(StdInstant::now))
116 }
117
118 #[inline]
119 pub fn elapsed(&self) -> Duration {
120 Self::now().0 - self.0
121 }
122 }
123
124 pub fn freeze_time() -> StdInstant {
125 let mut mocked_time = mocked_time().write();
126 mocked_time.replace(StdInstant::now());
127 mocked_time.unwrap()
128 }
129
130 pub fn tick(duration: Duration) -> Result<StdInstant, String> {
131 let mut mocked_time = mocked_time().write();
132 *mocked_time = mocked_time.map(|mocked_time| mocked_time + duration);
133 mocked_time
134 .as_ref()
135 .copied()
136 .ok_or_else(|| String::from("cannot tick time if not frozen"))
137 }
138
139 pub fn unfreeze_time() -> Result<(), String> {
140 let mut mocked_time = mocked_time().write();
141 mocked_time
142 .take()
143 .map(drop)
144 .ok_or_else(|| String::from("cannot unfreeze time if not frozen"))
145 }
146}
147
148#[cfg(feature = "_internal_testkit_backend")]
149pub use mockable_time::*;