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::{AppHandle, Emitter, Manager, Resource, ResourceId, Runtime, path::BaseDirectory};
15use tokio::{
16 select,
17 sync::mpsc::{UnboundedSender, unbounded_channel},
18 time::sleep,
19};
20
21pub type SerializeFn =
25 fn(&HashMap<String, JsonValue>) -> Result<Vec<u8>, Box<dyn std::error::Error + Send + Sync>>;
26pub type DeserializeFn =
30 fn(&[u8]) -> Result<HashMap<String, JsonValue>, Box<dyn std::error::Error + Send + Sync>>;
31
32pub fn resolve_store_path<R: Runtime>(
41 app: &AppHandle<R>,
42 path: impl AsRef<Path>,
43) -> crate::Result<PathBuf> {
44 Ok(dunce::simplified(&app.path().resolve(path, BaseDirectory::AppData)?).to_path_buf())
45}
46
47pub struct StoreBuilder<R: Runtime> {
49 app: AppHandle<R>,
50 path: PathBuf,
51 defaults: Option<HashMap<String, JsonValue>>,
52 serialize_fn: SerializeFn,
53 deserialize_fn: DeserializeFn,
54 auto_save: Option<Duration>,
55 create_new: bool,
56 override_defaults: bool,
57}
58
59impl<R: Runtime> StoreBuilder<R> {
60 pub fn new<M: Manager<R>, P: AsRef<Path>>(manager: &M, path: P) -> Self {
72 let app = manager.app_handle().clone();
73 let state = app.state::<StoreState>();
74 let serialize_fn = state.default_serialize;
75 let deserialize_fn = state.default_deserialize;
76 Self {
77 app,
78 path: path.as_ref().to_path_buf(),
79 defaults: None,
80 serialize_fn,
81 deserialize_fn,
82 auto_save: Some(Duration::from_millis(100)),
83 create_new: false,
84 override_defaults: false,
85 }
86 }
87
88 pub fn defaults(mut self, defaults: HashMap<String, JsonValue>) -> Self {
105 self.defaults = Some(defaults);
106 self
107 }
108
109 pub fn default(mut self, key: impl Into<String>, value: impl Into<JsonValue>) -> Self {
123 let key = key.into();
124 let value = value.into();
125 self.defaults
126 .get_or_insert(HashMap::new())
127 .insert(key, value);
128 self
129 }
130
131 pub fn serialize(mut self, serialize: SerializeFn) -> Self {
145 self.serialize_fn = serialize;
146 self
147 }
148
149 pub fn deserialize(mut self, deserialize: DeserializeFn) -> Self {
163 self.deserialize_fn = deserialize;
164 self
165 }
166
167 pub fn auto_save(mut self, debounce_duration: Duration) -> Self {
181 self.auto_save = Some(debounce_duration);
182 self
183 }
184
185 pub fn disable_auto_save(mut self) -> Self {
187 self.auto_save = None;
188 self
189 }
190
191 pub fn create_new(mut self) -> Self {
193 self.create_new = true;
194 self
195 }
196
197 pub fn override_defaults(mut self) -> Self {
199 self.override_defaults = true;
200 self
201 }
202
203 pub(crate) fn build_inner(mut self) -> crate::Result<(Arc<Store<R>>, ResourceId)> {
204 let stores = self.app.state::<StoreState>().stores.clone();
205 let mut stores = stores.write().unwrap();
206
207 self.path = resolve_store_path(&self.app, self.path)?;
208
209 if self.create_new {
210 if let Some(rid) = stores.remove(&self.path) {
211 let _ = self.app.resources_table().take::<Store<R>>(rid);
212 }
213 } else if let Some(rid) = stores.get(&self.path) {
214 return Ok((self.app.resources_table().get(*rid)?, *rid));
218 }
219
220 let mut store_inner = StoreInner::new(
225 self.app.clone(),
226 self.path.clone(),
227 self.defaults.take(),
228 self.serialize_fn,
229 self.deserialize_fn,
230 );
231
232 if !self.create_new {
233 if self.override_defaults {
234 let _ = store_inner.load_ignore_defaults();
235 } else {
236 let _ = store_inner.load();
237 }
238 }
239
240 let store = Store {
241 auto_save: self.auto_save,
242 auto_save_debounce_sender: Arc::new(Mutex::new(None)),
243 store: Arc::new(Mutex::new(store_inner)),
244 };
245
246 let store = Arc::new(store);
247 let rid = self.app.resources_table().add_arc(store.clone());
248 stores.insert(self.path, rid);
249
250 Ok((store, rid))
251 }
252
253 pub fn build(self) -> crate::Result<Arc<Store<R>>> {
267 let (store, _) = self.build_inner()?;
268 Ok(store)
269 }
270}
271
272enum AutoSaveMessage {
273 Reset,
274 Cancel,
275}
276
277#[derive(Clone)]
278struct StoreInner<R: Runtime> {
279 app: AppHandle<R>,
280 path: PathBuf,
281 cache: HashMap<String, JsonValue>,
282 defaults: Option<HashMap<String, JsonValue>>,
283 serialize_fn: SerializeFn,
284 deserialize_fn: DeserializeFn,
285}
286
287impl<R: Runtime> StoreInner<R> {
288 fn new(
289 app: AppHandle<R>,
290 path: PathBuf,
291 defaults: Option<HashMap<String, JsonValue>>,
292 serialize_fn: SerializeFn,
293 deserialize_fn: DeserializeFn,
294 ) -> Self {
295 Self {
296 app,
297 path,
298 cache: defaults.clone().unwrap_or_default(),
299 defaults,
300 serialize_fn,
301 deserialize_fn,
302 }
303 }
304
305 pub fn save(&self) -> crate::Result<()> {
307 fs::create_dir_all(self.path.parent().expect("invalid store path"))?;
308
309 let bytes = (self.serialize_fn)(&self.cache).map_err(crate::Error::Serialize)?;
310 fs::write(&self.path, bytes)?;
311
312 Ok(())
313 }
314
315 pub fn load(&mut self) -> crate::Result<()> {
319 let bytes = fs::read(&self.path)?;
320
321 self.cache
322 .extend((self.deserialize_fn)(&bytes).map_err(crate::Error::Deserialize)?);
323
324 Ok(())
325 }
326
327 pub fn load_ignore_defaults(&mut self) -> crate::Result<()> {
329 let bytes = fs::read(&self.path)?;
330 self.cache = (self.deserialize_fn)(&bytes).map_err(crate::Error::Deserialize)?;
331 Ok(())
332 }
333
334 pub fn set(&mut self, key: impl Into<String>, value: impl Into<JsonValue>) {
336 let key = key.into();
337 let value = value.into();
338 self.cache.insert(key.clone(), value.clone());
339 let _ = self.emit_change_event(&key, Some(&value));
340 }
341
342 pub fn get(&self, key: impl AsRef<str>) -> Option<&JsonValue> {
344 self.cache.get(key.as_ref())
345 }
346
347 pub fn has(&self, key: impl AsRef<str>) -> bool {
349 self.cache.contains_key(key.as_ref())
350 }
351
352 pub fn delete(&mut self, key: impl AsRef<str>) -> bool {
354 let flag = self.cache.remove(key.as_ref()).is_some();
355 if flag {
356 let _ = self.emit_change_event(key.as_ref(), None);
357 }
358 flag
359 }
360
361 pub fn clear(&mut self) {
365 let keys: Vec<String> = self.cache.keys().cloned().collect();
366 self.cache.clear();
367 for key in &keys {
368 let _ = self.emit_change_event(key, None);
369 }
370 }
371
372 pub fn reset(&mut self) {
376 if let Some(defaults) = &self.defaults {
377 for (key, value) in &self.cache {
378 if defaults.get(key) != Some(value) {
379 let _ = self.emit_change_event(key, defaults.get(key));
380 }
381 }
382 for (key, value) in defaults {
383 if !self.cache.contains_key(key) {
384 let _ = self.emit_change_event(key, Some(value));
385 }
386 }
387 self.cache.clone_from(defaults);
388 } else {
389 self.clear()
390 }
391 }
392
393 pub fn keys(&self) -> impl Iterator<Item = &String> {
395 self.cache.keys()
396 }
397
398 pub fn values(&self) -> impl Iterator<Item = &JsonValue> {
400 self.cache.values()
401 }
402
403 pub fn entries(&self) -> impl Iterator<Item = (&String, &JsonValue)> {
405 self.cache.iter()
406 }
407
408 pub fn len(&self) -> usize {
410 self.cache.len()
411 }
412
413 pub fn is_empty(&self) -> bool {
415 self.cache.is_empty()
416 }
417
418 fn emit_change_event(&self, key: &str, value: Option<&JsonValue>) -> crate::Result<()> {
419 let state = self.app.state::<StoreState>();
420 let stores = state.stores.read().unwrap();
421 let exists = value.is_some();
422 self.app.emit(
423 "store://change",
424 ChangePayload {
425 path: &self.path,
426 resource_id: stores.get(&self.path).copied(),
427 key,
428 value,
429 exists,
430 },
431 )?;
432 Ok(())
433 }
434}
435
436impl<R: Runtime> std::fmt::Debug for StoreInner<R> {
437 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
438 f.debug_struct("Store")
439 .field("path", &self.path)
440 .field("cache", &self.cache)
441 .finish()
442 }
443}
444
445pub struct Store<R: Runtime> {
455 auto_save: Option<Duration>,
456 auto_save_debounce_sender: Arc<Mutex<Option<UnboundedSender<AutoSaveMessage>>>>,
457 store: Arc<Mutex<StoreInner<R>>>,
458}
459
460impl<R: Runtime> Resource for Store<R> {
461 fn close(self: Arc<Self>) {
462 let store = self.store.lock().unwrap();
463 let state = store.app.state::<StoreState>();
464 let mut stores = state.stores.write().unwrap();
465 stores.remove(&store.path);
466 }
467}
468
469impl<R: Runtime> Store<R> {
470 pub fn set(&self, key: impl Into<String>, value: impl Into<JsonValue>) {
479 self.store.lock().unwrap().set(key.into(), value.into());
480 let _ = self.trigger_auto_save();
481 }
482
483 pub fn get(&self, key: impl AsRef<str>) -> Option<JsonValue> {
485 self.store.lock().unwrap().get(key).cloned()
486 }
487
488 pub fn has(&self, key: impl AsRef<str>) -> bool {
490 self.store.lock().unwrap().has(key)
491 }
492
493 pub fn delete(&self, key: impl AsRef<str>) -> bool {
495 let deleted = self.store.lock().unwrap().delete(key);
496 if deleted {
497 let _ = self.trigger_auto_save();
498 }
499 deleted
500 }
501
502 pub fn clear(&self) {
506 self.store.lock().unwrap().clear();
507 let _ = self.trigger_auto_save();
508 }
509
510 pub fn reset(&self) {
514 self.store.lock().unwrap().reset();
515 let _ = self.trigger_auto_save();
516 }
517
518 pub fn keys(&self) -> Vec<String> {
520 self.store.lock().unwrap().keys().cloned().collect()
521 }
522
523 pub fn values(&self) -> Vec<JsonValue> {
525 self.store.lock().unwrap().values().cloned().collect()
526 }
527
528 pub fn entries(&self) -> Vec<(String, JsonValue)> {
530 self.store
531 .lock()
532 .unwrap()
533 .entries()
534 .map(|(k, v)| (k.to_owned(), v.to_owned()))
535 .collect()
536 }
537
538 pub fn length(&self) -> usize {
540 self.store.lock().unwrap().len()
541 }
542
543 pub fn is_empty(&self) -> bool {
545 self.store.lock().unwrap().is_empty()
546 }
547
548 pub fn reload(&self) -> crate::Result<()> {
557 self.store.lock().unwrap().load()
558 }
559
560 pub fn reload_ignore_defaults(&self) -> crate::Result<()> {
564 self.store.lock().unwrap().load_ignore_defaults()
565 }
566
567 pub fn save(&self) -> crate::Result<()> {
569 if let Some(sender) = self.auto_save_debounce_sender.lock().unwrap().take() {
570 let _ = sender.send(AutoSaveMessage::Cancel);
571 }
572 self.store.lock().unwrap().save()
573 }
574
575 pub fn close_resource(&self) {
577 let store = self.store.lock().unwrap();
578 let app = store.app.clone();
579 let state = app.state::<StoreState>();
580 let stores = state.stores.read().unwrap();
581 if let Some(rid) = stores.get(&store.path).copied() {
582 drop(store);
583 drop(stores);
584 let _ = app.resources_table().close(rid);
585 }
586 }
587
588 fn trigger_auto_save(&self) -> crate::Result<()> {
589 let Some(auto_save_delay) = self.auto_save else {
590 return Ok(());
591 };
592 if auto_save_delay.is_zero() {
593 return self.save();
594 }
595 let mut auto_save_debounce_sender = self.auto_save_debounce_sender.lock().unwrap();
596 if let Some(ref sender) = *auto_save_debounce_sender {
597 let _ = sender.send(AutoSaveMessage::Reset);
598 return Ok(());
599 }
600 let (sender, mut receiver) = unbounded_channel();
601 auto_save_debounce_sender.replace(sender);
602 drop(auto_save_debounce_sender);
603 let store = self.store.clone();
604 let auto_save_debounce_sender = self.auto_save_debounce_sender.clone();
605 tauri::async_runtime::spawn(async move {
606 loop {
607 select! {
608 should_cancel = receiver.recv() => {
609 if matches!(should_cancel, Some(AutoSaveMessage::Cancel) | None) {
610 return;
611 }
612 }
613 _ = sleep(auto_save_delay) => {
614 auto_save_debounce_sender.lock().unwrap().take();
615 let _ = store.lock().unwrap().save();
616 return;
617 }
618 };
619 }
620 });
621 Ok(())
622 }
623
624 fn apply_pending_auto_save(&self) {
625 let auto_save_debounce_sender = self.auto_save_debounce_sender.lock().unwrap().take();
627 if let Some(sender) = auto_save_debounce_sender {
628 let _ = sender.send(AutoSaveMessage::Cancel);
629 let _ = self.save();
630 };
631 }
632}
633
634impl<R: Runtime> Drop for Store<R> {
635 fn drop(&mut self) {
636 self.apply_pending_auto_save();
637 }
638}