Skip to main content

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 named(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 from_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 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 fails only if the Arc has been cloned, which cannot happen
50        // during builder-style construction.
51        Arc::get_mut(&mut self.data)
52            .expect(
53                "MemoryConfigProvider::value() called after Arc was shared; use insert() instead",
54            )
55            .get_mut()
56            .insert(key.into(), value.into());
57        self
58    }
59
60    /// Insert a value asynchronously
61    pub async fn insert(&self, key: impl Into<String>, value: impl Into<String>) {
62        let mut data = self.data.write().await;
63        data.insert(key.into(), value.into());
64    }
65
66    /// Get the number of stored values
67    pub async fn len(&self) -> usize {
68        self.data.read().await.len()
69    }
70
71    /// Check if empty
72    pub async fn is_empty(&self) -> bool {
73        self.data.read().await.is_empty()
74    }
75
76    /// Clear all values
77    pub async fn clear(&self) {
78        self.data.write().await.clear();
79    }
80}
81
82#[async_trait::async_trait]
83impl ConfigProvider for MemoryConfigProvider {
84    fn name(&self) -> &str {
85        &self.name
86    }
87
88    async fn get_raw(&self, key: &str) -> ConfigResult<Option<String>> {
89        let data = self.data.read().await;
90        Ok(data.get(key).cloned())
91    }
92
93    async fn set_raw(&self, key: &str, value: &str) -> ConfigResult<()> {
94        let mut data = self.data.write().await;
95        data.insert(key.to_string(), value.to_string());
96        Ok(())
97    }
98
99    async fn delete(&self, key: &str) -> ConfigResult<bool> {
100        let mut data = self.data.write().await;
101        Ok(data.remove(key).is_some())
102    }
103
104    async fn list_keys(&self, prefix: &str) -> ConfigResult<Vec<String>> {
105        let data = self.data.read().await;
106        let keys: Vec<String> = data
107            .keys()
108            .filter(|k| k.starts_with(prefix))
109            .cloned()
110            .collect();
111        Ok(keys)
112    }
113}
114
115#[cfg(test)]
116mod tests {
117    use super::*;
118
119    #[tokio::test]
120    async fn test_memory_provider_basic() {
121        let provider = MemoryConfigProvider::new();
122
123        // Set and get
124        provider.set_raw("key1", "value1").await.unwrap();
125        let value = provider.get_raw("key1").await.unwrap();
126        assert_eq!(value, Some("value1".to_string()));
127
128        // Get non-existent
129        let value = provider.get_raw("nonexistent").await.unwrap();
130        assert_eq!(value, None);
131    }
132
133    #[tokio::test]
134    async fn test_memory_provider_delete() {
135        let provider = MemoryConfigProvider::new();
136
137        provider.set_raw("key1", "value1").await.unwrap();
138        assert!(provider.delete("key1").await.unwrap());
139        assert!(!provider.delete("key1").await.unwrap());
140
141        let value = provider.get_raw("key1").await.unwrap();
142        assert_eq!(value, None);
143    }
144
145    #[tokio::test]
146    async fn test_memory_provider_list_keys() {
147        let provider = MemoryConfigProvider::new();
148
149        provider.set_raw("app.name", "test").await.unwrap();
150        provider.set_raw("app.version", "1.0").await.unwrap();
151        provider.set_raw("other.key", "value").await.unwrap();
152
153        let keys = provider.list_keys("app.").await.unwrap();
154        assert_eq!(keys.len(), 2);
155        assert!(keys.contains(&"app.name".to_string()));
156        assert!(keys.contains(&"app.version".to_string()));
157    }
158
159    #[tokio::test]
160    async fn test_memory_provider_typed() {
161        use crate::config::provider::ConfigProviderExt;
162
163        let provider = MemoryConfigProvider::new();
164
165        // Set typed value
166        ConfigProviderExt::set(&provider, "count", &42i32)
167            .await
168            .unwrap();
169
170        // Get typed value
171        let count: Option<i32> = ConfigProviderExt::get(&provider, "count").await.unwrap();
172        assert_eq!(count, Some(42));
173    }
174
175    #[tokio::test]
176    async fn test_memory_provider_with_data() {
177        let mut data = HashMap::new();
178        data.insert("key1".to_string(), "value1".to_string());
179        data.insert("key2".to_string(), "value2".to_string());
180
181        let provider = MemoryConfigProvider::from_data(data);
182
183        assert_eq!(provider.len().await, 2);
184        assert_eq!(
185            provider.get_raw("key1").await.unwrap(),
186            Some("value1".to_string())
187        );
188    }
189}