jiff/util/
cache.rs

1use std::time::{Duration, Instant as MonotonicInstant};
2
3/// A little helper for representing expiration time.
4///
5/// An overflowing expiration time is treated identically to a time that is
6/// always expired.
7///
8/// When `None` internally, it implies that the expiration time is at some
9/// arbitrary point in the past beyond all possible "time to live" values.
10/// i.e., A `None` value invalidates the cache at the next failed lookup.
11#[derive(Clone, Copy, Debug)]
12pub(crate) struct Expiration(Option<MonotonicInstant>);
13
14impl Expiration {
15    /// Returns an expiration time for which `is_expired` returns true after
16    /// the given duration has elapsed from this instant.
17    pub(crate) fn after(ttl: Duration) -> Expiration {
18        Expiration(
19            crate::now::monotonic_time().and_then(|now| now.checked_add(ttl)),
20        )
21    }
22
23    /// Returns an expiration time for which `is_expired` always returns true.
24    pub(crate) const fn expired() -> Expiration {
25        Expiration(None)
26    }
27
28    /// Whether expiration has occurred or not.
29    pub(crate) fn is_expired(self) -> bool {
30        self.0.map_or(true, |t| {
31            let Some(now) = crate::now::monotonic_time() else { return true };
32            now > t
33        })
34    }
35}
36
37impl core::fmt::Display for Expiration {
38    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
39        let Some(instant) = self.0 else {
40            return write!(f, "expired");
41        };
42        let Some(now) = crate::now::monotonic_time() else {
43            return write!(f, "expired");
44        };
45        let Some(duration) = instant.checked_duration_since(now) else {
46            return write!(f, "expired");
47        };
48        write!(f, "{duration:?}")
49    }
50}