mofa_kernel/storage.rs
1//! Storage traits for key-value and session storage
2//!
3//! Defines abstract storage interfaces that can be implemented
4//! by different storage backends (in-memory, file, database, etc.)
5
6use crate::agent::error::AgentResult;
7use async_trait::async_trait;
8
9// ============================================================================
10// Generic Storage Trait
11// ============================================================================
12
13/// Generic storage trait for key-value operations
14///
15/// This trait provides a simple interface for storing and retrieving
16/// arbitrary data by key. Implementations can use various backends
17/// such as in-memory maps, files, databases, etc.
18///
19/// # Type Parameters
20///
21/// * `K` - Key type (must be able to be converted to/from String for serialization)
22/// * `V` - Value type (must be serializable)
23///
24/// # Example
25///
26/// ```rust,ignore
27/// use mofa_kernel::storage::Storage;
28///
29/// struct MyStorage {
30/// data: HashMap<String, Vec<u8>>,
31/// }
32///
33/// #[async_trait]
34/// impl Storage<String, Vec<u8>> for MyStorage {
35/// async fn load(&self, key: &String) -> AgentResult<Option<Vec<u8>>> {
36/// Ok(self.data.get(key).cloned())
37/// }
38///
39/// async fn save(&self, key: &String, value: &Vec<u8>) -> AgentResult<()> {
40/// self.data.insert(key.clone(), value.clone());
41/// Ok(())
42/// }
43///
44/// async fn delete(&self, key: &String) -> AgentResult<bool> {
45/// Ok(self.data.remove(key).is_some())
46/// }
47///
48/// async fn list(&self) -> AgentResult<Vec<String>> {
49/// Ok(self.data.keys().cloned().collect())
50/// }
51/// }
52/// ```
53#[async_trait]
54pub trait Storage<K, V>: Send + Sync {
55 /// Load a value by key
56 ///
57 /// Returns `Ok(None)` if the key doesn't exist.
58 async fn load(&self, key: &K) -> AgentResult<Option<V>>;
59
60 /// Save a value by key
61 ///
62 /// Creates a new entry or updates an existing one.
63 async fn save(&self, key: &K, value: &V) -> AgentResult<()>;
64
65 /// Delete a value by key
66 ///
67 /// Returns `Ok(true)` if the key existed and was deleted,
68 /// `Ok(false)` if the key didn't exist.
69 async fn delete(&self, key: &K) -> AgentResult<bool>;
70
71 /// List all keys
72 ///
73 /// Returns a vector of all keys in the storage.
74 async fn list(&self) -> AgentResult<Vec<K>>;
75}
76
77#[cfg(test)]
78mod tests {
79 use super::*;
80 use std::collections::HashMap;
81 use std::sync::Arc;
82 use tokio::sync::RwLock;
83
84 // Simple in-memory storage for testing
85 struct InMemoryStorage {
86 data: Arc<RwLock<HashMap<String, Vec<u8>>>>,
87 }
88
89 impl InMemoryStorage {
90 fn new() -> Self {
91 Self {
92 data: Arc::new(RwLock::new(HashMap::new())),
93 }
94 }
95 }
96
97 #[async_trait]
98 impl Storage<String, Vec<u8>> for InMemoryStorage {
99 async fn load(&self, key: &String) -> AgentResult<Option<Vec<u8>>> {
100 let data = self.data.read().await;
101 Ok(data.get(key).cloned())
102 }
103
104 async fn save(&self, key: &String, value: &Vec<u8>) -> AgentResult<()> {
105 let mut data = self.data.write().await;
106 data.insert(key.clone(), value.clone());
107 Ok(())
108 }
109
110 async fn delete(&self, key: &String) -> AgentResult<bool> {
111 let mut data = self.data.write().await;
112 Ok(data.remove(key).is_some())
113 }
114
115 async fn list(&self) -> AgentResult<Vec<String>> {
116 let data = self.data.read().await;
117 Ok(data.keys().cloned().collect())
118 }
119 }
120
121 #[tokio::test]
122 async fn test_storage_basic_operations() {
123 let storage = InMemoryStorage::new();
124
125 // Save
126 storage
127 .save(&"key1".to_string(), &vec![1, 2, 3])
128 .await
129 .unwrap();
130
131 // Load
132 let value = storage.load(&"key1".to_string()).await.unwrap();
133 assert_eq!(value, Some(vec![1, 2, 3]));
134
135 // List
136 let keys = storage.list().await.unwrap();
137 assert_eq!(keys, vec!["key1".to_string()]);
138
139 // Delete
140 let deleted = storage.delete(&"key1".to_string()).await.unwrap();
141 assert!(deleted);
142
143 // Verify deletion
144 let value = storage.load(&"key1".to_string()).await.unwrap();
145 assert_eq!(value, None);
146 }
147}