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).unwrap(), *rid));
201 }
202
203 let mut store_inner = StoreInner::new(
208 self.app.clone(),
209 self.path.clone(),
210 self.defaults.take(),
211 self.serialize_fn,
212 self.deserialize_fn,
213 );
214
215 if !self.create_new {
216 if self.override_defaults {
217 let _ = store_inner.load_ignore_defaults();
218 } else {
219 let _ = store_inner.load();
220 }
221 }
222
223 let store = Store {
224 auto_save: self.auto_save,
225 auto_save_debounce_sender: Arc::new(Mutex::new(None)),
226 store: Arc::new(Mutex::new(store_inner)),
227 };
228
229 let store = Arc::new(store);
230 let rid = self.app.resources_table().add_arc(store.clone());
231 stores.insert(self.path, rid);
232
233 Ok((store, rid))
234 }
235
236 pub fn build(self) -> crate::Result<Arc<Store<R>>> {
250 let (store, _) = self.build_inner()?;
251 Ok(store)
252 }
253}
254
255enum AutoSaveMessage {
256 Reset,
257 Cancel,
258}
259
260#[derive(Clone)]
261struct StoreInner<R: Runtime> {
262 app: AppHandle<R>,
263 path: PathBuf,
264 cache: HashMap<String, JsonValue>,
265 defaults: Option<HashMap<String, JsonValue>>,
266 serialize_fn: SerializeFn,
267 deserialize_fn: DeserializeFn,
268}
269
270impl<R: Runtime> StoreInner<R> {
271 fn new(
272 app: AppHandle<R>,
273 path: PathBuf,
274 defaults: Option<HashMap<String, JsonValue>>,
275 serialize_fn: SerializeFn,
276 deserialize_fn: DeserializeFn,
277 ) -> Self {
278 Self {
279 app,
280 path,
281 cache: defaults.clone().unwrap_or_default(),
282 defaults,
283 serialize_fn,
284 deserialize_fn,
285 }
286 }
287
288 pub fn save(&self) -> crate::Result<()> {
290 fs::create_dir_all(self.path.parent().expect("invalid store path"))?;
291
292 let bytes = (self.serialize_fn)(&self.cache).map_err(crate::Error::Serialize)?;
293 fs::write(&self.path, bytes)?;
294
295 Ok(())
296 }
297
298 pub fn load(&mut self) -> crate::Result<()> {
302 let bytes = fs::read(&self.path)?;
303
304 self.cache
305 .extend((self.deserialize_fn)(&bytes).map_err(crate::Error::Deserialize)?);
306
307 Ok(())
308 }
309
310 pub fn load_ignore_defaults(&mut self) -> crate::Result<()> {
312 let bytes = fs::read(&self.path)?;
313 self.cache = (self.deserialize_fn)(&bytes).map_err(crate::Error::Deserialize)?;
314 Ok(())
315 }
316
317 pub fn set(&mut self, key: impl Into<String>, value: impl Into<JsonValue>) {
319 let key = key.into();
320 let value = value.into();
321 self.cache.insert(key.clone(), value.clone());
322 let _ = self.emit_change_event(&key, Some(&value));
323 }
324
325 pub fn get(&self, key: impl AsRef<str>) -> Option<&JsonValue> {
327 self.cache.get(key.as_ref())
328 }
329
330 pub fn has(&self, key: impl AsRef<str>) -> bool {
332 self.cache.contains_key(key.as_ref())
333 }
334
335 pub fn delete(&mut self, key: impl AsRef<str>) -> bool {
337 let flag = self.cache.remove(key.as_ref()).is_some();
338 if flag {
339 let _ = self.emit_change_event(key.as_ref(), None);
340 }
341 flag
342 }
343
344 pub fn clear(&mut self) {
348 let keys: Vec<String> = self.cache.keys().cloned().collect();
349 self.cache.clear();
350 for key in &keys {
351 let _ = self.emit_change_event(key, None);
352 }
353 }
354
355 pub fn reset(&mut self) {
359 if let Some(defaults) = &self.defaults {
360 for (key, value) in &self.cache {
361 if defaults.get(key) != Some(value) {
362 let _ = self.emit_change_event(key, defaults.get(key));
363 }
364 }
365 for (key, value) in defaults {
366 if !self.cache.contains_key(key) {
367 let _ = self.emit_change_event(key, Some(value));
368 }
369 }
370 self.cache.clone_from(defaults);
371 } else {
372 self.clear()
373 }
374 }
375
376 pub fn keys(&self) -> impl Iterator<Item = &String> {
378 self.cache.keys()
379 }
380
381 pub fn values(&self) -> impl Iterator<Item = &JsonValue> {
383 self.cache.values()
384 }
385
386 pub fn entries(&self) -> impl Iterator<Item = (&String, &JsonValue)> {
388 self.cache.iter()
389 }
390
391 pub fn len(&self) -> usize {
393 self.cache.len()
394 }
395
396 pub fn is_empty(&self) -> bool {
398 self.cache.is_empty()
399 }
400
401 fn emit_change_event(&self, key: &str, value: Option<&JsonValue>) -> crate::Result<()> {
402 let state = self.app.state::<StoreState>();
403 let stores = state.stores.lock().unwrap();
404 let exists = value.is_some();
405 self.app.emit(
406 "store://change",
407 ChangePayload {
408 path: &self.path,
409 resource_id: stores.get(&self.path).copied(),
410 key,
411 value,
412 exists,
413 },
414 )?;
415 Ok(())
416 }
417}
418
419impl<R: Runtime> std::fmt::Debug for StoreInner<R> {
420 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
421 f.debug_struct("Store")
422 .field("path", &self.path)
423 .field("cache", &self.cache)
424 .finish()
425 }
426}
427
428pub struct Store<R: Runtime> {
429 auto_save: Option<Duration>,
430 auto_save_debounce_sender: Arc<Mutex<Option<UnboundedSender<AutoSaveMessage>>>>,
431 store: Arc<Mutex<StoreInner<R>>>,
432}
433
434impl<R: Runtime> Resource for Store<R> {
435 fn close(self: Arc<Self>) {
436 let store = self.store.lock().unwrap();
437 let state = store.app.state::<StoreState>();
438 let mut stores = state.stores.lock().unwrap();
439 stores.remove(&store.path);
440 }
441}
442
443impl<R: Runtime> Store<R> {
444 pub fn set(&self, key: impl Into<String>, value: impl Into<JsonValue>) {
453 self.store.lock().unwrap().set(key.into(), value.into());
454 let _ = self.trigger_auto_save();
455 }
456
457 pub fn get(&self, key: impl AsRef<str>) -> Option<JsonValue> {
459 self.store.lock().unwrap().get(key).cloned()
460 }
461
462 pub fn has(&self, key: impl AsRef<str>) -> bool {
464 self.store.lock().unwrap().has(key)
465 }
466
467 pub fn delete(&self, key: impl AsRef<str>) -> bool {
469 let deleted = self.store.lock().unwrap().delete(key);
470 if deleted {
471 let _ = self.trigger_auto_save();
472 }
473 deleted
474 }
475
476 pub fn clear(&self) {
480 self.store.lock().unwrap().clear();
481 let _ = self.trigger_auto_save();
482 }
483
484 pub fn reset(&self) {
488 self.store.lock().unwrap().reset();
489 let _ = self.trigger_auto_save();
490 }
491
492 pub fn keys(&self) -> Vec<String> {
494 self.store.lock().unwrap().keys().cloned().collect()
495 }
496
497 pub fn values(&self) -> Vec<JsonValue> {
499 self.store.lock().unwrap().values().cloned().collect()
500 }
501
502 pub fn entries(&self) -> Vec<(String, JsonValue)> {
504 self.store
505 .lock()
506 .unwrap()
507 .entries()
508 .map(|(k, v)| (k.to_owned(), v.to_owned()))
509 .collect()
510 }
511
512 pub fn length(&self) -> usize {
514 self.store.lock().unwrap().len()
515 }
516
517 pub fn is_empty(&self) -> bool {
519 self.store.lock().unwrap().is_empty()
520 }
521
522 pub fn reload(&self) -> crate::Result<()> {
531 self.store.lock().unwrap().load()
532 }
533
534 pub fn reload_ignore_defaults(&self) -> crate::Result<()> {
538 self.store.lock().unwrap().load_ignore_defaults()
539 }
540
541 pub fn save(&self) -> crate::Result<()> {
543 if let Some(sender) = self.auto_save_debounce_sender.lock().unwrap().take() {
544 let _ = sender.send(AutoSaveMessage::Cancel);
545 }
546 self.store.lock().unwrap().save()
547 }
548
549 pub fn close_resource(&self) {
551 let store = self.store.lock().unwrap();
552 let app = store.app.clone();
553 let state = app.state::<StoreState>();
554 let stores = state.stores.lock().unwrap();
555 if let Some(rid) = stores.get(&store.path).copied() {
556 drop(store);
557 drop(stores);
558 let _ = app.resources_table().close(rid);
559 }
560 }
561
562 fn trigger_auto_save(&self) -> crate::Result<()> {
563 let Some(auto_save_delay) = self.auto_save else {
564 return Ok(());
565 };
566 if auto_save_delay.is_zero() {
567 return self.save();
568 }
569 let mut auto_save_debounce_sender = self.auto_save_debounce_sender.lock().unwrap();
570 if let Some(ref sender) = *auto_save_debounce_sender {
571 let _ = sender.send(AutoSaveMessage::Reset);
572 return Ok(());
573 }
574 let (sender, mut receiver) = unbounded_channel();
575 auto_save_debounce_sender.replace(sender);
576 drop(auto_save_debounce_sender);
577 let store = self.store.clone();
578 let auto_save_debounce_sender = self.auto_save_debounce_sender.clone();
579 tauri::async_runtime::spawn(async move {
580 loop {
581 select! {
582 should_cancel = receiver.recv() => {
583 if matches!(should_cancel, Some(AutoSaveMessage::Cancel) | None) {
584 return;
585 }
586 }
587 _ = sleep(auto_save_delay) => {
588 auto_save_debounce_sender.lock().unwrap().take();
589 let _ = store.lock().unwrap().save();
590 return;
591 }
592 };
593 }
594 });
595 Ok(())
596 }
597
598 fn apply_pending_auto_save(&self) {
599 if let Some(sender) = self.auto_save_debounce_sender.lock().unwrap().take() {
601 let _ = sender.send(AutoSaveMessage::Cancel);
602 let _ = self.save();
603 };
604 }
605}
606
607impl<R: Runtime> Drop for Store<R> {
608 fn drop(&mut self) {
609 self.apply_pending_auto_save();
610 }
611}