Mountain/Environment/ConfigurationProvider/
Loading.rs1use std::{
19 collections::HashMap,
20 path::PathBuf,
21 sync::{Arc, Mutex, OnceLock},
22 time::{Duration, Instant},
23};
24
25use CommonLibrary::{
26 Effect::ApplicationRunTime::ApplicationRunTime as _,
27 Error::CommonError::CommonError,
28 FileSystem::ReadFile::ReadFile,
29};
30use serde_json::{Map, Value};
31use tauri::Manager;
32
33use crate::{
34 ApplicationState::DTO::MergedConfigurationStateDTO::MergedConfigurationStateDTO,
35 Environment::Utility,
36 RunTime::ApplicationRunTime::ApplicationRunTime,
37 dev_log,
38};
39
40const SETTINGS_FILE_CACHE_TTL_MS:u64 = 250;
50
51struct CachedSettingsValue {
52 StoredAt:Instant,
53
54 Parsed:Value,
55}
56
57fn SettingsFileCache() -> &'static Mutex<HashMap<PathBuf, CachedSettingsValue>> {
58 static CACHE:OnceLock<Mutex<HashMap<PathBuf, CachedSettingsValue>>> = OnceLock::new();
59
60 CACHE.get_or_init(|| Mutex::new(HashMap::new()))
61}
62
63pub fn ClearSettingsFileCache() {
67 if let Ok(mut Guard) = SettingsFileCache().lock() {
68 Guard.clear();
69 }
70}
71
72pub(super) async fn read_and_parse_configuration_file(
74 environment:&crate::Environment::MountainEnvironment::MountainEnvironment,
75
76 path:&Option<PathBuf>,
77) -> Result<Value, CommonError> {
78 if let Some(p) = path {
79 if let Ok(Guard) = SettingsFileCache().lock() {
82 if let Some(Entry) = Guard.get(p) {
83 if Entry.StoredAt.elapsed() < Duration::from_millis(SETTINGS_FILE_CACHE_TTL_MS) {
84 return Ok(Entry.Parsed.clone());
85 }
86 }
87 }
88
89 let runtime = environment.ApplicationHandle.state::<Arc<ApplicationRunTime>>().inner().clone();
90
91 if let Ok(bytes) = runtime.Run(ReadFile(p.clone())).await {
92 let Parsed = serde_json::from_slice(&bytes).unwrap_or_else(|_| Value::Object(Map::new()));
93
94 if let Ok(mut Guard) = SettingsFileCache().lock() {
95 Guard.insert(
96 p.clone(),
97 CachedSettingsValue { StoredAt:Instant::now(), Parsed:Parsed.clone() },
98 );
99 }
100
101 return Ok(Parsed);
102 }
103 }
104
105 Ok(Value::Object(Map::new()))
106}
107
108pub async fn initialize_and_merge_configurations(
111 environment:&crate::Environment::MountainEnvironment::MountainEnvironment,
112) -> Result<(), CommonError> {
113 dev_log!(
114 "config",
115 "[ConfigurationProvider] Re-initializing and merging all configurations..."
116 );
117
118 let default_config = collect_default_configurations(&environment.ApplicationState)?;
119
120 let user_settings_path = environment
121 .ApplicationHandle
122 .path()
123 .app_config_dir()
124 .map(|p| p.join("settings.json"))
125 .ok();
126
127 let workspace_settings_path = environment
128 .ApplicationState
129 .Workspace
130 .WorkspaceConfigurationPath
131 .lock()
132 .map_err(Utility::ErrorMapping::MapApplicationStateLockErrorToCommonError)?
133 .clone();
134
135 let user_config = read_and_parse_configuration_file(environment, &user_settings_path).await?;
136
137 let workspace_config = read_and_parse_configuration_file(environment, &workspace_settings_path).await?;
138
139 let mut merged = default_config.as_object().cloned().unwrap_or_default();
142
143 if let Some(user_map) = user_config.as_object() {
144 for (key, value) in user_map {
145 if value.is_object() && merged.get(key.as_str()).is_some_and(|v| v.is_object()) {
147 if let (Some(user_value), Some(_base_value)) =
148 (value.as_object(), merged.get(key.as_str()).and_then(|v| v.as_object()))
149 {
150 for (inner_key, inner_value) in user_value {
151 merged.get_mut(key.as_str()).and_then(|v| v.as_object_mut()).map(|m| {
152 m.insert(inner_key.clone(), inner_value.clone());
153 });
154 }
155 }
156 } else {
157 merged.insert(key.clone(), value.clone());
158 }
159 }
160 }
161
162 if let Some(workspace_map) = workspace_config.as_object() {
163 for (key, value) in workspace_map {
164 if value.is_object() && merged.get(key.as_str()).is_some_and(|v| v.is_object()) {
165 if let (Some(workspace_value), Some(_base_value)) =
166 (value.as_object(), merged.get(key.as_str()).and_then(|v| v.as_object()))
167 {
168 for (inner_key, inner_value) in workspace_value {
169 merged.get_mut(key.as_str()).and_then(|v| v.as_object_mut()).map(|m| {
170 m.insert(inner_key.clone(), inner_value.clone());
171 });
172 }
173 }
174 } else {
175 merged.insert(key.clone(), value.clone());
176 }
177 }
178 }
179
180 let configuration_size = merged.len();
181
182 let final_config = MergedConfigurationStateDTO::Create(Value::Object(merged));
183
184 *environment
185 .ApplicationState
186 .Configuration
187 .GlobalConfiguration
188 .lock()
189 .map_err(Utility::ErrorMapping::MapApplicationStateLockErrorToCommonError)? = final_config.Data;
190
191 dev_log!(
192 "config",
193 "[ConfigurationProvider] Configuration merged successfully with {} top-level keys.",
194 configuration_size
195 );
196
197 Ok(())
198}
199
200pub(super) fn collect_default_configurations(
235 application_state:&crate::ApplicationState::State::ApplicationState::ApplicationState,
236) -> Result<Value, CommonError> {
237 let mut default_config = Map::new();
238
239 for extension in application_state
240 .Extension
241 .ScannedExtensions
242 .ScannedExtensions
243 .lock()
244 .map_err(Utility::ErrorMapping::MapApplicationStateLockErrorToCommonError)?
245 .values()
246 {
247 let Some(contributes) = &extension.Contributes else {
248 continue;
249 };
250
251 let Some(configuration) = contributes.get("configuration") else {
252 continue;
253 };
254
255 let blocks:Vec<&Value> = if let Some(array) = configuration.as_array() {
257 array.iter().collect()
258 } else {
259 vec![configuration]
260 };
261
262 for block in blocks {
263 let Some(properties) = block.get("properties").and_then(|p| p.as_object()) else {
264 continue;
265 };
266
267 for (DottedKey, schema) in properties {
268 let Some(default) = schema.get("default") else {
269 continue;
270 };
271
272 InsertDottedDefault(&mut default_config, DottedKey, default.clone());
273 }
274 }
275 }
276
277 Ok(Value::Object(default_config))
278}
279
280fn InsertDottedDefault(target:&mut Map<String, Value>, dotted:&str, value:Value) {
285 let parts:Vec<&str> = dotted.split('.').collect();
286
287 if parts.is_empty() {
288 return;
289 }
290
291 if parts.len() == 1 {
292 target.insert(parts[0].to_string(), value);
293
294 return;
295 }
296
297 let head = parts[0];
298
299 let entry = target.entry(head.to_string()).or_insert_with(|| Value::Object(Map::new()));
300
301 if !entry.is_object() {
302 *entry = Value::Object(Map::new());
307 }
308
309 if let Some(child) = entry.as_object_mut() {
310 let mut sub = std::mem::take(child);
315
316 let RemainingDotted = parts[1..].join(".");
317
318 InsertDottedDefault(&mut sub, &RemainingDotted, value);
319
320 *child = sub;
321 }
322}