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