1use std::ops::{Add, AddAssign, Sub, SubAssign};
2use std::time::Duration;
3
4use crate::{maybe_fut_constructor_sync, maybe_fut_method_sync};
5
6#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Unwrap)]
8#[unwrap_types(
9 std(std::time::Instant),
10 tokio(tokio::time::Instant),
11 tokio_gated("tokio-time")
12)]
13pub struct Instant(InstantInner);
14
15#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd)]
16enum InstantInner {
17 Std(std::time::Instant),
19 #[cfg(tokio_time)]
21 #[cfg_attr(docsrs, doc(cfg(feature = "tokio-time")))]
22 Tokio(tokio::time::Instant),
23}
24
25impl From<std::time::Instant> for Instant {
26 fn from(instant: std::time::Instant) -> Self {
27 Instant(InstantInner::Std(instant))
28 }
29}
30
31#[cfg(tokio_time)]
32#[cfg_attr(docsrs, doc(cfg(feature = "tokio-time")))]
33impl From<tokio::time::Instant> for Instant {
34 fn from(instant: tokio::time::Instant) -> Self {
35 Instant(InstantInner::Tokio(instant))
36 }
37}
38
39impl Add<Duration> for Instant {
40 type Output = Self;
41
42 fn add(self, other: Duration) -> Self::Output {
43 #[cfg(tokio_time)]
45 {
46 let is_async = matches!(self.0, InstantInner::Tokio(_));
47 let a = match self.0 {
48 InstantInner::Std(a) => a,
49 #[cfg(tokio_time)]
50 InstantInner::Tokio(a) => a.into_std(),
51 };
52 if is_async {
54 Instant(InstantInner::Tokio((a + other).into()))
55 } else {
56 Instant(InstantInner::Std(a + other))
57 }
58 }
59 #[cfg(not(tokio_time))]
60 {
61 use crate::unwrap::Unwrap as _;
62 Instant(InstantInner::Std(self.unwrap_std() + other))
63 }
64 }
65}
66
67impl AddAssign<Duration> for Instant {
68 fn add_assign(&mut self, other: Duration) {
69 #[cfg(tokio_time)]
70 {
71 let is_async = matches!(self.0, InstantInner::Tokio(_));
73 let a = match self.0 {
74 InstantInner::Std(a) => a,
75 #[cfg(tokio_time)]
76 InstantInner::Tokio(a) => a.into_std(),
77 };
78 if is_async {
80 self.0 = InstantInner::Tokio((a + other).into());
81 } else {
82 self.0 = InstantInner::Std(a + other);
83 }
84 }
85 #[cfg(not(tokio_time))]
86 {
87 use crate::unwrap::Unwrap as _;
89 *self = (self.unwrap_std() + other).into();
90 }
91 }
92}
93
94impl Sub for Instant {
95 type Output = std::time::Duration;
96
97 fn sub(self, other: Instant) -> Self::Output {
98 let a = match self.0 {
100 InstantInner::Std(a) => a,
101 #[cfg(tokio_time)]
102 InstantInner::Tokio(a) => a.into_std(),
103 };
104 let b = match other.0 {
105 InstantInner::Std(b) => b,
106 #[cfg(tokio_time)]
107 InstantInner::Tokio(b) => b.into_std(),
108 };
109 a - b
111 }
112}
113
114impl SubAssign<Duration> for Instant {
115 fn sub_assign(&mut self, other: Duration) {
116 #[cfg(tokio_time)]
117 {
118 let is_async = matches!(self.0, InstantInner::Tokio(_));
119
120 let a = match self.0 {
122 InstantInner::Std(a) => a,
123 #[cfg(tokio_time)]
124 InstantInner::Tokio(a) => a.into_std(),
125 };
126
127 if is_async {
129 self.0 = InstantInner::Tokio((a - other).into());
130 } else {
131 self.0 = InstantInner::Std(a - other);
132 }
133 }
134 #[cfg(not(tokio_time))]
135 {
136 use crate::unwrap::Unwrap as _;
137 *self = (self.unwrap_std() - other).into();
139 }
140 }
141}
142
143impl Instant {
144 maybe_fut_constructor_sync!(
145 now() -> Self,
147 std::time::Instant::now,
148 tokio::time::Instant::now,
149 tokio_time
150 );
151
152 maybe_fut_method_sync!(
153 elapsed() -> Duration,
155 InstantInner::Std,
156 InstantInner::Tokio,
157 tokio_time
158 );
159
160 pub fn checked_add(&self, duration: Duration) -> Option<Self> {
162 match self.0 {
163 InstantInner::Std(a) => Some(InstantInner::Std(a.checked_add(duration)?)),
164 #[cfg(tokio_time)]
165 InstantInner::Tokio(a) => Some(InstantInner::Tokio(a.checked_add(duration)?)),
166 }
167 .map(Instant)
168 }
169
170 pub fn checked_sub(&self, duration: Duration) -> Option<Self> {
172 #[cfg(tokio_time)]
173 {
174 let is_async = matches!(self.0, InstantInner::Tokio(_));
175
176 let a = match self.0 {
178 InstantInner::Std(a) => a,
179 #[cfg(tokio_time)]
180 InstantInner::Tokio(a) => a.into_std(),
181 };
182
183 if is_async {
185 Some(InstantInner::Tokio(a.checked_sub(duration)?.into()))
186 } else {
187 Some(InstantInner::Std(a.checked_sub(duration)?))
188 }
189 .map(Instant)
190 }
191 #[cfg(not(tokio_time))]
192 {
193 use crate::unwrap::Unwrap as _;
195 let a = self.unwrap_std();
196
197 Some(InstantInner::Std(a.checked_sub(duration)?)).map(Instant)
199 }
200 }
201
202 pub fn duration_since(&self, earlier: Instant) -> Duration {
203 let a = match self.0 {
205 InstantInner::Std(a) => a,
206 #[cfg(tokio_time)]
207 InstantInner::Tokio(a) => a.into_std(),
208 };
209 let b = match earlier.0 {
210 InstantInner::Std(b) => b,
211 #[cfg(tokio_time)]
212 InstantInner::Tokio(b) => b.into_std(),
213 };
214
215 a.duration_since(b)
217 }
218
219 pub fn checked_duration_since(&self, earlier: Instant) -> Option<Duration> {
221 let a = match self.0 {
223 InstantInner::Std(a) => a,
224 #[cfg(tokio_time)]
225 InstantInner::Tokio(a) => a.into_std(),
226 };
227 let b = match earlier.0 {
228 InstantInner::Std(b) => b,
229 #[cfg(tokio_time)]
230 InstantInner::Tokio(b) => b.into_std(),
231 };
232
233 a.checked_duration_since(b)
235 }
236
237 pub fn saturating_duration_since(&self, earlier: Instant) -> Duration {
239 let a = match self.0 {
241 InstantInner::Std(a) => a,
242 #[cfg(tokio_time)]
243 InstantInner::Tokio(a) => a.into_std(),
244 };
245 let b = match earlier.0 {
246 InstantInner::Std(b) => b,
247 #[cfg(tokio_time)]
248 InstantInner::Tokio(b) => b.into_std(),
249 };
250
251 a.saturating_duration_since(b)
253 }
254}
255
256#[cfg(test)]
257mod test {
258
259 use super::*;
260
261 #[test]
262 fn test_instant_add() {
263 let instant = Instant::now();
264 let duration = Duration::new(1, 0);
265 let new_instant = instant + duration;
266 assert!(new_instant > instant);
267 }
268
269 #[test]
270 fn test_instant_sub() {
271 let instant1 = Instant::now();
272 let instant2 = Instant::now();
273 let duration = instant1 - instant2;
274 assert!(duration >= Duration::new(0, 0));
275 }
276
277 #[test]
278 fn test_instant_checked_add() {
279 let instant = Instant::now();
280 let duration = Duration::new(1, 0);
281 let new_instant = instant.checked_add(duration).unwrap();
282 assert!(new_instant > instant);
283 }
284
285 #[test]
286 fn test_instant_checked_sub() {
287 let instant1 = Instant::now();
288 let duration = Duration::new(1, 0);
289 let new_instant = instant1.checked_sub(duration).unwrap();
290 assert!(new_instant < instant1);
291
292 assert!(matches!(new_instant.0, InstantInner::Std(_)));
294 }
295
296 #[cfg(tokio_time)]
297 #[tokio::test]
298 async fn test_instant_checked_sub_async() {
299 let instant1 = Instant::now();
300 let duration = Duration::new(1, 0);
301 let new_instant = instant1.checked_sub(duration).unwrap();
302 assert!(new_instant < instant1);
303
304 assert!(matches!(new_instant.0, InstantInner::Tokio(_)));
306 }
307
308 #[test]
309 fn test_instant_duration_since() {
310 let instant1 = Instant::now();
311 let instant2 = Instant::now();
312 let duration = instant1.duration_since(instant2);
313 assert!(duration >= Duration::new(0, 0));
314 }
315
316 #[test]
317 fn test_instant_checked_duration_since() {
318 let instant2 = Instant::now();
319 std::thread::sleep(Duration::from_millis(100));
320 let instant1 = Instant::now();
321 let duration = instant1.checked_duration_since(instant2);
322 assert!(duration.is_some());
323 assert!(duration.unwrap() >= Duration::new(0, 0));
324 }
325
326 #[test]
327 fn test_instant_saturating_duration_since() {
328 let instant1 = Instant::now();
329 let instant2 = Instant::now();
330 let duration = instant1.saturating_duration_since(instant2);
331 assert!(duration >= Duration::new(0, 0));
332 }
333
334 #[test]
335 fn test_instant_elapsed() {
336 let instant = Instant::now();
337 std::thread::sleep(Duration::from_millis(100));
338 let elapsed = instant.elapsed();
339 assert!(elapsed >= Duration::from_millis(100));
340 }
341
342 #[cfg(tokio_time)]
343 #[tokio::test]
344 async fn test_instant_elapsed_async() {
345 let instant = Instant::now();
346 tokio::time::sleep(Duration::from_millis(100)).await;
347 let elapsed = instant.elapsed();
348 assert!(elapsed >= Duration::from_millis(100));
349 }
350
351 #[test]
352 fn test_instant_now() {
353 let instant = Instant::now();
354 assert!(instant.elapsed() >= Duration::new(0, 0));
355
356 assert!(matches!(instant.0, InstantInner::Std(_)));
357 }
358
359 #[cfg(tokio_time)]
360 #[tokio::test]
361 async fn test_instant_now_async() {
362 let instant = Instant::now();
363 assert!(instant.elapsed() >= Duration::new(0, 0));
364
365 assert!(matches!(instant.0, InstantInner::Tokio(_)));
366 }
367
368 #[test]
369 fn test_instant_checked_add_none() {
370 let instant = Instant::now();
371 let duration = Duration::new(u64::MAX, 0);
372 let new_instant = instant.checked_add(duration);
373 assert!(new_instant.is_none());
374 }
375
376 #[test]
377 fn test_instant_checked_sub_none() {
378 let instant = Instant::now();
379 let duration = Duration::new(u64::MAX, 0);
380 let new_instant = instant.checked_sub(duration);
381 assert!(new_instant.is_none());
382 }
383
384 #[cfg(tokio_time)]
385 #[tokio::test]
386 async fn test_instant_checked_sub_async_none() {
387 let instant = Instant::now();
388 let duration = Duration::new(u64::MAX, 0);
389 let new_instant = instant.checked_sub(duration);
390 assert!(new_instant.is_none());
391 }
392
393 #[test]
394 fn test_instant_saturating_duration_since_zero() {
395 let instant = Instant::now();
396 let duration = instant.saturating_duration_since(instant);
397 assert_eq!(duration, Duration::new(0, 0));
398 }
399
400 #[test]
401 fn test_instant_saturating_duration_since_future() {
402 let instant1 = Instant::now();
403 let instant2 = Instant::now() + Duration::new(1, 0);
404 let duration = instant1.saturating_duration_since(instant2);
405 assert_eq!(duration, Duration::new(0, 0));
406 }
407}