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(crate) 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 Fn(environment:&crate::Environment::MountainEnvironment::MountainEnvironment) -> Result<(), CommonError> {
111 dev_log!(
112 "config",
113 "[ConfigurationProvider] Re-initializing and merging all configurations..."
114 );
115
116 let default_config = collect_default_configurations(&environment.ApplicationState)?;
117
118 let user_settings_path = environment
119 .ApplicationHandle
120 .path()
121 .app_config_dir()
122 .map(|p| p.join("settings.json"))
123 .ok();
124
125 let workspace_settings_path = environment
126 .ApplicationState
127 .Workspace
128 .WorkspaceConfigurationPath
129 .lock()
130 .map_err(Utility::ErrorMapping::MapApplicationStateLockErrorToCommonError)?
131 .clone();
132
133 let user_config = read_and_parse_configuration_file(environment, &user_settings_path).await?;
134
135 let workspace_config = read_and_parse_configuration_file(environment, &workspace_settings_path).await?;
136
137 let mut merged = default_config.as_object().cloned().unwrap_or_default();
140
141 if let Some(user_map) = user_config.as_object() {
142 for (key, value) in user_map {
143 if value.is_object() && merged.get(key.as_str()).is_some_and(|v| v.is_object()) {
145 if let (Some(user_value), Some(_base_value)) =
146 (value.as_object(), merged.get(key.as_str()).and_then(|v| v.as_object()))
147 {
148 for (inner_key, inner_value) in user_value {
149 merged.get_mut(key.as_str()).and_then(|v| v.as_object_mut()).map(|m| {
150 m.insert(inner_key.clone(), inner_value.clone());
151 });
152 }
153 }
154 } else {
155 merged.insert(key.clone(), value.clone());
156 }
157 }
158 }
159
160 if let Some(workspace_map) = workspace_config.as_object() {
161 for (key, value) in workspace_map {
162 if value.is_object() && merged.get(key.as_str()).is_some_and(|v| v.is_object()) {
163 if let (Some(workspace_value), Some(_base_value)) =
164 (value.as_object(), merged.get(key.as_str()).and_then(|v| v.as_object()))
165 {
166 for (inner_key, inner_value) in workspace_value {
167 merged.get_mut(key.as_str()).and_then(|v| v.as_object_mut()).map(|m| {
168 m.insert(inner_key.clone(), inner_value.clone());
169 });
170 }
171 }
172 } else {
173 merged.insert(key.clone(), value.clone());
174 }
175 }
176 }
177
178 let configuration_size = merged.len();
179
180 let final_config = MergedConfigurationStateDTO::Create(Value::Object(merged));
181
182 *environment
183 .ApplicationState
184 .Configuration
185 .GlobalConfiguration
186 .lock()
187 .map_err(Utility::ErrorMapping::MapApplicationStateLockErrorToCommonError)? = final_config.Data;
188
189 dev_log!(
190 "config",
191 "[ConfigurationProvider] Configuration merged successfully with {} top-level keys.",
192 configuration_size
193 );
194
195 Ok(())
196}
197
198pub(super) fn collect_default_configurations(
233 application_state:&crate::ApplicationState::State::ApplicationState::ApplicationState,
234) -> Result<Value, CommonError> {
235 let mut default_config = Map::new();
236
237 for extension in application_state
238 .Extension
239 .ScannedExtensions
240 .ScannedExtensions
241 .lock()
242 .map_err(Utility::ErrorMapping::MapApplicationStateLockErrorToCommonError)?
243 .values()
244 {
245 let Some(contributes) = &extension.Contributes else {
246 continue;
247 };
248
249 let Some(configuration) = contributes.get("configuration") else {
250 continue;
251 };
252
253 let blocks:Vec<&Value> = if let Some(array) = configuration.as_array() {
255 array.iter().collect()
256 } else {
257 vec![configuration]
258 };
259
260 for block in blocks {
261 let Some(properties) = block.get("properties").and_then(|p| p.as_object()) else {
262 continue;
263 };
264
265 for (DottedKey, schema) in properties {
266 let Some(default) = schema.get("default") else {
267 continue;
268 };
269
270 InsertDottedDefault(&mut default_config, DottedKey, default.clone());
271 }
272 }
273 }
274
275 Ok(Value::Object(default_config))
276}
277
278fn InsertDottedDefault(target:&mut Map<String, Value>, dotted:&str, value:Value) {
283 let parts:Vec<&str> = dotted.split('.').collect();
284
285 if parts.is_empty() {
286 return;
287 }
288
289 if parts.len() == 1 {
290 target.insert(parts[0].to_string(), value);
291
292 return;
293 }
294
295 let head = parts[0];
296
297 let entry = target.entry(head.to_string()).or_insert_with(|| Value::Object(Map::new()));
298
299 if !entry.is_object() {
300 *entry = Value::Object(Map::new());
305 }
306
307 if let Some(child) = entry.as_object_mut() {
308 let mut sub = std::mem::take(child);
313
314 let RemainingDotted = parts[1..].join(".");
315
316 InsertDottedDefault(&mut sub, &RemainingDotted, value);
317
318 *child = sub;
319 }
320}