Skip to main content

axum_oidc_layer/cache/
memory.rs

1//! In-memory cache implementation using standard library types.
2
3use std::{
4    collections::HashMap,
5    sync::{Arc, RwLock},
6    time::{Duration, Instant},
7};
8
9use super::JwksCache;
10
11/// Default in-memory implementation of `JwksCache` using standard library types.
12///
13/// This implementation provides a simple, thread-safe cache that automatically
14/// cleans up expired entries. It's suitable for single-instance deployments
15/// where cache persistence across restarts is not required.
16#[derive(Debug)]
17pub struct InMemoryCache {
18    storage: Arc<RwLock<HashMap<String, CacheEntry>>>,
19}
20
21/// Internal cache entry with expiration tracking.
22#[derive(Debug, Clone)]
23struct CacheEntry {
24    data: String,
25    expires_at: Instant,
26}
27
28impl Default for InMemoryCache {
29    fn default() -> Self {
30        Self::new()
31    }
32}
33
34impl InMemoryCache {
35    /// Creates a new in-memory cache instance.
36    #[must_use]
37    pub fn new() -> Self {
38        Self {
39            storage: Arc::new(RwLock::new(HashMap::new())),
40        }
41    }
42
43    /// Removes expired entries from the cache.
44    ///
45    /// This is called automatically during get operations to maintain
46    /// cache hygiene without requiring a separate cleanup thread.
47    fn cleanup_expired(&self) {
48        if let Ok(mut storage) = self.storage.write() {
49            let now = Instant::now();
50            storage.retain(|_, entry| entry.expires_at > now);
51        }
52    }
53}
54
55impl JwksCache for InMemoryCache {
56    fn get(&self, key: &str) -> Option<String> {
57        self.cleanup_expired();
58
59        self.storage.read().map_or(None, |storage| {
60            storage
61                .get(key)
62                .filter(|entry| entry.expires_at > Instant::now())
63                .map(|entry| entry.data.clone())
64        })
65    }
66
67    fn set(&self, key: &str, value: String, ttl: Duration) {
68        if let Ok(mut storage) = self.storage.write() {
69            let entry = CacheEntry {
70                data: value,
71                expires_at: Instant::now() + ttl,
72            };
73            storage.insert(key.to_string(), entry);
74        }
75    }
76}