Mountain/Environment/ConfigurationProvider/
Loading.rs1use std::{
4 collections::HashMap,
5 path::PathBuf,
6 sync::{Arc, Mutex, OnceLock},
7 time::{Duration, Instant},
8};
9
10use CommonLibrary::{
11 Effect::ApplicationRunTime::ApplicationRunTime as _,
12 Error::CommonError::CommonError,
13 FileSystem::ReadFile::ReadFile,
14};
15use serde_json::{Map, Value};
16use tauri::Manager;
17
18use crate::{
19 ApplicationState::DTO::MergedConfigurationStateDTO::MergedConfigurationStateDTO,
20 Environment::Utility,
21 RunTime::ApplicationRunTime::ApplicationRunTime,
22 dev_log,
23};
24
25const SETTINGS_FILE_CACHE_TTL_MS:u64 = 250;
35
36struct CachedSettingsValue {
37 StoredAt:Instant,
38 Parsed:Value,
39}
40
41fn SettingsFileCache() -> &'static Mutex<HashMap<PathBuf, CachedSettingsValue>> {
42 static CACHE:OnceLock<Mutex<HashMap<PathBuf, CachedSettingsValue>>> = OnceLock::new();
43 CACHE.get_or_init(|| Mutex::new(HashMap::new()))
44}
45
46pub fn ClearSettingsFileCache() {
50 if let Ok(mut Guard) = SettingsFileCache().lock() {
51 Guard.clear();
52 }
53}
54
55pub(super) async fn read_and_parse_configuration_file(
57 environment:&crate::Environment::MountainEnvironment::MountainEnvironment,
58 path:&Option<PathBuf>,
59) -> Result<Value, CommonError> {
60 if let Some(p) = path {
61 if let Ok(Guard) = SettingsFileCache().lock() {
64 if let Some(Entry) = Guard.get(p) {
65 if Entry.StoredAt.elapsed() < Duration::from_millis(SETTINGS_FILE_CACHE_TTL_MS) {
66 return Ok(Entry.Parsed.clone());
67 }
68 }
69 }
70
71 let runtime = environment.ApplicationHandle.state::<Arc<ApplicationRunTime>>().inner().clone();
72
73 if let Ok(bytes) = runtime.Run(ReadFile(p.clone())).await {
74 let Parsed = serde_json::from_slice(&bytes).unwrap_or_else(|_| Value::Object(Map::new()));
75 if let Ok(mut Guard) = SettingsFileCache().lock() {
76 Guard.insert(
77 p.clone(),
78 CachedSettingsValue { StoredAt:Instant::now(), Parsed:Parsed.clone() },
79 );
80 }
81 return Ok(Parsed);
82 }
83 }
84
85 Ok(Value::Object(Map::new()))
86}
87
88pub async fn initialize_and_merge_configurations(
91 environment:&crate::Environment::MountainEnvironment::MountainEnvironment,
92) -> Result<(), CommonError> {
93 dev_log!(
94 "config",
95 "[ConfigurationProvider] Re-initializing and merging all configurations..."
96 );
97
98 let default_config = collect_default_configurations(&environment.ApplicationState)?;
99
100 let user_settings_path = environment
101 .ApplicationHandle
102 .path()
103 .app_config_dir()
104 .map(|p| p.join("settings.json"))
105 .ok();
106
107 let workspace_settings_path = environment
108 .ApplicationState
109 .Workspace
110 .WorkspaceConfigurationPath
111 .lock()
112 .map_err(Utility::ErrorMapping::MapApplicationStateLockErrorToCommonError)?
113 .clone();
114
115 let user_config = read_and_parse_configuration_file(environment, &user_settings_path).await?;
116
117 let workspace_config = read_and_parse_configuration_file(environment, &workspace_settings_path).await?;
118
119 let mut merged = default_config.as_object().cloned().unwrap_or_default();
122
123 if let Some(user_map) = user_config.as_object() {
124 for (key, value) in user_map {
125 if value.is_object() && merged.get(key.as_str()).is_some_and(|v| v.is_object()) {
127 if let (Some(user_value), Some(_base_value)) =
128 (value.as_object(), merged.get(key.as_str()).and_then(|v| v.as_object()))
129 {
130 for (inner_key, inner_value) in user_value {
131 merged.get_mut(key.as_str()).and_then(|v| v.as_object_mut()).map(|m| {
132 m.insert(inner_key.clone(), inner_value.clone());
133 });
134 }
135 }
136 } else {
137 merged.insert(key.clone(), value.clone());
138 }
139 }
140 }
141
142 if let Some(workspace_map) = workspace_config.as_object() {
143 for (key, value) in workspace_map {
144 if value.is_object() && merged.get(key.as_str()).is_some_and(|v| v.is_object()) {
145 if let (Some(workspace_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 workspace_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 let configuration_size = merged.len();
161 let final_config = MergedConfigurationStateDTO::Create(Value::Object(merged));
162
163 *environment
164 .ApplicationState
165 .Configuration
166 .GlobalConfiguration
167 .lock()
168 .map_err(Utility::ErrorMapping::MapApplicationStateLockErrorToCommonError)? = final_config.Data;
169
170 dev_log!(
171 "config",
172 "[ConfigurationProvider] Configuration merged successfully with {} top-level keys.",
173 configuration_size
174 );
175
176 Ok(())
177}
178
179pub(super) fn collect_default_configurations(
214 application_state:&crate::ApplicationState::State::ApplicationState::ApplicationState,
215) -> Result<Value, CommonError> {
216 let mut default_config = Map::new();
217
218 for extension in application_state
219 .Extension
220 .ScannedExtensions
221 .ScannedExtensions
222 .lock()
223 .map_err(Utility::ErrorMapping::MapApplicationStateLockErrorToCommonError)?
224 .values()
225 {
226 let Some(contributes) = &extension.Contributes else {
227 continue;
228 };
229 let Some(configuration) = contributes.get("configuration") else {
230 continue;
231 };
232
233 let blocks:Vec<&Value> = if let Some(array) = configuration.as_array() {
235 array.iter().collect()
236 } else {
237 vec![configuration]
238 };
239
240 for block in blocks {
241 let Some(properties) = block.get("properties").and_then(|p| p.as_object()) else {
242 continue;
243 };
244 for (DottedKey, schema) in properties {
245 let Some(default) = schema.get("default") else {
246 continue;
247 };
248 InsertDottedDefault(&mut default_config, DottedKey, default.clone());
249 }
250 }
251 }
252
253 Ok(Value::Object(default_config))
254}
255
256fn InsertDottedDefault(target:&mut Map<String, Value>, dotted:&str, value:Value) {
261 let parts:Vec<&str> = dotted.split('.').collect();
262 if parts.is_empty() {
263 return;
264 }
265 if parts.len() == 1 {
266 target.insert(parts[0].to_string(), value);
267 return;
268 }
269 let head = parts[0];
270 let entry = target.entry(head.to_string()).or_insert_with(|| Value::Object(Map::new()));
271 if !entry.is_object() {
272 *entry = Value::Object(Map::new());
277 }
278 if let Some(child) = entry.as_object_mut() {
279 let mut sub = std::mem::take(child);
284 let RemainingDotted = parts[1..].join(".");
285 InsertDottedDefault(&mut sub, &RemainingDotted, value);
286 *child = sub;
287 }
288}