1use std::{
7 collections::HashMap,
8 path::{Path, PathBuf},
9 sync::Arc,
10};
11
12use anyhow::{Context, Result};
13use serde_json::Value;
14use tokio::sync::RwLock;
15
16use crate::{Services::Service, dev_log};
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
20pub enum ConfigurationScope {
21 Global,
23 Workspace,
25 Extension,
27}
28
29#[derive(Debug, Clone)]
31pub struct ConfigurationValue {
32 pub value:Value,
34 pub scope:ConfigurationScope,
36 pub modified_at:u64,
38}
39
40pub struct ConfigurationServiceImpl {
42 name:String,
44 config:Arc<RwLock<HashMap<String, ConfigurationValue>>>,
46 config_paths:Arc<RwLock<HashMap<ConfigurationScope, PathBuf>>>,
48 running:Arc<RwLock<bool>>,
50 watchers:Arc<RwLock<HashMap<String, Vec<ConfigurationWatcherCallback>>>>,
52}
53
54type ConfigurationWatcherCallback = Arc<RwLock<dyn Fn(String, Value) -> Result<()> + Send + Sync>>;
56
57impl ConfigurationServiceImpl {
58 pub fn new(config_path:Option<PathBuf>) -> Self {
60 let mut config_paths = HashMap::new();
61
62 if let Some(path) = config_path {
63 config_paths.insert(ConfigurationScope::Global, path);
64 }
65
66 Self {
67 name:"ConfigurationService".to_string(),
68 config:Arc::new(RwLock::new(HashMap::new())),
69 config_paths:Arc::new(RwLock::new(config_paths)),
70 running:Arc::new(RwLock::new(false)),
71 watchers:Arc::new(RwLock::new(HashMap::new())),
72 }
73 }
74
75 pub async fn get(&self, key:&str) -> Option<Value> {
77 dev_log!("config", "Getting configuration value: {}", key);
78 self.config.read().await.get(key).map(|v| v.value.clone())
79 }
80
81 pub async fn get_with_default(&self, key:&str, default:Value) -> Value { self.get(key).await.unwrap_or(default) }
83
84 pub async fn set(&self, key:String, value:Value, scope:ConfigurationScope) -> Result<()> {
86 dev_log!("config", "Setting configuration value: {} = {:?}", key, value);
87
88 let now = std::time::SystemTime::now()
89 .duration_since(std::time::UNIX_EPOCH)
90 .map(|d| d.as_secs())
91 .unwrap_or(0);
92
93 let config_value = ConfigurationValue { value:value.clone(), scope, modified_at:now };
94
95 self.config.write().await.insert(key.clone(), config_value);
96
97 self.notify_watchers(key, value).await;
99
100 Ok(())
101 }
102
103 pub async fn remove(&self, key:String) -> Result<bool> {
105 dev_log!("config", "Removing configuration value: {}", key);
106
107 let removed = self.config.write().await.remove(&key).is_some();
108 Ok(removed)
109 }
110
111 pub async fn get_all(&self) -> HashMap<String, Value> {
113 self.config
114 .read()
115 .await
116 .iter()
117 .map(|(k, v)| (k.clone(), v.value.clone()))
118 .collect()
119 }
120
121 pub async fn get_all_in_scope(&self, scope:ConfigurationScope) -> HashMap<String, Value> {
123 self.config
124 .read()
125 .await
126 .iter()
127 .filter(|(_, v)| v.scope == scope)
128 .map(|(k, v)| (k.clone(), v.value.clone()))
129 .collect()
130 }
131
132 pub async fn load_from_file(&self, path:&Path, scope:ConfigurationScope) -> Result<()> {
134 dev_log!("config", "Loading configuration from: {:?}", path);
135
136 let content = tokio::fs::read_to_string(path)
137 .await
138 .context("Failed to read configuration file")?;
139
140 let config:Value = serde_json::from_str(&content).context("Failed to parse configuration file")?;
141
142 self.load_from_value(config, scope).await?;
143
144 self.config_paths.write().await.insert(scope, path.to_path_buf());
146
147 dev_log!("config", "Configuration loaded successfully");
148
149 Ok(())
150 }
151
152 pub async fn load_from_value(&self, value:Value, scope:ConfigurationScope) -> Result<()> {
154 if let Value::Object(object) = value {
155 let mut config = self.config.write().await;
156 let now = std::time::SystemTime::now()
157 .duration_since(std::time::UNIX_EPOCH)
158 .map(|d| d.as_secs())
159 .unwrap_or(0);
160
161 for (key, val) in object {
162 config.insert(key, ConfigurationValue { value:val, scope, modified_at:now });
163 }
164 }
165
166 Ok(())
167 }
168
169 pub async fn save_to_file(&self, path:&Path, scope:ConfigurationScope) -> Result<()> {
171 dev_log!("config", "Saving configuration to: {:?}", path);
172
173 let config = self.get_all_in_scope(scope).await;
174 let config_value = Value::Object(config.into_iter().map(|(k, v)| (k, v)).collect());
175
176 let content = serde_json::to_string_pretty(&config_value).context("Failed to serialize configuration")?;
177
178 tokio::fs::write(path, content)
179 .await
180 .context("Failed to write configuration file")?;
181
182 dev_log!("config", "Configuration saved successfully");
183
184 Ok(())
185 }
186
187 pub async fn register_watcher<F>(&self, key:String, callback:F)
189 where
190 F: Fn(String, Value) -> Result<()> + Send + Sync + 'static, {
191 let key_clone = key.clone();
192 let mut watchers = self.watchers.write().await;
193 watchers
194 .entry(key)
195 .or_insert_with(Vec::new)
196 .push(Arc::new(RwLock::new(callback)));
197 dev_log!("config", "Registered configuration watcher for: {}", key_clone);
198 }
199
200 pub async fn unregister_watcher(&self, key:String) -> Result<bool> {
202 let mut watchers = self.watchers.write().await;
203 let removed = watchers.remove(&key).is_some();
204 Ok(removed)
205 }
206
207 async fn notify_watchers(&self, key:String, value:Value) {
209 let watchers = self.watchers.read().await;
210
211 if let Some(callbacks) = watchers.get(&key) {
212 for callback in callbacks {
213 if let Err(e) = callback.read().await(key.clone(), value.clone()) {
214 dev_log!("config", "warn: configuration watcher callback failed: {}", e);
215 }
216 }
217 }
218 }
219
220 pub async fn get_config_paths(&self) -> HashMap<ConfigurationScope, PathBuf> {
222 self.config_paths.read().await.clone()
223 }
224}
225
226impl Service for ConfigurationServiceImpl {
227 fn name(&self) -> &str { &self.name }
228
229 async fn start(&self) -> Result<()> {
230 dev_log!("config", "Starting configuration service");
231
232 *self.running.write().await = true;
233
234 dev_log!("config", "Configuration service started");
235 Ok(())
236 }
237
238 async fn stop(&self) -> Result<()> {
239 dev_log!("config", "Stopping configuration service");
240
241 *self.running.write().await = false;
242
243 dev_log!("config", "Configuration service stopped");
244 Ok(())
245 }
246
247 async fn is_running(&self) -> bool { *self.running.read().await }
248}
249
250#[cfg(test)]
251mod tests {
252 use super::*;
253
254 #[tokio::test]
255 async fn test_configuration_service_basic() {
256 let service = ConfigurationServiceImpl::new(None);
257 let _:anyhow::Result<()> = service.start().await;
258
259 let _:anyhow::Result<()> = service
261 .set(
262 "test.key".to_string(),
263 serde_json::json!("test-value"),
264 ConfigurationScope::Global,
265 )
266 .await;
267
268 let value = service.get("test.key").await;
269 assert_eq!(value, Some(serde_json::json!("test-value")));
270
271 let _:anyhow::Result<()> = service.stop().await;
272 }
273
274 #[tokio::test]
275 async fn test_get_with_default() {
276 let service = ConfigurationServiceImpl::new(None);
277
278 let default = serde_json::json!("default-value");
279 let value = service.get_with_default("nonexistent.key", default.clone()).await;
280 assert_eq!(value, default);
281 }
282
283 #[tokio::test]
284 async fn test_get_all_in_scope() {
285 let service = ConfigurationServiceImpl::new(None);
286
287 let _:anyhow::Result<()> = service
288 .set("key1".to_string(), serde_json::json!("value1"), ConfigurationScope::Global)
289 .await;
290
291 let _:anyhow::Result<()> = service
292 .set("key2".to_string(), serde_json::json!("value2"), ConfigurationScope::Workspace)
293 .await;
294
295 let global_values = service.get_all_in_scope(ConfigurationScope::Global).await;
296 assert_eq!(global_values.len(), 1);
297 assert_eq!(global_values.get("key1"), Some(&serde_json::json!("value1")));
298 }
299
300 #[test]
301 fn test_configuration_scope() {
302 let global = ConfigurationScope::Global;
303 let workspace = ConfigurationScope::Workspace;
304 let extension = ConfigurationScope::Extension;
305
306 assert_eq!(global, ConfigurationScope::Global);
307 assert_ne!(global, workspace);
308 assert_ne!(global, extension);
309 }
310}