1use dashmap::DashMap;
2
3use crate::item::Item;
4use std::hash::Hash;
5use std::time::Duration;
6
7pub struct Cache<T, V> {
8 items: DashMap<T, Item<V>>,
9 item_duration: Option<Duration>,
10}
11
12impl<T, V> Cache<T, V>
13where
14 T: Eq + Hash,
15 V: Clone,
16{
17 pub fn new(item_duration: Option<Duration>) -> Self {
50 Cache {
51 items: DashMap::new(),
52 item_duration,
53 }
54 }
55
56 pub fn get(&self, key: &T) -> Option<V>
58 where
59 T: Eq + Hash,
60 V: Clone,
61 {
62 self.items
63 .get(key)
64 .filter(|item| !item.expired())
65 .map(|item| item.object.clone())
66 }
67
68 pub fn set(&self, key: T, value: V, custom_duration: Option<Duration>) -> Option<V>
71 where
72 T: Eq + Hash,
73 {
74 self.items
75 .insert(
76 key,
77 Item::new(value, custom_duration.or(self.item_duration)),
78 )
79 .map(|item| item.object)
80 }
81
82 pub fn remove_expired(&self)
84 where
85 T: Eq + Hash + Clone,
86 {
87 self.items.retain(|_, item| !item.expired());
88 self.shrink();
89 }
90
91 pub fn remove(&self, key: &T) -> Option<V>
93 where
94 T: Eq + Hash,
95 {
96 let item = self.items.remove(key).map(|(_, item)| item.object);
97 self.shrink();
98
99 item
100 }
101
102 pub fn clear(&self) {
104 self.items.clear();
105 self.shrink();
106 }
107
108 pub fn shrink(&self)
110 where
111 T: Eq + Hash,
112 {
113 self.items.shrink_to_fit()
114 }
115}
116
117#[cfg(test)]
118mod tests {
119 use crate::cache::Cache;
120 use std::time::Duration;
121
122 const KEY: i8 = 0;
123 const VALUE: &str = "VALUE";
124
125 #[test]
126 fn set_and_get_value_with_default_duration() {
127 let cache = Cache::new(Some(Duration::from_secs(2)));
128 cache.set(KEY, VALUE, None);
129 let value = cache.get(&KEY);
130 match value {
131 Some(value) => assert_eq!(value, VALUE),
132 None => panic!("value was not found in cache"),
133 };
134 }
135
136 #[test]
137 fn set_and_get_value_without_duration() {
138 let cache = Cache::new(None);
139 cache.set(KEY, VALUE, None);
140 let value = cache.get(&KEY);
141 match value {
142 Some(value) => assert_eq!(value, VALUE),
143 None => panic!("value was not found in cache"),
144 };
145 }
146
147 #[test]
148 fn set_and_get_value_with_custom_duration() {
149 let cache = Cache::new(Some(Duration::from_secs(0)));
150 cache.set(KEY, VALUE, Some(Duration::from_secs(2)));
151 let value = cache.get(&KEY);
152 match value {
153 Some(value) => assert_eq!(value, VALUE),
154 None => panic!("value was not found in cache"),
155 };
156 }
157
158 #[test]
159 fn set_do_not_get_expired_value() {
160 let cache = Cache::new(Some(Duration::from_secs(0)));
161 cache.set(KEY, VALUE, None);
162 let value = cache.get(&KEY);
163 if value.is_some() {
164 panic!("found expired value in cache")
165 };
166 }
167
168 #[test]
169 fn set_replace_existing_value() {
170 const NEW_VALUE: &str = "NEW_VALUE";
171 let cache = Cache::new(Some(Duration::from_secs(2)));
172 cache.set(KEY, VALUE, None);
173 cache.set(KEY, NEW_VALUE, None);
174 let value = cache.get(&KEY);
175 match value {
176 Some(value) => assert_eq!(value, NEW_VALUE),
177 None => panic!("value was not found in cache"),
178 };
179 }
180
181 #[test]
182 fn remove_expired_item() {
183 let cache = Cache::new(Some(Duration::from_secs(0)));
184 cache.set(KEY, VALUE, None);
185 cache.remove_expired();
186 if cache.items.get(&KEY).is_some() {
187 panic!("found expired item in cache")
188 };
189 }
190
191 #[test]
192 fn remove_expired_do_not_remove_not_expired_item() {
193 let cache = Cache::new(Some(Duration::from_secs(2)));
194 cache.set(KEY, VALUE, None);
195 cache.remove_expired();
196 if cache.items.get(&KEY).is_none() {
197 panic!("could not find not expired item in cache")
198 };
199 }
200
201 #[test]
202 fn clear_not_expired_item() {
203 let cache = Cache::new(Some(Duration::from_secs(2)));
204 cache.set(KEY, VALUE, None);
205 cache.clear();
206 if cache.items.get(&KEY).is_some() {
207 panic!("found item in cache")
208 };
209 }
210
211 #[test]
212 fn remove_remove_expired_item() {
213 let cache = Cache::new(Some(Duration::from_secs(2)));
214 cache.set(KEY, VALUE, None);
215 if let None = cache.remove(&KEY) {
216 panic!("none returned from removing existing value")
217 };
218 if cache.items.get(&KEY).is_some() {
219 panic!("found not expired item in cache")
220 };
221 }
222
223 #[test]
224 fn remove_return_none_if_not_found() {
225 let cache: Cache<i8, &str> = Cache::new(Some(Duration::from_secs(2)));
226 if let Some(_) = cache.remove(&KEY) {
227 panic!("some value was returned from remove")
228 };
229 }
230}