1use std::{
39 collections::HashMap,
40 path::PathBuf,
41 sync::{
42 Arc,
43 Mutex,
44 OnceLock,
45 atomic::{AtomicBool, Ordering},
46 },
47};
48
49use CommonLibrary::{Error::CommonError::CommonError, Storage::StorageProvider::StorageProvider};
50use async_trait::async_trait;
51use serde_json::Value;
52use tokio::fs;
53
54use super::{MountainEnvironment::MountainEnvironment, Utility};
55use crate::dev_log;
56
57struct StorageWriteDebouncer {
61 Pending:Mutex<Option<(PathBuf, HashMap<String, Value>)>>,
62
63 FlushScheduled:AtomicBool,
64}
65
66impl StorageWriteDebouncer {
67 fn new() -> Arc<Self> { Arc::new(Self { Pending:Mutex::new(None), FlushScheduled:AtomicBool::new(false) }) }
68
69 fn Queue(&self, Path:PathBuf, Data:HashMap<String, Value>, Debouncer:Arc<Self>) {
70 if let Ok(mut Guard) = self.Pending.lock() {
71 *Guard = Some((Path, Data));
72 }
73
74 if !self.FlushScheduled.swap(true, Ordering::AcqRel) {
75 tokio::spawn(async move {
76 tokio::time::sleep(std::time::Duration::from_millis(100)).await;
77
78 let Item = {
79 let mut Guard = Debouncer.Pending.lock().unwrap();
80
81 let Item = Guard.take();
82
83 Debouncer.FlushScheduled.store(false, Ordering::Release);
88
89 Item
90 };
91
92 if let Some((StoragePath, StorageData)) = Item {
93 SaveStorageToDisk(StoragePath, StorageData).await;
94 }
95 });
96 }
97 }
98}
99
100static GLOBAL_DEBOUNCER:OnceLock<Arc<StorageWriteDebouncer>> = OnceLock::new();
101
102static WORKSPACE_DEBOUNCER:OnceLock<Arc<StorageWriteDebouncer>> = OnceLock::new();
103
104fn GetGlobalDebouncer() -> Arc<StorageWriteDebouncer> {
105 GLOBAL_DEBOUNCER.get_or_init(StorageWriteDebouncer::new).clone()
106}
107
108fn GetWorkspaceDebouncer() -> Arc<StorageWriteDebouncer> {
109 WORKSPACE_DEBOUNCER.get_or_init(StorageWriteDebouncer::new).clone()
110}
111
112#[async_trait]
117impl StorageProvider for MountainEnvironment {
118 async fn GetStorageValue(&self, IsGlobalScope:bool, Key:&str) -> Result<Option<Value>, CommonError> {
121 let ScopeName = if IsGlobalScope { "Global" } else { "Workspace" };
122
123 dev_log!(
124 "storage",
125 "[StorageProvider] Getting value from {} scope for key: {}",
126 ScopeName,
127 Key
128 );
129
130 if Key.is_empty() {
132 return Ok(None);
133 }
134
135 if Key.len() > 1024 {
136 return Err(CommonError::InvalidArgument {
137 ArgumentName:"Key".into(),
138 Reason:"Key length exceeds maximum allowed length of 1024 characters".into(),
139 });
140 }
141
142 let StorageMapMutex = if IsGlobalScope {
143 &self.ApplicationState.Configuration.MementoGlobalStorage
144 } else {
145 &self.ApplicationState.Configuration.MementoWorkspaceStorage
146 };
147
148 let StorageMapGuard = StorageMapMutex
149 .lock()
150 .map_err(Utility::ErrorMapping::MapApplicationStateLockErrorToCommonError)?;
151
152 Ok(StorageMapGuard.get(Key).cloned())
153 }
154
155 async fn UpdateStorageValue(
159 &self,
160
161 IsGlobalScope:bool,
162
163 Key:String,
164
165 ValueToSet:Option<Value>,
166 ) -> Result<(), CommonError> {
167 let ScopeName = if IsGlobalScope { "Global" } else { "Workspace" };
168
169 if crate::IPC::DevLog::IsShort::Fn() {
175 crate::dev_log!("storage-verbose", "update {} {}", ScopeName, Key);
176 } else {
177 dev_log!(
178 "storage-verbose",
179 "[StorageProvider] Updating value in {} scope for key: {}",
180 ScopeName,
181 Key
182 );
183 }
184
185 if Key.is_empty() {
187 return Err(CommonError::InvalidArgument {
188 ArgumentName:"Key".into(),
189 Reason:"Key cannot be empty".into(),
190 });
191 }
192
193 if Key.len() > 1024 {
194 return Err(CommonError::InvalidArgument {
195 ArgumentName:"Key".into(),
196 Reason:"Key length exceeds maximum allowed length of 1024 characters".into(),
197 });
198 }
199
200 if let Some(ref value) = ValueToSet {
202 if let Ok(json_string) = serde_json::to_string(value) {
203 if json_string.len() > 10 * 1024 * 1024 {
204 return Err(CommonError::InvalidArgument {
206 ArgumentName:"ValueToSet".into(),
207 Reason:"Value size exceeds maximum allowed size of 10MB".into(),
208 });
209 }
210 }
211 }
212
213 let (StorageMapMutex, StoragePathOption) = if IsGlobalScope {
214 (
215 self.ApplicationState.Configuration.MementoGlobalStorage.clone(),
216 Some(
217 self.ApplicationState
218 .GlobalMementoPath
219 .lock()
220 .map_err(Utility::ErrorMapping::MapApplicationStateLockErrorToCommonError)?
221 .clone(),
222 ),
223 )
224 } else {
225 (
226 self.ApplicationState.Configuration.MementoWorkspaceStorage.clone(),
227 self.ApplicationState
228 .WorkspaceMementoPath
229 .lock()
230 .map_err(Utility::ErrorMapping::MapApplicationStateLockErrorToCommonError)?
231 .clone(),
232 )
233 };
234
235 let DataToSave = {
237 let mut StorageMapGuard = StorageMapMutex
238 .lock()
239 .map_err(Utility::ErrorMapping::MapApplicationStateLockErrorToCommonError)?;
240
241 if let Some(Value) = ValueToSet {
242 StorageMapGuard.insert(Key, Value);
243 } else {
244 StorageMapGuard.remove(&Key);
245 }
246
247 StorageMapGuard.clone()
248 };
249
250 if let Some(StoragePath) = StoragePathOption {
251 let Debouncer = if IsGlobalScope { GetGlobalDebouncer() } else { GetWorkspaceDebouncer() };
254
255 Debouncer.Queue(StoragePath, DataToSave, Debouncer.clone());
256 }
257
258 Ok(())
259 }
260
261 async fn GetAllStorage(&self, IsGlobalScope:bool) -> Result<Value, CommonError> {
263 let ScopeName = if IsGlobalScope { "Global" } else { "Workspace" };
264
265 dev_log!(
266 "storage-verbose",
267 "[StorageProvider] Getting all values from {} scope.",
268 ScopeName
269 );
270
271 let StorageMapMutex = if IsGlobalScope {
272 &self.ApplicationState.Configuration.MementoGlobalStorage
273 } else {
274 &self.ApplicationState.Configuration.MementoWorkspaceStorage
275 };
276
277 let StorageMapGuard = StorageMapMutex
278 .lock()
279 .map_err(Utility::ErrorMapping::MapApplicationStateLockErrorToCommonError)?;
280
281 Ok(serde_json::to_value(&*StorageMapGuard)?)
282 }
283
284 async fn SetAllStorage(&self, IsGlobalScope:bool, FullState:Value) -> Result<(), CommonError> {
286 let ScopeName = if IsGlobalScope { "Global" } else { "Workspace" };
287
288 dev_log!(
289 "storage-verbose",
290 "[StorageProvider] Setting all values for {} scope.",
291 ScopeName
292 );
293
294 let DeserializedState:HashMap<String, Value> = serde_json::from_value(FullState)?;
295
296 let (StorageMapMutex, StoragePathOption) = if IsGlobalScope {
297 (
298 self.ApplicationState.Configuration.MementoGlobalStorage.clone(),
299 Some(
300 self.ApplicationState
301 .GlobalMementoPath
302 .lock()
303 .map_err(Utility::ErrorMapping::MapApplicationStateLockErrorToCommonError)?
304 .clone(),
305 ),
306 )
307 } else {
308 (
309 self.ApplicationState.Configuration.MementoWorkspaceStorage.clone(),
310 self.ApplicationState
311 .WorkspaceMementoPath
312 .lock()
313 .map_err(Utility::ErrorMapping::MapApplicationStateLockErrorToCommonError)?
314 .clone(),
315 )
316 };
317
318 *StorageMapMutex
320 .lock()
321 .map_err(Utility::ErrorMapping::MapApplicationStateLockErrorToCommonError)? = DeserializedState.clone();
322
323 if let Some(StoragePath) = StoragePathOption {
326 let Debouncer = if IsGlobalScope { GetGlobalDebouncer() } else { GetWorkspaceDebouncer() };
327
328 Debouncer.Queue(StoragePath, DeserializedState, Debouncer.clone());
329 }
330
331 Ok(())
332 }
333}
334
335async fn SaveStorageToDisk(Path:PathBuf, Data:HashMap<String, Value>) {
340 dev_log!(
344 "storage-verbose",
345 "[StorageProvider] Persisting storage to disk: {}",
346 Path.display()
347 );
348
349 match serde_json::to_string_pretty(&Data) {
350 Ok(JSONString) => {
351 if let Some(ParentDirectory) = Path.parent() {
352 if let Err(Error) = fs::create_dir_all(ParentDirectory).await {
353 dev_log!(
354 "storage",
355 "error: [StorageProvider] Failed to create parent directory for '{}': {}",
356 Path.display(),
357 Error
358 );
359
360 return;
361 }
362 }
363
364 if let Err(Error) = fs::write(&Path, JSONString).await {
365 dev_log!(
366 "storage",
367 "error: [StorageProvider] Failed to write storage file to '{}': {}",
368 Path.display(),
369 Error
370 );
371 }
372 },
373
374 Err(Error) => {
375 dev_log!(
376 "storage",
377 "error: [StorageProvider] Failed to serialize storage data for '{}': {}",
378 Path.display(),
379 Error
380 );
381 },
382 }
383}