Skip to main content

threatflux_cache/
storage.rs

1//! Storage backend trait and utilities
2
3use crate::entry::CacheEntry;
4use crate::error::Result;
5use async_trait::async_trait;
6use serde::{Serialize, de::DeserializeOwned};
7use std::collections::HashMap;
8use std::hash::Hash;
9
10/// Convenience alias for the internal storage map
11pub type EntryMap<K, V, M> = HashMap<K, Vec<CacheEntry<K, V, M>>>;
12
13/// Trait for cache storage backends
14#[async_trait]
15pub trait StorageBackend: Send + Sync + 'static {
16    /// Key type for the storage
17    type Key: Serialize + DeserializeOwned + Hash + Eq + Clone + Send + Sync;
18    /// Value type for the storage
19    type Value: Serialize + DeserializeOwned + Clone + Send + Sync;
20    /// Metadata type for entries
21    type Metadata: Serialize + DeserializeOwned + Clone + Send + Sync;
22
23    /// Save entries to storage
24    async fn save(&self, entries: &EntryMap<Self::Key, Self::Value, Self::Metadata>) -> Result<()>;
25
26    /// Load entries from storage
27    async fn load(&self) -> Result<EntryMap<Self::Key, Self::Value, Self::Metadata>>;
28
29    /// Remove entries for a specific key
30    async fn remove(&self, key: &Self::Key) -> Result<()>;
31
32    /// Clear all entries from storage
33    async fn clear(&self) -> Result<()>;
34
35    /// Check if storage contains a key
36    async fn contains(&self, key: &Self::Key) -> Result<bool> {
37        let entries = self.load().await?;
38        Ok(entries.contains_key(key))
39    }
40
41    /// Get approximate size of storage in bytes
42    async fn size_bytes(&self) -> Result<u64>;
43
44    /// Compact storage (optional operation for backends that support it)
45    async fn compact(&self) -> Result<()> {
46        Ok(()) // Default is no-op
47    }
48}
49
50#[cfg(test)]
51mod tests {
52    use super::*;
53    #[tokio::test]
54    async fn test_default_storage_methods() {
55        use crate::test_utils::TestBackend;
56        use std::collections::HashMap;
57
58        let backend = TestBackend::default();
59        let mut map = HashMap::new();
60        map.insert(
61            "a".to_string(),
62            vec![CacheEntry::new("a".to_string(), "v".to_string())],
63        );
64        backend.save(&map).await.unwrap();
65
66        assert!(backend.contains(&"a".to_string()).await.unwrap());
67        assert!(!backend.contains(&"b".to_string()).await.unwrap());
68        assert!(backend.size_bytes().await.unwrap() > 0);
69        backend.compact().await.unwrap();
70    }
71}