1use std::hash::Hasher;
2use std::sync::Arc;
3use std::{collections::HashMap, error::Error};
4
5use crate::config::{ServerConfiguration, ServerConfigurationEntries};
6
7pub struct ModuleCache<T> {
9 inner: HashMap<CacheKey, Arc<T>>,
11 properties: Box<[&'static str]>, }
13
14#[derive(Clone, PartialEq, Eq)]
16struct CacheKey {
17 entries: Box<[(String, Option<ServerConfigurationEntries>)]>,
19}
20
21impl std::hash::Hash for CacheKey {
22 fn hash<H: Hasher>(&self, state: &mut H) {
23 self.entries.hash(state);
24 }
25}
26
27impl CacheKey {
28 fn new(config: &ServerConfiguration, properties: &[&'static str]) -> Self {
29 let mut entries: Vec<_> = properties
30 .iter()
31 .map(|&prop| (prop.to_string(), config.entries.get(prop).cloned()))
32 .collect();
33
34 entries.sort_by(|a, b| a.0.cmp(&b.0));
36
37 Self {
38 entries: entries.into_boxed_slice(),
39 }
40 }
41}
42
43#[allow(dead_code)]
44impl<T> ModuleCache<T> {
45 pub fn new(properties: Vec<&'static str>) -> Self {
47 Self {
48 inner: HashMap::with_capacity(16), properties: properties.into_boxed_slice(),
50 }
51 }
52
53 pub fn with_capacity(properties: Vec<&'static str>, capacity: usize) -> Self {
55 Self {
56 inner: HashMap::with_capacity(capacity),
57 properties: properties.into_boxed_slice(),
58 }
59 }
60
61 pub fn get_or_init<F, E>(&mut self, config: &ServerConfiguration, init_fn: F) -> Result<Arc<T>, E>
64 where
65 F: FnOnce(&ServerConfiguration) -> Result<Arc<T>, E>,
66 E: From<Box<dyn Error + Send + Sync>>,
67 {
68 let cache_key = CacheKey::new(config, &self.properties);
69
70 if let Some(cached_value) = self.inner.get(&cache_key) {
72 return Ok(cached_value.clone());
73 }
74
75 let new_value = init_fn(config)?;
77 self.inner.insert(cache_key, new_value.clone());
78 Ok(new_value)
79 }
80
81 pub fn get(&self, config: &ServerConfiguration) -> Option<Arc<T>> {
83 let cache_key = CacheKey::new(config, &self.properties);
84 self.inner.get(&cache_key).cloned()
85 }
86
87 pub fn clear(&mut self) {
89 self.inner.clear();
90 }
91
92 pub fn len(&self) -> usize {
94 self.inner.len()
95 }
96
97 pub fn is_empty(&self) -> bool {
99 self.inner.is_empty()
100 }
101
102 pub fn reserve(&mut self, additional: usize) {
104 self.inner.reserve(additional);
105 }
106
107 pub fn get_or<F, E>(&self, config: &ServerConfiguration, fallback_fn: F) -> Result<Arc<T>, E>
109 where
110 F: FnOnce(&ServerConfiguration) -> Result<Arc<T>, E>,
111 {
112 let cache_key = CacheKey::new(config, &self.properties);
113
114 if let Some(cached_value) = self.inner.get(&cache_key) {
116 return Ok(cached_value.clone());
117 }
118
119 fallback_fn(config)
121 }
122}
123
124impl<T> Default for ModuleCache<T> {
126 fn default() -> Self {
127 Self::new(Vec::new())
128 }
129}
130
131#[cfg(test)]
132mod test {
133 use crate::config::{ServerConfigurationEntry, ServerConfigurationFilters, ServerConfigurationValue};
134
135 use super::*;
136
137 #[test]
138 fn module_loading_test() {
139 let module = 1;
140
141 let cache = ModuleCache::new(vec!["property"]);
142
143 let mut config_entries = HashMap::new();
144 config_entries.insert(
145 "property".to_string(),
146 ServerConfigurationEntries {
147 inner: vec![ServerConfigurationEntry {
148 values: vec![ServerConfigurationValue::String("something".to_string())],
149 props: HashMap::new(),
150 }],
151 },
152 );
153 let config = ServerConfiguration {
154 entries: config_entries,
155 filters: ServerConfigurationFilters {
156 is_host: true,
157 hostname: None,
158 ip: None,
159 port: None,
160 condition: None,
161 error_handler_status: None,
162 },
163 modules: vec![],
164 };
165
166 let mut config2_entries = HashMap::new();
167 config2_entries.insert(
168 "property".to_string(),
169 ServerConfigurationEntries {
170 inner: vec![ServerConfigurationEntry {
171 values: vec![ServerConfigurationValue::String("something".to_string())],
172 props: HashMap::new(),
173 }],
174 },
175 );
176 config2_entries.insert(
177 "ignore".to_string(),
178 ServerConfigurationEntries {
179 inner: vec![ServerConfigurationEntry {
180 values: vec![ServerConfigurationValue::String("something else".to_string())],
181 props: HashMap::new(),
182 }],
183 },
184 );
185 let config2 = ServerConfiguration {
186 entries: config2_entries,
187 filters: ServerConfigurationFilters {
188 is_host: true,
189 hostname: None,
190 ip: None,
191 port: Some(80),
192 condition: None,
193 error_handler_status: None,
194 },
195 modules: vec![],
196 };
197
198 assert_eq!(
199 cache
200 .get_or::<_, Box<dyn std::error::Error + Send + Sync>>(&config, |_config| Ok(Arc::new(module)))
201 .unwrap(),
202 Arc::new(module)
203 );
204
205 assert_eq!(
206 cache
207 .get_or::<_, Box<dyn std::error::Error + Send + Sync>>(&config2, |_config| Ok(Arc::new(module)))
208 .unwrap(),
209 Arc::new(module)
210 );
211 }
212
213 #[test]
214 fn should_cache_the_module() {
215 let module = 1;
216 let module2 = 2;
217
218 let mut cache = ModuleCache::new(vec!["property"]);
219
220 let mut config_entries = HashMap::new();
221 config_entries.insert(
222 "property".to_string(),
223 ServerConfigurationEntries {
224 inner: vec![ServerConfigurationEntry {
225 values: vec![ServerConfigurationValue::String("something".to_string())],
226 props: HashMap::new(),
227 }],
228 },
229 );
230 let config = ServerConfiguration {
231 entries: config_entries,
232 filters: ServerConfigurationFilters {
233 is_host: true,
234 hostname: None,
235 ip: None,
236 port: None,
237 condition: None,
238 error_handler_status: None,
239 },
240 modules: vec![],
241 };
242
243 let mut config2_entries = HashMap::new();
244 config2_entries.insert(
245 "property".to_string(),
246 ServerConfigurationEntries {
247 inner: vec![ServerConfigurationEntry {
248 values: vec![ServerConfigurationValue::String("something".to_string())],
249 props: HashMap::new(),
250 }],
251 },
252 );
253 config2_entries.insert(
254 "ignore".to_string(),
255 ServerConfigurationEntries {
256 inner: vec![ServerConfigurationEntry {
257 values: vec![ServerConfigurationValue::String("something else".to_string())],
258 props: HashMap::new(),
259 }],
260 },
261 );
262 let config2 = ServerConfiguration {
263 entries: config2_entries,
264 filters: ServerConfigurationFilters {
265 is_host: true,
266 hostname: None,
267 ip: None,
268 port: Some(80),
269 condition: None,
270 error_handler_status: None,
271 },
272 modules: vec![],
273 };
274
275 assert_eq!(
277 cache
278 .get_or_init::<_, Box<dyn std::error::Error + Send + Sync>>(&config, |_config| Ok(Arc::new(module)))
279 .unwrap(),
280 Arc::new(module)
281 );
282
283 assert_eq!(
285 cache
286 .get_or_init::<_, Box<dyn std::error::Error + Send + Sync>>(&config2, |_config| Ok(Arc::new(module2)))
287 .unwrap(),
288 Arc::new(module)
289 );
290 }
291
292 #[test]
293 fn test_cache_operations() {
294 let mut cache = ModuleCache::with_capacity(vec!["test_prop"], 10);
295
296 let config = ServerConfiguration {
297 entries: HashMap::new(),
298 filters: ServerConfigurationFilters {
299 is_host: true,
300 hostname: None,
301 ip: None,
302 port: None,
303 condition: None,
304 error_handler_status: None,
305 },
306 modules: vec![],
307 };
308
309 assert!(cache.is_empty());
310 assert_eq!(cache.len(), 0);
311
312 let value = cache
314 .get_or_init::<_, Box<dyn std::error::Error + Send + Sync>>(&config, |_| Ok(Arc::new(42)))
315 .unwrap();
316 assert_eq!(*value, 42);
317 assert_eq!(cache.len(), 1);
318
319 let cached = cache.get(&config).unwrap();
321 assert_eq!(*cached, 42);
322
323 cache.clear();
324 assert!(cache.is_empty());
325 }
326}