claude_agent/config/
composite.rs1use super::ConfigResult;
7use super::provider::ConfigProvider;
8
9pub struct CompositeConfigProvider {
11 providers: Vec<Box<dyn ConfigProvider>>,
12}
13
14impl CompositeConfigProvider {
15 pub fn new() -> Self {
17 Self {
18 providers: Vec::new(),
19 }
20 }
21
22 pub fn add_provider(&mut self, provider: Box<dyn ConfigProvider>) {
24 self.providers.push(provider);
25 }
26
27 pub fn with_provider(mut self, provider: Box<dyn ConfigProvider>) -> Self {
29 self.providers.push(provider);
30 self
31 }
32
33 pub fn provider_count(&self) -> usize {
35 self.providers.len()
36 }
37
38 pub fn provider_names(&self) -> Vec<&str> {
40 self.providers.iter().map(|p| p.name()).collect()
41 }
42}
43
44impl Default for CompositeConfigProvider {
45 fn default() -> Self {
46 Self::new()
47 }
48}
49
50#[async_trait::async_trait]
51impl ConfigProvider for CompositeConfigProvider {
52 fn name(&self) -> &str {
53 "composite"
54 }
55
56 async fn get_raw(&self, key: &str) -> ConfigResult<Option<String>> {
57 for provider in &self.providers {
59 if let Some(value) = provider.get_raw(key).await? {
60 return Ok(Some(value));
61 }
62 }
63 Ok(None)
64 }
65
66 async fn set_raw(&self, key: &str, value: &str) -> ConfigResult<()> {
67 if let Some(provider) = self.providers.first() {
69 provider.set_raw(key, value).await?;
70 }
71 Ok(())
72 }
73
74 async fn delete(&self, key: &str) -> ConfigResult<bool> {
75 let mut deleted = false;
77 for provider in &self.providers {
78 if provider.delete(key).await? {
79 deleted = true;
80 }
81 }
82 Ok(deleted)
83 }
84
85 async fn list_keys(&self, prefix: &str) -> ConfigResult<Vec<String>> {
86 let mut all_keys = std::collections::HashSet::new();
88 for provider in &self.providers {
89 for key in provider.list_keys(prefix).await? {
90 all_keys.insert(key);
91 }
92 }
93 Ok(all_keys.into_iter().collect())
94 }
95}
96
97impl std::fmt::Debug for CompositeConfigProvider {
98 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
99 f.debug_struct("CompositeConfigProvider")
100 .field("provider_count", &self.providers.len())
101 .field("provider_names", &self.provider_names())
102 .finish()
103 }
104}
105
106#[cfg(test)]
107mod tests {
108 use super::*;
109 use crate::config::memory::MemoryConfigProvider;
110
111 #[tokio::test]
112 async fn test_composite_provider_priority() {
113 let high_priority = MemoryConfigProvider::with_name("high");
114 high_priority.set_raw("key", "high_value").await.unwrap();
115
116 let low_priority = MemoryConfigProvider::with_name("low");
117 low_priority.set_raw("key", "low_value").await.unwrap();
118 low_priority.set_raw("only_low", "from_low").await.unwrap();
119
120 let composite = CompositeConfigProvider::new()
121 .with_provider(Box::new(high_priority))
122 .with_provider(Box::new(low_priority));
123
124 assert_eq!(
126 composite.get_raw("key").await.unwrap(),
127 Some("high_value".to_string())
128 );
129
130 assert_eq!(
132 composite.get_raw("only_low").await.unwrap(),
133 Some("from_low".to_string())
134 );
135 }
136
137 #[tokio::test]
138 async fn test_composite_provider_list_keys() {
139 let p1 = MemoryConfigProvider::new();
140 p1.set_raw("app.name", "value1").await.unwrap();
141 p1.set_raw("app.version", "value2").await.unwrap();
142
143 let p2 = MemoryConfigProvider::new();
144 p2.set_raw("app.config", "value3").await.unwrap();
145 p2.set_raw("other", "value4").await.unwrap();
146
147 let composite = CompositeConfigProvider::new()
148 .with_provider(Box::new(p1))
149 .with_provider(Box::new(p2));
150
151 let keys = composite.list_keys("app.").await.unwrap();
152 assert_eq!(keys.len(), 3); }
154
155 #[tokio::test]
156 async fn test_composite_provider_set() {
157 let composite = CompositeConfigProvider::new()
158 .with_provider(Box::new(MemoryConfigProvider::new()))
159 .with_provider(Box::new(MemoryConfigProvider::new()));
160
161 composite.set_raw("new_key", "new_value").await.unwrap();
162
163 assert_eq!(
165 composite.get_raw("new_key").await.unwrap(),
166 Some("new_value".to_string())
167 );
168 }
169
170 #[tokio::test]
171 async fn test_composite_provider_delete() {
172 let p1 = MemoryConfigProvider::new();
173 p1.set_raw("shared", "from_p1").await.unwrap();
174
175 let p2 = MemoryConfigProvider::new();
176 p2.set_raw("shared", "from_p2").await.unwrap();
177
178 let composite = CompositeConfigProvider::new()
179 .with_provider(Box::new(p1))
180 .with_provider(Box::new(p2));
181
182 assert!(composite.delete("shared").await.unwrap());
184 assert_eq!(composite.get_raw("shared").await.unwrap(), None);
185 }
186
187 #[tokio::test]
188 async fn test_composite_provider_names() {
189 let composite = CompositeConfigProvider::new()
190 .with_provider(Box::new(MemoryConfigProvider::with_name("first")))
191 .with_provider(Box::new(MemoryConfigProvider::with_name("second")));
192
193 let names = composite.provider_names();
194 assert_eq!(names, vec!["first", "second"]);
195 }
196}