1use crate::deps::{Duration, Instant};
8
9#[derive(Debug, Clone)]
11pub struct CacheEntry<V> {
12 value: V,
14 created_at: Instant,
16 last_accessed: Instant,
18 expires_at: Option<Instant>,
20 access_count: u64,
22}
23
24impl<V> CacheEntry<V> {
25 pub fn new(value: V, ttl: Option<Duration>) -> Self {
27 let now = Instant::now();
28 Self {
29 value,
30 created_at: now,
31 last_accessed: now,
32 expires_at: ttl.map(|t| now + t),
33 access_count: 0,
34 }
35 }
36
37 pub fn value(&self) -> &V {
39 &self.value
40 }
41
42 pub fn value_mut(&mut self) -> &mut V {
44 &mut self.value
45 }
46
47 pub fn into_value(self) -> V {
49 self.value
50 }
51
52 pub fn is_expired(&self) -> bool {
54 self.expires_at
55 .map(|exp| Instant::now() > exp)
56 .unwrap_or(false)
57 }
58
59 pub fn remaining_ttl(&self) -> Option<Duration> {
61 self.expires_at.and_then(|exp| {
62 let now = Instant::now();
63 if now < exp {
64 Some(exp - now)
65 } else {
66 None
67 }
68 })
69 }
70
71 pub fn touch(&mut self) {
73 self.last_accessed = Instant::now();
74 self.access_count += 1;
75 }
76
77 pub fn last_accessed(&self) -> Instant {
79 self.last_accessed
80 }
81
82 pub fn created_at(&self) -> Instant {
84 self.created_at
85 }
86
87 pub fn access_count(&self) -> u64 {
89 self.access_count
90 }
91
92 pub fn age(&self) -> Duration {
94 self.created_at.elapsed()
95 }
96
97 pub fn extend_ttl(&mut self, duration: Duration) {
99 if let Some(exp) = self.expires_at.as_mut() {
100 *exp = *exp + duration;
101 }
102 }
103
104 pub fn set_ttl(&mut self, ttl: Option<Duration>) {
106 self.expires_at = ttl.map(|t| Instant::now() + t);
107 }
108}
109
110#[cfg(test)]
111mod tests {
112 use super::*;
113
114 #[test]
115 fn test_entry_creation() {
116 let entry = CacheEntry::new("value", Some(Duration::from_secs(10)));
117 assert_eq!(entry.value(), &"value");
118 assert!(!entry.is_expired());
119 }
120
121 #[test]
122 fn test_entry_no_ttl() {
123 let entry = CacheEntry::new(42, None);
124 assert!(!entry.is_expired());
125 assert!(entry.remaining_ttl().is_none());
126 }
127
128 #[test]
129 fn test_entry_touch() {
130 let mut entry = CacheEntry::new("value", None);
131 assert_eq!(entry.access_count(), 0);
132
133 entry.touch();
134 assert_eq!(entry.access_count(), 1);
135
136 entry.touch();
137 assert_eq!(entry.access_count(), 2);
138 }
139}