Mountain/Environment/
ConfigurationProvider.rs1#![allow(non_snake_case, non_camel_case_types)]
16
17use std::{path::PathBuf, sync::Arc};
18
19use Common::{
20 Configuration::{
21 ConfigurationInspector::ConfigurationInspector,
22 ConfigurationProvider::ConfigurationProvider,
23 DTO::{
24 ConfigurationOverridesDTO::ConfigurationOverridesDTO,
25 ConfigurationTarget::ConfigurationTarget,
26 InspectResultDataDTO::InspectResultDataDTO,
27 },
28 },
29 Effect::ApplicationRunTime::ApplicationRunTime as _,
30 Error::CommonError::CommonError,
31 FileSystem::{ReadFile::ReadFile, WriteFileBytes::WriteFileBytes},
32};
33use async_trait::async_trait;
34use log::{debug, info, warn};
35use serde_json::{Map, Value};
36use tauri::Manager;
37
38use super::{MountainEnvironment::MountainEnvironment, Utility};
39use crate::{
40 ApplicationState::DTO::MergedConfigurationStateDTO::MergedConfigurationStateDTO,
41 ExtensionManagement::Scanner::CollectDefaultConfigurations,
42 RunTime::ApplicationRunTime::ApplicationRunTime as MountainRunTime,
43};
44
45#[async_trait]
46impl ConfigurationProvider for MountainEnvironment {
47 async fn GetConfigurationValue(
49 &self,
50
51 Section:Option<String>,
52
53 _Overrides:ConfigurationOverridesDTO,
54 ) -> Result<Value, CommonError> {
55 debug!("[ConfigurationProvider] Getting configuration for section: {:?}", Section);
56
57 let ConfigurationGuard = self
58 .ApplicationState
59 .Configuration
60 .lock()
61 .map_err(Utility::MapApplicationStateLockErrorToCommonError)?;
62
63 Ok(ConfigurationGuard.GetValue(Section.as_deref()))
64 }
65
66 async fn UpdateConfigurationValue(
68 &self,
69
70 Key:String,
71
72 ValueToSet:Value,
73
74 Target:ConfigurationTarget,
75
76 _Overrides:ConfigurationOverridesDTO,
77
78 _ScopeToLanguage:Option<bool>,
79 ) -> Result<(), CommonError> {
80 info!("[ConfigurationProvider] Updating key '{}' in target {:?}", Key, Target);
81
82 let RunTime = self.ApplicationHandle.state::<Arc<MountainRunTime>>().inner().clone();
83
84 let ConfigPath:PathBuf = match Target {
85 ConfigurationTarget::User => {
86 self.ApplicationHandle
87 .path()
88 .app_config_dir()
89 .map(|p| p.join("settings.json"))
90 .map_err(|Error| {
91 CommonError::ConfigurationLoad {
92 Description:format!("Could not resolve user config path: {}", Error),
93 }
94 })?
95 },
96
97 ConfigurationTarget::WorkSpace => {
98 self.ApplicationState
99 .WorkSpaceConfigurationPath
100 .lock()
101 .map_err(Utility::MapApplicationStateLockErrorToCommonError)?
102 .clone()
103 .ok_or_else(|| {
104 CommonError::ConfigurationLoad { Description:"No workspace configuration path set".into() }
105 })?
106 },
107
108 _ => {
109 warn!("[ConfigurationProvider] Unsupported configuration target: {:?}", Target);
110
111 return Err(CommonError::NotImplemented {
112 FeatureName:"This configuration target is not supported".into(),
113 });
114 },
115 };
116
117 let Bytes = RunTime.Run(ReadFile(ConfigPath.clone())).await.unwrap_or_default();
119
120 let mut CurrentConfig:Value = serde_json::from_slice(&Bytes).unwrap_or_else(|_| Value::Object(Map::new()));
121
122 if let Value::Object(Map) = &mut CurrentConfig {
123 if ValueToSet.is_null() {
124 Map.remove(&Key);
125 } else {
126 Map.insert(Key.clone(), ValueToSet);
127 }
128 }
129
130 let ContentBytes = serde_json::to_vec_pretty(&CurrentConfig)?;
131
132 RunTime
133 .Run(WriteFileBytes(ConfigPath.clone(), ContentBytes, true, true))
134 .await?;
135
136 InitializeAndMergeConfigurations(self).await?;
138
139 Ok(())
140 }
141}
142
143#[async_trait]
144impl ConfigurationInspector for MountainEnvironment {
145 async fn InspectConfigurationValue(
147 &self,
148
149 Key:String,
150
151 _Overrides:ConfigurationOverridesDTO,
152 ) -> Result<Option<InspectResultDataDTO>, CommonError> {
153 info!("[ConfigurationProvider] Inspecting key: {}", Key);
154
155 let UserSettingsPath = self
156 .ApplicationHandle
157 .path()
158 .app_config_dir()
159 .map(|p| p.join("settings.json"))
160 .ok();
161
162 let WorkSpaceSettingsPath = self
163 .ApplicationState
164 .WorkSpaceConfigurationPath
165 .lock()
166 .map_err(Utility::MapApplicationStateLockErrorToCommonError)?
167 .clone();
168
169 let DefaultConfig = CollectDefaultConfigurations(&self.ApplicationState)?;
171
172 let UserConfig = ReadAndParseConfigurationFile(self, &UserSettingsPath).await?;
173
174 let WorkSpaceConfig = ReadAndParseConfigurationFile(self, &WorkSpaceSettingsPath).await?;
175
176 let GetValueFromDotPath =
177 |Node:&Value, Path:&str| -> Option<Value> { Path.split('.').try_fold(Node, |n, k| n.get(k)).cloned() };
178
179 let mut ResultDTO = InspectResultDataDTO::default();
180
181 ResultDTO.DefaultValue = GetValueFromDotPath(&DefaultConfig, &Key);
182
183 ResultDTO.UserValue = GetValueFromDotPath(&UserConfig, &Key);
184
185 ResultDTO.WorkSpaceValue = GetValueFromDotPath(&WorkSpaceConfig, &Key);
186
187 ResultDTO.EffectiveValue = ResultDTO
189 .WorkSpaceValue
190 .clone()
191 .or_else(|| ResultDTO.UserValue.clone())
192 .or_else(|| ResultDTO.DefaultValue.clone());
193
194 if ResultDTO.EffectiveValue.is_some() { Ok(Some(ResultDTO)) } else { Ok(None) }
195 }
196}
197
198async fn ReadAndParseConfigurationFile(
200 Environment:&MountainEnvironment,
201
202 Path:&Option<PathBuf>,
203) -> Result<Value, CommonError> {
204 if let Some(p) = Path {
205 let RunTime = Environment.ApplicationHandle.state::<Arc<MountainRunTime>>().inner().clone();
206
207 if let Ok(Bytes) = RunTime.Run(ReadFile(p.clone())).await {
208 return Ok(serde_json::from_slice(&Bytes).unwrap_or(Value::Object(Map::new())));
209 }
210 }
211
212 Ok(Value::Object(Map::new()))
213}
214
215pub async fn InitializeAndMergeConfigurations(Environment:&MountainEnvironment) -> Result<(), CommonError> {
218 info!("[ConfigurationProvider] Re-initializing and merging all configurations...");
219
220 let DefaultConfig = CollectDefaultConfigurations(&Environment.ApplicationState)?;
221
222 let UserSettingsPath = Environment
223 .ApplicationHandle
224 .path()
225 .app_config_dir()
226 .map(|p| p.join("settings.json"))
227 .ok();
228
229 let WorkSpaceSettingsPath = Environment
230 .ApplicationState
231 .WorkSpaceConfigurationPath
232 .lock()
233 .map_err(Utility::MapApplicationStateLockErrorToCommonError)?
234 .clone();
235
236 let UserConfig = ReadAndParseConfigurationFile(Environment, &UserSettingsPath).await?;
237
238 let WorkSpaceConfig = ReadAndParseConfigurationFile(Environment, &WorkSpaceSettingsPath).await?;
239
240 let mut Merged = DefaultConfig.as_object().cloned().unwrap_or_default();
242
243 if let Some(UserMap) = UserConfig.as_object() {
244 for (k, v) in UserMap {
245 Merged.insert(k.clone(), v.clone());
246 }
247 }
248
249 if let Some(WorkSpaceMap) = WorkSpaceConfig.as_object() {
250 for (k, v) in WorkSpaceMap {
251 Merged.insert(k.clone(), v.clone());
252 }
253 }
254
255 let FinalConfig = MergedConfigurationStateDTO::Create(Value::Object(Merged));
256
257 *Environment
258 .ApplicationState
259 .Configuration
260 .lock()
261 .map_err(Utility::MapApplicationStateLockErrorToCommonError)? = FinalConfig;
262
263 info!("[ConfigurationProvider] Configuration state updated and merged.");
264
265 Ok(())
266}