1use crate::{ChangePayload, StoreState};
6use serde_json::Value as JsonValue;
7use std::{
8 collections::HashMap,
9 fs,
10 path::{Path, PathBuf},
11 sync::{Arc, Mutex},
12 time::Duration,
13};
14use tauri::{path::BaseDirectory, AppHandle, Emitter, Manager, Resource, ResourceId, Runtime};
15use tokio::{
16 select,
17 sync::mpsc::{unbounded_channel, UnboundedSender},
18 time::sleep,
19};
20
21pub type SerializeFn =
22 fn(&HashMap<String, JsonValue>) -> Result<Vec<u8>, Box<dyn std::error::Error + Send + Sync>>;
23pub type DeserializeFn =
24 fn(&[u8]) -> Result<HashMap<String, JsonValue>, Box<dyn std::error::Error + Send + Sync>>;
25
26pub fn resolve_store_path<R: Runtime>(
27 app: &AppHandle<R>,
28 path: impl AsRef<Path>,
29) -> crate::Result<PathBuf> {
30 Ok(dunce::simplified(&app.path().resolve(path, BaseDirectory::AppData)?).to_path_buf())
31}
32
33pub struct StoreBuilder<R: Runtime> {
35 app: AppHandle<R>,
36 path: PathBuf,
37 defaults: Option<HashMap<String, JsonValue>>,
38 serialize_fn: SerializeFn,
39 deserialize_fn: DeserializeFn,
40 auto_save: Option<Duration>,
41 create_new: bool,
42 override_defaults: bool,
43}
44
45impl<R: Runtime> StoreBuilder<R> {
46 pub fn new<M: Manager<R>, P: AsRef<Path>>(manager: &M, path: P) -> Self {
58 let app = manager.app_handle().clone();
59 let state = app.state::<StoreState>();
60 let serialize_fn = state.default_serialize;
61 let deserialize_fn = state.default_deserialize;
62 Self {
63 app,
64 path: path.as_ref().to_path_buf(),
65 defaults: None,
66 serialize_fn,
67 deserialize_fn,
68 auto_save: Some(Duration::from_millis(100)),
69 create_new: false,
70 override_defaults: false,
71 }
72 }
73
74 pub fn defaults(mut self, defaults: HashMap<String, JsonValue>) -> Self {
91 self.defaults = Some(defaults);
92 self
93 }
94
95 pub fn default(mut self, key: impl Into<String>, value: impl Into<JsonValue>) -> Self {
109 let key = key.into();
110 let value = value.into();
111 self.defaults
112 .get_or_insert(HashMap::new())
113 .insert(key, value);
114 self
115 }
116
117 pub fn serialize(mut self, serialize: SerializeFn) -> Self {
131 self.serialize_fn = serialize;
132 self
133 }
134
135 pub fn deserialize(mut self, deserialize: DeserializeFn) -> Self {
149 self.deserialize_fn = deserialize;
150 self
151 }
152
153 pub fn auto_save(mut self, debounce_duration: Duration) -> Self {
167 self.auto_save = Some(debounce_duration);
168 self
169 }
170
171 pub fn disable_auto_save(mut self) -> Self {
173 self.auto_save = None;
174 self
175 }
176
177 pub fn create_new(mut self) -> Self {
179 self.create_new = true;
180 self
181 }
182
183 pub fn override_defaults(mut self) -> Self {
185 self.override_defaults = true;
186 self
187 }
188
189 pub(crate) fn build_inner(mut self) -> crate::Result<(Arc<Store<R>>, ResourceId)> {
190 let stores = self.app.state::<StoreState>().stores.clone();
191 let mut stores = stores.lock().unwrap();
192
193 self.path = resolve_store_path(&self.app, self.path)?;
194
195 if self.create_new {
196 if let Some(rid) = stores.remove(&self.path) {
197 let _ = self.app.resources_table().take::<Store<R>>(rid);
198 }
199 } else if let Some(rid) = stores.get(&self.path) {
200 return Ok((self.app.resources_table().get(*rid)?, *rid));
204 }
205
206 let mut store_inner = StoreInner::new(
211 self.app.clone(),
212 self.path.clone(),
213 self.defaults.take(),
214 self.serialize_fn,
215 self.deserialize_fn,
216 );
217
218 if !self.create_new {
219 if self.override_defaults {
220 let _ = store_inner.load_ignore_defaults();
221 } else {
222 let _ = store_inner.load();
223 }
224 }
225
226 let store = Store {
227 auto_save: self.auto_save,
228 auto_save_debounce_sender: Arc::new(Mutex::new(None)),
229 store: Arc::new(Mutex::new(store_inner)),
230 };
231
232 let store = Arc::new(store);
233 let rid = self.app.resources_table().add_arc(store.clone());
234 stores.insert(self.path, rid);
235
236 Ok((store, rid))
237 }
238
239 pub fn build(self) -> crate::Result<Arc<Store<R>>> {
253 let (store, _) = self.build_inner()?;
254 Ok(store)
255 }
256}
257
258enum AutoSaveMessage {
259 Reset,
260 Cancel,
261}
262
263#[derive(Clone)]
264struct StoreInner<R: Runtime> {
265 app: AppHandle<R>,
266 path: PathBuf,
267 cache: HashMap<String, JsonValue>,
268 defaults: Option<HashMap<String, JsonValue>>,
269 serialize_fn: SerializeFn,
270 deserialize_fn: DeserializeFn,
271}
272
273impl<R: Runtime> StoreInner<R> {
274 fn new(
275 app: AppHandle<R>,
276 path: PathBuf,
277 defaults: Option<HashMap<String, JsonValue>>,
278 serialize_fn: SerializeFn,
279 deserialize_fn: DeserializeFn,
280 ) -> Self {
281 Self {
282 app,
283 path,
284 cache: defaults.clone().unwrap_or_default(),
285 defaults,
286 serialize_fn,
287 deserialize_fn,
288 }
289 }
290
291 pub fn save(&self) -> crate::Result<()> {
293 fs::create_dir_all(self.path.parent().expect("invalid store path"))?;
294
295 let bytes = (self.serialize_fn)(&self.cache).map_err(crate::Error::Serialize)?;
296 fs::write(&self.path, bytes)?;
297
298 Ok(())
299 }
300
301 pub fn load(&mut self) -> crate::Result<()> {
305 let bytes = fs::read(&self.path)?;
306
307 self.cache
308 .extend((self.deserialize_fn)(&bytes).map_err(crate::Error::Deserialize)?);
309
310 Ok(())
311 }
312
313 pub fn load_ignore_defaults(&mut self) -> crate::Result<()> {
315 let bytes = fs::read(&self.path)?;
316 self.cache = (self.deserialize_fn)(&bytes).map_err(crate::Error::Deserialize)?;
317 Ok(())
318 }
319
320 pub fn set(&mut self, key: impl Into<String>, value: impl Into<JsonValue>) {
322 let key = key.into();
323 let value = value.into();
324 self.cache.insert(key.clone(), value.clone());
325 let _ = self.emit_change_event(&key, Some(&value));
326 }
327
328 pub fn get(&self, key: impl AsRef<str>) -> Option<&JsonValue> {
330 self.cache.get(key.as_ref())
331 }
332
333 pub fn has(&self, key: impl AsRef<str>) -> bool {
335 self.cache.contains_key(key.as_ref())
336 }
337
338 pub fn delete(&mut self, key: impl AsRef<str>) -> bool {
340 let flag = self.cache.remove(key.as_ref()).is_some();
341 if flag {
342 let _ = self.emit_change_event(key.as_ref(), None);
343 }
344 flag
345 }
346
347 pub fn clear(&mut self) {
351 let keys: Vec<String> = self.cache.keys().cloned().collect();
352 self.cache.clear();
353 for key in &keys {
354 let _ = self.emit_change_event(key, None);
355 }
356 }
357
358 pub fn reset(&mut self) {
362 if let Some(defaults) = &self.defaults {
363 for (key, value) in &self.cache {
364 if defaults.get(key) != Some(value) {
365 let _ = self.emit_change_event(key, defaults.get(key));
366 }
367 }
368 for (key, value) in defaults {
369 if !self.cache.contains_key(key) {
370 let _ = self.emit_change_event(key, Some(value));
371 }
372 }
373 self.cache.clone_from(defaults);
374 } else {
375 self.clear()
376 }
377 }
378
379 pub fn keys(&self) -> impl Iterator<Item = &String> {
381 self.cache.keys()
382 }
383
384 pub fn values(&self) -> impl Iterator<Item = &JsonValue> {
386 self.cache.values()
387 }
388
389 pub fn entries(&self) -> impl Iterator<Item = (&String, &JsonValue)> {
391 self.cache.iter()
392 }
393
394 pub fn len(&self) -> usize {
396 self.cache.len()
397 }
398
399 pub fn is_empty(&self) -> bool {
401 self.cache.is_empty()
402 }
403
404 fn emit_change_event(&self, key: &str, value: Option<&JsonValue>) -> crate::Result<()> {
405 let state = self.app.state::<StoreState>();
406 let stores = state.stores.lock().unwrap();
407 let exists = value.is_some();
408 self.app.emit(
409 "store://change",
410 ChangePayload {
411 path: &self.path,
412 resource_id: stores.get(&self.path).copied(),
413 key,
414 value,
415 exists,
416 },
417 )?;
418 Ok(())
419 }
420}
421
422impl<R: Runtime> std::fmt::Debug for StoreInner<R> {
423 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
424 f.debug_struct("Store")
425 .field("path", &self.path)
426 .field("cache", &self.cache)
427 .finish()
428 }
429}
430
431pub struct Store<R: Runtime> {
432 auto_save: Option<Duration>,
433 auto_save_debounce_sender: Arc<Mutex<Option<UnboundedSender<AutoSaveMessage>>>>,
434 store: Arc<Mutex<StoreInner<R>>>,
435}
436
437impl<R: Runtime> Resource for Store<R> {
438 fn close(self: Arc<Self>) {
439 let store = self.store.lock().unwrap();
440 let state = store.app.state::<StoreState>();
441 let mut stores = state.stores.lock().unwrap();
442 stores.remove(&store.path);
443 }
444}
445
446impl<R: Runtime> Store<R> {
447 pub fn set(&self, key: impl Into<String>, value: impl Into<JsonValue>) {
456 self.store.lock().unwrap().set(key.into(), value.into());
457 let _ = self.trigger_auto_save();
458 }
459
460 pub fn get(&self, key: impl AsRef<str>) -> Option<JsonValue> {
462 self.store.lock().unwrap().get(key).cloned()
463 }
464
465 pub fn has(&self, key: impl AsRef<str>) -> bool {
467 self.store.lock().unwrap().has(key)
468 }
469
470 pub fn delete(&self, key: impl AsRef<str>) -> bool {
472 let deleted = self.store.lock().unwrap().delete(key);
473 if deleted {
474 let _ = self.trigger_auto_save();
475 }
476 deleted
477 }
478
479 pub fn clear(&self) {
483 self.store.lock().unwrap().clear();
484 let _ = self.trigger_auto_save();
485 }
486
487 pub fn reset(&self) {
491 self.store.lock().unwrap().reset();
492 let _ = self.trigger_auto_save();
493 }
494
495 pub fn keys(&self) -> Vec<String> {
497 self.store.lock().unwrap().keys().cloned().collect()
498 }
499
500 pub fn values(&self) -> Vec<JsonValue> {
502 self.store.lock().unwrap().values().cloned().collect()
503 }
504
505 pub fn entries(&self) -> Vec<(String, JsonValue)> {
507 self.store
508 .lock()
509 .unwrap()
510 .entries()
511 .map(|(k, v)| (k.to_owned(), v.to_owned()))
512 .collect()
513 }
514
515 pub fn length(&self) -> usize {
517 self.store.lock().unwrap().len()
518 }
519
520 pub fn is_empty(&self) -> bool {
522 self.store.lock().unwrap().is_empty()
523 }
524
525 pub fn reload(&self) -> crate::Result<()> {
534 self.store.lock().unwrap().load()
535 }
536
537 pub fn reload_ignore_defaults(&self) -> crate::Result<()> {
541 self.store.lock().unwrap().load_ignore_defaults()
542 }
543
544 pub fn save(&self) -> crate::Result<()> {
546 if let Some(sender) = self.auto_save_debounce_sender.lock().unwrap().take() {
547 let _ = sender.send(AutoSaveMessage::Cancel);
548 }
549 self.store.lock().unwrap().save()
550 }
551
552 pub fn close_resource(&self) {
554 let store = self.store.lock().unwrap();
555 let app = store.app.clone();
556 let state = app.state::<StoreState>();
557 let stores = state.stores.lock().unwrap();
558 if let Some(rid) = stores.get(&store.path).copied() {
559 drop(store);
560 drop(stores);
561 let _ = app.resources_table().close(rid);
562 }
563 }
564
565 fn trigger_auto_save(&self) -> crate::Result<()> {
566 let Some(auto_save_delay) = self.auto_save else {
567 return Ok(());
568 };
569 if auto_save_delay.is_zero() {
570 return self.save();
571 }
572 let mut auto_save_debounce_sender = self.auto_save_debounce_sender.lock().unwrap();
573 if let Some(ref sender) = *auto_save_debounce_sender {
574 let _ = sender.send(AutoSaveMessage::Reset);
575 return Ok(());
576 }
577 let (sender, mut receiver) = unbounded_channel();
578 auto_save_debounce_sender.replace(sender);
579 drop(auto_save_debounce_sender);
580 let store = self.store.clone();
581 let auto_save_debounce_sender = self.auto_save_debounce_sender.clone();
582 tauri::async_runtime::spawn(async move {
583 loop {
584 select! {
585 should_cancel = receiver.recv() => {
586 if matches!(should_cancel, Some(AutoSaveMessage::Cancel) | None) {
587 return;
588 }
589 }
590 _ = sleep(auto_save_delay) => {
591 auto_save_debounce_sender.lock().unwrap().take();
592 let _ = store.lock().unwrap().save();
593 return;
594 }
595 };
596 }
597 });
598 Ok(())
599 }
600
601 fn apply_pending_auto_save(&self) {
602 if let Some(sender) = self.auto_save_debounce_sender.lock().unwrap().take() {
604 let _ = sender.send(AutoSaveMessage::Cancel);
605 let _ = self.save();
606 };
607 }
608}
609
610impl<R: Runtime> Drop for Store<R> {
611 fn drop(&mut self) {
612 self.apply_pending_auto_save();
613 }
614}