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::{de::DeserializeOwned, Serialize};
7use std::collections::HashMap;
8use std::hash::Hash;
9
10/// Trait for cache storage backends
11#[async_trait]
12pub trait StorageBackend: Send + Sync + 'static {
13    /// Key type for the storage
14    type Key: Serialize + DeserializeOwned + Hash + Eq + Clone + Send + Sync;
15    /// Value type for the storage
16    type Value: Serialize + DeserializeOwned + Clone + Send + Sync;
17    /// Metadata type for entries
18    type Metadata: Serialize + DeserializeOwned + Clone + Send + Sync;
19
20    /// Save entries to storage
21    async fn save(
22        &self,
23        entries: &HashMap<Self::Key, Vec<CacheEntry<Self::Key, Self::Value, Self::Metadata>>>,
24    ) -> Result<()>;
25
26    /// Load entries from storage
27    async fn load(
28        &self,
29    ) -> Result<HashMap<Self::Key, Vec<CacheEntry<Self::Key, Self::Value, Self::Metadata>>>>;
30
31    /// Remove entries for a specific key
32    async fn remove(&self, key: &Self::Key) -> Result<()>;
33
34    /// Clear all entries from storage
35    async fn clear(&self) -> Result<()>;
36
37    /// Check if storage contains a key
38    async fn contains(&self, key: &Self::Key) -> Result<bool> {
39        let entries = self.load().await?;
40        Ok(entries.contains_key(key))
41    }
42
43    /// Get approximate size of storage in bytes
44    async fn size_bytes(&self) -> Result<u64> {
45        Ok(0) // Default implementation returns 0
46    }
47
48    /// Compact storage (optional operation for backends that support it)
49    async fn compact(&self) -> Result<()> {
50        Ok(()) // Default is no-op
51    }
52}
53
54/// Serialization format for storage backends
55#[derive(Debug, Clone, Copy, PartialEq, Eq)]
56pub enum SerializationFormat {
57    /// JSON format
58    #[cfg(feature = "json-serialization")]
59    Json,
60    /// Bincode format
61    #[cfg(feature = "bincode-serialization")]
62    Bincode,
63}
64
65impl SerializationFormat {
66    /// Get file extension for this format
67    pub fn extension(&self) -> &'static str {
68        match self {
69            #[cfg(feature = "json-serialization")]
70            SerializationFormat::Json => "json",
71            #[cfg(feature = "bincode-serialization")]
72            SerializationFormat::Bincode => "bin",
73            #[cfg(not(any(feature = "json-serialization", feature = "bincode-serialization")))]
74            _ => "data",
75        }
76    }
77
78    /// Serialize data to bytes
79    #[allow(unused_variables)]
80    pub fn serialize<T: Serialize>(&self, value: &T) -> Result<Vec<u8>> {
81        match self {
82            #[cfg(feature = "json-serialization")]
83            SerializationFormat::Json => serde_json::to_vec_pretty(value).map_err(Into::into),
84            #[cfg(feature = "bincode-serialization")]
85            SerializationFormat::Bincode => bincode::serialize(value).map_err(Into::into),
86            #[cfg(not(any(feature = "json-serialization", feature = "bincode-serialization")))]
87            _ => Err(crate::error::CacheError::Serialization(
88                "No serialization features enabled".to_string(),
89            )),
90        }
91    }
92
93    /// Deserialize data from bytes
94    #[allow(unused_variables)]
95    pub fn deserialize<T: DeserializeOwned>(&self, data: &[u8]) -> Result<T> {
96        match self {
97            #[cfg(feature = "json-serialization")]
98            SerializationFormat::Json => serde_json::from_slice(data).map_err(Into::into),
99            #[cfg(feature = "bincode-serialization")]
100            SerializationFormat::Bincode => bincode::deserialize(data).map_err(Into::into),
101            #[cfg(not(any(feature = "json-serialization", feature = "bincode-serialization")))]
102            _ => Err(crate::error::CacheError::Serialization(
103                "No serialization features enabled".to_string(),
104            )),
105        }
106    }
107}
108
109/// Storage statistics
110#[derive(Debug, Clone, Default)]
111pub struct StorageStats {
112    /// Total number of keys
113    pub total_keys: usize,
114    /// Total number of entries
115    pub total_entries: usize,
116    /// Total size in bytes
117    pub total_bytes: u64,
118    /// Average entries per key
119    pub avg_entries_per_key: f64,
120}
121
122#[cfg(test)]
123mod tests {
124    #[cfg(any(feature = "json-serialization", feature = "bincode-serialization"))]
125    use super::*;
126
127    #[test]
128    fn test_serialization_format_extension() {
129        #[cfg(feature = "json-serialization")]
130        assert_eq!(SerializationFormat::Json.extension(), "json");
131
132        #[cfg(feature = "bincode-serialization")]
133        assert_eq!(SerializationFormat::Bincode.extension(), "bin");
134    }
135
136    #[cfg(feature = "json-serialization")]
137    #[test]
138    fn test_json_serialization() {
139        use serde::{Deserialize, Serialize};
140
141        #[derive(Serialize, Deserialize, PartialEq, Debug)]
142        struct TestData {
143            value: String,
144        }
145
146        let data = TestData {
147            value: "test".to_string(),
148        };
149        let format = SerializationFormat::Json;
150
151        let serialized = format.serialize(&data).unwrap();
152        let deserialized: TestData = format.deserialize(&serialized).unwrap();
153
154        assert_eq!(data, deserialized);
155    }
156}