matrix_sdk_common/
ttl_cache.rs1use std::{borrow::Borrow, collections::HashMap, hash::Hash, time::Duration};
19
20use ruma::time::Instant;
21
22const DEFAULT_LIFETIME: Duration = Duration::from_secs(24 * 60 * 60);
24
25#[derive(Debug)]
26struct TtlItem<V: Clone> {
27 value: V,
28 insertion_time: Instant,
29 lifetime: Duration,
30}
31
32impl<V: Clone> TtlItem<V> {
33 fn expired(&self) -> bool {
34 self.insertion_time.elapsed() >= self.lifetime
35 }
36}
37
38#[derive(Debug)]
40pub struct TtlCache<K: Eq + Hash, V: Clone> {
41 lifetime: Duration,
42 items: HashMap<K, TtlItem<V>>,
43}
44
45impl<K, V> TtlCache<K, V>
46where
47 K: Eq + Hash,
48 V: Clone,
49{
50 pub fn new() -> Self {
52 Self { items: Default::default(), lifetime: DEFAULT_LIFETIME }
53 }
54
55 pub fn contains<Q>(&self, key: &Q) -> bool
57 where
58 K: Borrow<Q>,
59 Q: Hash + Eq + ?Sized,
60 {
61 let cache = &self.items;
62 let contains = if let Some(item) = cache.get(key) { !item.expired() } else { false };
63
64 contains
65 }
66
67 pub fn insert(&mut self, key: K, value: V) {
69 self.extend([(key, value)]);
70 }
71
72 pub fn extend(&mut self, iterator: impl IntoIterator<Item = (K, V)>) {
74 let cache = &mut self.items;
75
76 let now = Instant::now();
77
78 for (key, value) in iterator {
79 let item = TtlItem { value, insertion_time: now, lifetime: self.lifetime };
80
81 cache.insert(key, item);
82 }
83 }
84
85 pub fn remove<Q>(&mut self, key: &Q) -> Option<V>
87 where
88 K: Borrow<Q>,
89 Q: Hash + Eq + ?Sized,
90 {
91 self.items.remove(key.borrow()).map(|item| item.value)
92 }
93
94 pub fn get<Q>(&mut self, key: &Q) -> Option<V>
97 where
98 K: Borrow<Q>,
99 Q: Hash + Eq + ?Sized,
100 {
101 self.items.retain(|_, value| !value.expired());
103 self.items.get(key.borrow()).map(|item| item.value.clone())
105 }
106
107 #[doc(hidden)]
111 pub fn expire<Q>(&mut self, key: &Q)
112 where
113 K: Borrow<Q>,
114 Q: Hash + Eq + ?Sized,
115 {
116 if let Some(item) = self.items.get_mut(key) {
117 item.lifetime = Duration::from_secs(0);
118 }
119 }
120}
121
122impl<K: Eq + Hash, V: Clone> Default for TtlCache<K, V> {
123 fn default() -> Self {
124 Self::new()
125 }
126}
127
128#[cfg(test)]
129mod tests {
130
131 use super::TtlCache;
132
133 #[test]
134 fn test_ttl_cache_insertion() {
135 let mut cache = TtlCache::new();
136 assert!(!cache.contains("A"));
137
138 cache.insert("A", 1);
139 assert!(cache.contains("A"));
140
141 let value = cache.get("A").expect("The value should be in the cache");
142 assert_eq!(value, 1);
143
144 cache.expire("A");
145
146 assert!(!cache.contains("A"));
147 assert!(cache.get("A").is_none(), "The item should have been removed from the cache");
148 }
149}