1use crate::time::TimeSource;
2use std::time::Duration;
3
4#[derive(Debug, Clone)]
7pub struct Value<V, TS> {
8 value: V,
9 expires: TS,
10}
11
12impl<V, TS> Value<V, TS>
13where
14 V: Clone,
15 TS: TimeSource,
16{
17 pub fn new(value: V, lifetime: Duration) -> Self {
23 Self {
24 value,
25 expires: TS::now() + lifetime,
26 }
27 }
28
29 pub fn is_expired(&self) -> bool {
32 self.is_expired_at(&TS::now())
33 }
34
35 pub fn is_expired_at(&self, at: &TS) -> bool {
38 at > &self.expires
39 }
40
41 pub fn expires(&self) -> &TS {
44 &self.expires
45 }
46
47 pub fn set_expiry(&mut self, lifetime: Duration) {
50 self.expires = TS::now() + lifetime;
51 }
52
53 pub fn add_expiry(&mut self, lifetime: Duration) {
56 self.expires += lifetime;
57 }
58
59 pub fn value(&self) -> V {
61 self.value.clone()
62 }
63
64 pub fn value_ref(&self) -> &V {
66 &self.value
67 }
68
69 pub fn value_checked(&self) -> Option<V> {
72 if self.is_expired() {
73 None
74 } else {
75 Some(self.value())
76 }
77 }
78
79 pub fn value_ref_checked(&self) -> Option<&V> {
82 if self.is_expired() {
83 None
84 } else {
85 Some(self.value_ref())
86 }
87 }
88}
89
90#[cfg(test)]
91mod test {
92 use super::*;
93 use mock_instant::{Instant, MockClock};
94
95 #[test]
96 fn expiry() {
97 let v: Value<_, Instant> = Value::new("foo", Duration::from_millis(100));
98 assert_eq!(v.expires(), &(Instant::now() + Duration::from_millis(100)));
99 assert!(!v.is_expired());
100 assert_eq!(v.value_checked(), Some("foo"));
101
102 MockClock::advance(Duration::from_millis(100));
103
104 assert!(!v.is_expired());
105 assert_eq!(v.value_checked(), Some("foo"));
106
107 MockClock::advance(Duration::from_millis(1));
108
109 assert!(v.is_expired());
110 assert_eq!(v.value_checked(), None);
111 }
112}