Skip to main content

amalgam/
registry.rs

1//! Named caches and dynamic default options.
2//!
3//! FusionCache supports multiple named caches resolved from a DI container, plus
4//! a `DefaultEntryOptionsProvider` that supplies per-key defaults. The Rust
5//! equivalents: a [`CacheRegistry`] of named caches and the
6//! [`DefaultEntryOptionsProvider`] trait consulted when a call supplies no
7//! explicit options.
8
9use std::collections::HashMap;
10use std::sync::RwLock;
11
12use crate::cache::Cache;
13use crate::options::EntryOptions;
14
15/// Supplies default [`EntryOptions`] dynamically, keyed by cache key.
16///
17/// Returning `None` falls back to the cache's static default options.
18pub trait DefaultEntryOptionsProvider: Send + Sync {
19    /// The options to use for `key`, or `None` to fall back to the static default.
20    fn options_for(&self, key: &str) -> Option<EntryOptions>;
21}
22
23/// A thread-safe registry of named caches of a single value type.
24///
25/// Use this where FusionCache uses keyed/named caches from DI: register a cache
26/// under a name once, then resolve it anywhere.
27pub struct CacheRegistry<V: Clone + Send + Sync + 'static> {
28    caches: RwLock<HashMap<String, Cache<V>>>,
29}
30
31impl<V: Clone + Send + Sync + 'static> CacheRegistry<V> {
32    /// Creates an empty registry.
33    #[must_use]
34    pub fn new() -> Self {
35        Self {
36            caches: RwLock::new(HashMap::new()),
37        }
38    }
39
40    /// Registers (or replaces) a named cache.
41    pub fn register(&self, name: impl Into<String>, cache: Cache<V>) {
42        if let Ok(mut map) = self.caches.write() {
43            map.insert(name.into(), cache);
44        }
45    }
46
47    /// Resolves a named cache.
48    #[must_use]
49    pub fn get(&self, name: &str) -> Option<Cache<V>> {
50        self.caches.read().ok()?.get(name).cloned()
51    }
52
53    /// Resolves a named cache, building and registering it on first use.
54    pub fn get_or_create(&self, name: &str, build: impl FnOnce() -> Cache<V>) -> Cache<V> {
55        if let Some(existing) = self.get(name) {
56            return existing;
57        }
58        let cache = build();
59        self.register(name.to_owned(), cache.clone());
60        cache
61    }
62
63    /// The number of registered caches.
64    #[must_use]
65    pub fn len(&self) -> usize {
66        self.caches.read().map(|m| m.len()).unwrap_or(0)
67    }
68
69    /// `true` if no caches are registered.
70    #[must_use]
71    pub fn is_empty(&self) -> bool {
72        self.len() == 0
73    }
74}
75
76impl<V: Clone + Send + Sync + 'static> Default for CacheRegistry<V> {
77    fn default() -> Self {
78        Self::new()
79    }
80}