maybe_fut/api/time/
instant.rs

1use std::ops::{Add, AddAssign, Sub, SubAssign};
2use std::time::Duration;
3
4use crate::{maybe_fut_constructor_sync, maybe_fut_method_sync};
5
6/// A measurement of a monotonically nondecreasing clock. Opaque and useful only with [`std::time::Duration`].
7#[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 instant
18    Std(std::time::Instant),
19    /// Tokio instant
20    #[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        // convert the inner types to std
44        #[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            // perform the addition
53            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            // convert the inner types to std
72            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            // perform the addition
79            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            // perform the addition
88            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        // convert the inner types to std
99        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        // perform the subtraction
110        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            // convert the inner types to std
121            let a = match self.0 {
122                InstantInner::Std(a) => a,
123                #[cfg(tokio_time)]
124                InstantInner::Tokio(a) => a.into_std(),
125            };
126
127            // perform the subtraction
128            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            // perform the subtraction
138            *self = (self.unwrap_std() - other).into();
139        }
140    }
141}
142
143impl Instant {
144    maybe_fut_constructor_sync!(
145        /// Returns an instant corresponding to the current time.
146        now() -> Self,
147        std::time::Instant::now,
148        tokio::time::Instant::now,
149        tokio_time
150    );
151
152    maybe_fut_method_sync!(
153        /// Returns the amount of time elapsed since this instant was created, or zero duration if this instant is in the future.
154        elapsed() -> Duration,
155        InstantInner::Std,
156        InstantInner::Tokio,
157        tokio_time
158    );
159
160    /// Returns `Some(T)` where `t is the time `self + duration` if `t` can be represented as [`Instant`], otherwise `None`.
161    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    /// Returns `Some(T)` where `t is the time `self - duration` if `t` can be represented as [`Instant`], otherwise `None`.
171    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            // convert the inner types to std
177            let a = match self.0 {
178                InstantInner::Std(a) => a,
179                #[cfg(tokio_time)]
180                InstantInner::Tokio(a) => a.into_std(),
181            };
182
183            // perform the checked subtraction
184            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            // convert the inner types to std
194            use crate::unwrap::Unwrap as _;
195            let a = self.unwrap_std();
196
197            // perform the checked subtraction
198            Some(InstantInner::Std(a.checked_sub(duration)?)).map(Instant)
199        }
200    }
201
202    pub fn duration_since(&self, earlier: Instant) -> Duration {
203        // convert the inner types to std
204        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        // perform the duration since
216        a.duration_since(b)
217    }
218
219    /// Returns the duration since `earlier` if `earlier` is before `self`, otherwise returns `None`.
220    pub fn checked_duration_since(&self, earlier: Instant) -> Option<Duration> {
221        // convert the inner types to std
222        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        // perform the checked duration since
234        a.checked_duration_since(b)
235    }
236
237    /// Returns the amount of time elapsed from another instant to this one, or zero duration if that instant is later than this one.
238    pub fn saturating_duration_since(&self, earlier: Instant) -> Duration {
239        // convert the inner types to std
240        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        // perform the saturation duration since
252        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        // check if it's still std
293        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        // check if it's still tokio
305        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}