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::{
134 config::{ServerConfigurationEntry, ServerConfigurationFilters, ServerConfigurationValue},
135 observability::ObservabilityBackendChannels,
136 };
137
138 use super::*;
139
140 #[test]
141 fn module_loading_test() {
142 let module = 1;
143
144 let cache = ModuleCache::new(vec!["property"]);
145
146 let mut config_entries = HashMap::new();
147 config_entries.insert(
148 "property".to_string(),
149 ServerConfigurationEntries {
150 inner: vec![ServerConfigurationEntry {
151 values: vec![ServerConfigurationValue::String("something".to_string())],
152 props: HashMap::new(),
153 }],
154 },
155 );
156 let config = ServerConfiguration {
157 entries: config_entries,
158 filters: ServerConfigurationFilters {
159 is_host: true,
160 hostname: None,
161 ip: None,
162 port: None,
163 condition: None,
164 error_handler_status: None,
165 },
166 modules: vec![],
167 observability: ObservabilityBackendChannels::new(),
168 };
169
170 let mut config2_entries = HashMap::new();
171 config2_entries.insert(
172 "property".to_string(),
173 ServerConfigurationEntries {
174 inner: vec![ServerConfigurationEntry {
175 values: vec![ServerConfigurationValue::String("something".to_string())],
176 props: HashMap::new(),
177 }],
178 },
179 );
180 config2_entries.insert(
181 "ignore".to_string(),
182 ServerConfigurationEntries {
183 inner: vec![ServerConfigurationEntry {
184 values: vec![ServerConfigurationValue::String("something else".to_string())],
185 props: HashMap::new(),
186 }],
187 },
188 );
189 let config2 = ServerConfiguration {
190 entries: config2_entries,
191 filters: ServerConfigurationFilters {
192 is_host: true,
193 hostname: None,
194 ip: None,
195 port: Some(80),
196 condition: None,
197 error_handler_status: None,
198 },
199 modules: vec![],
200 observability: ObservabilityBackendChannels::new(),
201 };
202
203 assert_eq!(
204 cache
205 .get_or::<_, Box<dyn std::error::Error + Send + Sync>>(&config, |_config| Ok(Arc::new(module)))
206 .unwrap(),
207 Arc::new(module)
208 );
209
210 assert_eq!(
211 cache
212 .get_or::<_, Box<dyn std::error::Error + Send + Sync>>(&config2, |_config| Ok(Arc::new(module)))
213 .unwrap(),
214 Arc::new(module)
215 );
216 }
217
218 #[test]
219 fn should_cache_the_module() {
220 let module = 1;
221 let module2 = 2;
222
223 let mut cache = ModuleCache::new(vec!["property"]);
224
225 let mut config_entries = HashMap::new();
226 config_entries.insert(
227 "property".to_string(),
228 ServerConfigurationEntries {
229 inner: vec![ServerConfigurationEntry {
230 values: vec![ServerConfigurationValue::String("something".to_string())],
231 props: HashMap::new(),
232 }],
233 },
234 );
235 let config = ServerConfiguration {
236 entries: config_entries,
237 filters: ServerConfigurationFilters {
238 is_host: true,
239 hostname: None,
240 ip: None,
241 port: None,
242 condition: None,
243 error_handler_status: None,
244 },
245 modules: vec![],
246 observability: ObservabilityBackendChannels::new(),
247 };
248
249 let mut config2_entries = HashMap::new();
250 config2_entries.insert(
251 "property".to_string(),
252 ServerConfigurationEntries {
253 inner: vec![ServerConfigurationEntry {
254 values: vec![ServerConfigurationValue::String("something".to_string())],
255 props: HashMap::new(),
256 }],
257 },
258 );
259 config2_entries.insert(
260 "ignore".to_string(),
261 ServerConfigurationEntries {
262 inner: vec![ServerConfigurationEntry {
263 values: vec![ServerConfigurationValue::String("something else".to_string())],
264 props: HashMap::new(),
265 }],
266 },
267 );
268 let config2 = ServerConfiguration {
269 entries: config2_entries,
270 filters: ServerConfigurationFilters {
271 is_host: true,
272 hostname: None,
273 ip: None,
274 port: Some(80),
275 condition: None,
276 error_handler_status: None,
277 },
278 modules: vec![],
279 observability: ObservabilityBackendChannels::new(),
280 };
281
282 assert_eq!(
284 cache
285 .get_or_init::<_, Box<dyn std::error::Error + Send + Sync>>(&config, |_config| Ok(Arc::new(module)))
286 .unwrap(),
287 Arc::new(module)
288 );
289
290 assert_eq!(
292 cache
293 .get_or_init::<_, Box<dyn std::error::Error + Send + Sync>>(&config2, |_config| Ok(Arc::new(module2)))
294 .unwrap(),
295 Arc::new(module)
296 );
297 }
298
299 #[test]
300 fn test_cache_operations() {
301 let mut cache = ModuleCache::with_capacity(vec!["test_prop"], 10);
302
303 let config = ServerConfiguration {
304 entries: HashMap::new(),
305 filters: ServerConfigurationFilters {
306 is_host: true,
307 hostname: None,
308 ip: None,
309 port: None,
310 condition: None,
311 error_handler_status: None,
312 },
313 modules: vec![],
314 observability: ObservabilityBackendChannels::new(),
315 };
316
317 assert!(cache.is_empty());
318 assert_eq!(cache.len(), 0);
319
320 let value = cache
322 .get_or_init::<_, Box<dyn std::error::Error + Send + Sync>>(&config, |_| Ok(Arc::new(42)))
323 .unwrap();
324 assert_eq!(*value, 42);
325 assert_eq!(cache.len(), 1);
326
327 let cached = cache.get(&config).unwrap();
329 assert_eq!(*cached, 42);
330
331 cache.clear();
332 assert!(cache.is_empty());
333 }
334}