claude_agent/config/
memory.rs

1//! In-Memory Configuration Provider
2//!
3//! Provides a simple in-memory key-value store for configuration.
4//! Useful for testing and code-defined configuration.
5
6use std::collections::HashMap;
7use std::sync::Arc;
8
9use tokio::sync::RwLock;
10
11use super::ConfigResult;
12use super::provider::ConfigProvider;
13
14/// In-memory configuration provider
15#[derive(Debug, Default)]
16pub struct MemoryConfigProvider {
17    data: Arc<RwLock<HashMap<String, String>>>,
18    name: String,
19}
20
21impl MemoryConfigProvider {
22    /// Create a new empty memory provider
23    pub fn new() -> Self {
24        Self {
25            data: Arc::new(RwLock::new(HashMap::new())),
26            name: "memory".to_string(),
27        }
28    }
29
30    /// Create a memory provider with a custom name
31    pub fn with_name(name: impl Into<String>) -> Self {
32        Self {
33            data: Arc::new(RwLock::new(HashMap::new())),
34            name: name.into(),
35        }
36    }
37
38    /// Create a memory provider with initial data
39    pub fn with_data(data: HashMap<String, String>) -> Self {
40        Self {
41            data: Arc::new(RwLock::new(data)),
42            name: "memory".to_string(),
43        }
44    }
45
46    /// Add an initial value during construction (builder pattern)
47    pub fn with_value(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
48        // Safe: called during construction before any async access
49        Arc::get_mut(&mut self.data)
50            .expect("with_value must be called during construction")
51            .get_mut()
52            .insert(key.into(), value.into());
53        self
54    }
55
56    /// Insert a value asynchronously
57    pub async fn insert(&self, key: impl Into<String>, value: impl Into<String>) {
58        let mut data = self.data.write().await;
59        data.insert(key.into(), value.into());
60    }
61
62    /// Get the number of stored values
63    pub async fn len(&self) -> usize {
64        self.data.read().await.len()
65    }
66
67    /// Check if empty
68    pub async fn is_empty(&self) -> bool {
69        self.data.read().await.is_empty()
70    }
71
72    /// Clear all values
73    pub async fn clear(&self) {
74        self.data.write().await.clear();
75    }
76}
77
78#[async_trait::async_trait]
79impl ConfigProvider for MemoryConfigProvider {
80    fn name(&self) -> &str {
81        &self.name
82    }
83
84    async fn get_raw(&self, key: &str) -> ConfigResult<Option<String>> {
85        let data = self.data.read().await;
86        Ok(data.get(key).cloned())
87    }
88
89    async fn set_raw(&self, key: &str, value: &str) -> ConfigResult<()> {
90        let mut data = self.data.write().await;
91        data.insert(key.to_string(), value.to_string());
92        Ok(())
93    }
94
95    async fn delete(&self, key: &str) -> ConfigResult<bool> {
96        let mut data = self.data.write().await;
97        Ok(data.remove(key).is_some())
98    }
99
100    async fn list_keys(&self, prefix: &str) -> ConfigResult<Vec<String>> {
101        let data = self.data.read().await;
102        let keys: Vec<String> = data
103            .keys()
104            .filter(|k| k.starts_with(prefix))
105            .cloned()
106            .collect();
107        Ok(keys)
108    }
109}
110
111#[cfg(test)]
112mod tests {
113    use super::*;
114
115    #[tokio::test]
116    async fn test_memory_provider_basic() {
117        let provider = MemoryConfigProvider::new();
118
119        // Set and get
120        provider.set_raw("key1", "value1").await.unwrap();
121        let value = provider.get_raw("key1").await.unwrap();
122        assert_eq!(value, Some("value1".to_string()));
123
124        // Get non-existent
125        let value = provider.get_raw("nonexistent").await.unwrap();
126        assert_eq!(value, None);
127    }
128
129    #[tokio::test]
130    async fn test_memory_provider_delete() {
131        let provider = MemoryConfigProvider::new();
132
133        provider.set_raw("key1", "value1").await.unwrap();
134        assert!(provider.delete("key1").await.unwrap());
135        assert!(!provider.delete("key1").await.unwrap());
136
137        let value = provider.get_raw("key1").await.unwrap();
138        assert_eq!(value, None);
139    }
140
141    #[tokio::test]
142    async fn test_memory_provider_list_keys() {
143        let provider = MemoryConfigProvider::new();
144
145        provider.set_raw("app.name", "test").await.unwrap();
146        provider.set_raw("app.version", "1.0").await.unwrap();
147        provider.set_raw("other.key", "value").await.unwrap();
148
149        let keys = provider.list_keys("app.").await.unwrap();
150        assert_eq!(keys.len(), 2);
151        assert!(keys.contains(&"app.name".to_string()));
152        assert!(keys.contains(&"app.version".to_string()));
153    }
154
155    #[tokio::test]
156    async fn test_memory_provider_typed() {
157        use crate::config::provider::ConfigProviderExt;
158
159        let provider = MemoryConfigProvider::new();
160
161        // Set typed value
162        ConfigProviderExt::set(&provider, "count", &42i32)
163            .await
164            .unwrap();
165
166        // Get typed value
167        let count: Option<i32> = ConfigProviderExt::get(&provider, "count").await.unwrap();
168        assert_eq!(count, Some(42));
169    }
170
171    #[tokio::test]
172    async fn test_memory_provider_with_data() {
173        let mut data = HashMap::new();
174        data.insert("key1".to_string(), "value1".to_string());
175        data.insert("key2".to_string(), "value2".to_string());
176
177        let provider = MemoryConfigProvider::with_data(data);
178
179        assert_eq!(provider.len().await, 2);
180        assert_eq!(
181            provider.get_raw("key1").await.unwrap(),
182            Some("value1".to_string())
183        );
184    }
185}