Skip to main content

tauri_plugin_store/
store.rs

1// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
2// SPDX-License-Identifier: Apache-2.0
3// SPDX-License-Identifier: MIT
4
5use 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
21/// Function used to serialize the store cache to the bytes written to the store file.
22///
23/// The default implementation writes pretty printed JSON.
24pub type SerializeFn =
25    fn(&HashMap<String, JsonValue>) -> Result<Vec<u8>, Box<dyn std::error::Error + Send + Sync>>;
26/// Function used to deserialize the bytes read from the store file into the store cache.
27///
28/// The default implementation parses JSON.
29pub type DeserializeFn =
30    fn(&[u8]) -> Result<HashMap<String, JsonValue>, Box<dyn std::error::Error + Send + Sync>>;
31
32/// Resolves the path of a store file, relative to the app data directory
33/// ([`BaseDirectory::AppData`]).
34///
35/// This is the path the [`Store`] created with the given `path` reads from and writes to.
36///
37/// # Errors
38///
39/// Returns an error if the app data directory cannot be resolved.
40pub 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
47/// Builds a [`Store`]
48pub 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    /// Creates a new [`StoreBuilder`].
61    ///
62    /// # Examples
63    /// ```
64    /// tauri::Builder::default()
65    ///   .plugin(tauri_plugin_store::Builder::default().build())
66    ///   .setup(|app| {
67    ///     let builder = tauri_plugin_store::StoreBuilder::new(app, "store.bin");
68    ///     Ok(())
69    ///   });
70    /// ```
71    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    /// Inserts a default key-value pair.
89    ///
90    /// # Examples
91    /// ```
92    /// tauri::Builder::default()
93    ///   .plugin(tauri_plugin_store::Builder::default().build())
94    ///   .setup(|app| {
95    ///     let mut defaults = std::collections::HashMap::new();
96    ///     defaults.insert("foo".to_string(), "bar".into());
97    ///
98    ///     let store = tauri_plugin_store::StoreBuilder::new(app, "store.bin")
99    ///       .defaults(defaults)
100    ///       .build()?;
101    ///     Ok(())
102    ///   });
103    /// ```
104    pub fn defaults(mut self, defaults: HashMap<String, JsonValue>) -> Self {
105        self.defaults = Some(defaults);
106        self
107    }
108
109    /// Inserts multiple default key-value pairs.
110    ///
111    /// # Examples
112    /// ```
113    /// tauri::Builder::default()
114    ///   .plugin(tauri_plugin_store::Builder::default().build())
115    ///   .setup(|app| {
116    ///     let store = tauri_plugin_store::StoreBuilder::new(app, "store.bin")
117    ///       .default("foo".to_string(), "bar")
118    ///       .build()?;
119    ///     Ok(())
120    ///   });
121    /// ```
122    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    /// Defines a custom serialization function.
132    ///
133    /// # Examples
134    /// ```
135    /// tauri::Builder::default()
136    ///   .plugin(tauri_plugin_store::Builder::default().build())
137    ///   .setup(|app| {
138    ///     let store = tauri_plugin_store::StoreBuilder::new(app, "store.json")
139    ///       .serialize(|cache| serde_json::to_vec(&cache).map_err(Into::into))
140    ///       .build()?;
141    ///     Ok(())
142    ///   });
143    /// ```
144    pub fn serialize(mut self, serialize: SerializeFn) -> Self {
145        self.serialize_fn = serialize;
146        self
147    }
148
149    /// Defines a custom deserialization function
150    ///
151    /// # Examples
152    /// ```
153    /// tauri::Builder::default()
154    ///   .plugin(tauri_plugin_store::Builder::default().build())
155    ///   .setup(|app| {
156    ///     let store = tauri_plugin_store::StoreBuilder::new(app, "store.json")
157    ///       .deserialize(|bytes| serde_json::from_slice(&bytes).map_err(Into::into))
158    ///       .build()?;
159    ///     Ok(())
160    ///   });
161    /// ```
162    pub fn deserialize(mut self, deserialize: DeserializeFn) -> Self {
163        self.deserialize_fn = deserialize;
164        self
165    }
166
167    /// Auto save on modified with a debounce duration
168    ///
169    /// # Examples
170    /// ```
171    /// tauri::Builder::default()
172    ///    .plugin(tauri_plugin_store::Builder::default().build())
173    ///   .setup(|app| {
174    ///     let store = tauri_plugin_store::StoreBuilder::new(app, "store.json")
175    ///         .auto_save(std::time::Duration::from_millis(100))
176    ///         .build()?;
177    ///     Ok(())
178    ///   });
179    /// ```
180    pub fn auto_save(mut self, debounce_duration: Duration) -> Self {
181        self.auto_save = Some(debounce_duration);
182        self
183    }
184
185    /// Disable auto save on modified with a debounce duration.
186    pub fn disable_auto_save(mut self) -> Self {
187        self.auto_save = None;
188        self
189    }
190
191    /// Force create a new store with default values even if it already exists.
192    pub fn create_new(mut self) -> Self {
193        self.create_new = true;
194        self
195    }
196
197    /// Override the store values when creating the store, ignoring defaults.
198    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            // The resource id we stored can be invalid due to
215            // the resource table getting modified by an external source
216            // (e.g. `App::cleanup_before_exit` > `manager.resources_table.clear()`)
217            return Ok((self.app.resources_table().get(*rid)?, *rid));
218        }
219
220        // if stores.contains_key(&self.path) {
221        //     return Err(crate::Error::AlreadyExists(self.path));
222        // }
223
224        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    /// Load the existing store with the same path or creates a new [`Store`].
254    ///
255    /// If a store with the same path has already been loaded its instance is returned.
256    ///
257    /// # Examples
258    /// ```
259    /// tauri::Builder::default()
260    ///   .plugin(tauri_plugin_store::Builder::default().build())
261    ///   .setup(|app| {
262    ///     let store = tauri_plugin_store::StoreBuilder::new(app, "store.json").build();
263    ///     Ok(())
264    ///   });
265    /// ```
266    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    /// Saves the store to disk at the store's `path`.
306    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    /// Update the store from the on-disk state
316    ///
317    /// Note: This method resets the store to its defaults and then merges the on-disk state into it
318    pub fn load(&mut self) -> crate::Result<()> {
319        let bytes = fs::read(&self.path)?;
320        let entries = (self.deserialize_fn)(&bytes).map_err(crate::Error::Deserialize)?;
321
322        self.cache = self.defaults.clone().unwrap_or_default();
323        self.cache.extend(entries);
324
325        Ok(())
326    }
327
328    /// Load the store from the on-disk state, ignoring defaults
329    pub fn load_ignore_defaults(&mut self) -> crate::Result<()> {
330        let bytes = fs::read(&self.path)?;
331        self.cache = (self.deserialize_fn)(&bytes).map_err(crate::Error::Deserialize)?;
332        Ok(())
333    }
334
335    /// Inserts a key-value pair into the store.
336    pub fn set(&mut self, key: impl Into<String>, value: impl Into<JsonValue>) {
337        let key = key.into();
338        let value = value.into();
339        self.cache.insert(key.clone(), value.clone());
340        let _ = self.emit_change_event(&key, Some(&value));
341    }
342
343    /// Returns a reference to the value corresponding to the key.
344    pub fn get(&self, key: impl AsRef<str>) -> Option<&JsonValue> {
345        self.cache.get(key.as_ref())
346    }
347
348    /// Returns `true` if the given `key` exists in the store.
349    pub fn has(&self, key: impl AsRef<str>) -> bool {
350        self.cache.contains_key(key.as_ref())
351    }
352
353    /// Removes a key-value pair from the store.
354    pub fn delete(&mut self, key: impl AsRef<str>) -> bool {
355        let flag = self.cache.remove(key.as_ref()).is_some();
356        if flag {
357            let _ = self.emit_change_event(key.as_ref(), None);
358        }
359        flag
360    }
361
362    /// Clears the store, removing all key-value pairs.
363    ///
364    /// Note: To clear the storage and reset it to its `default` value, use [`reset`](Self::reset) instead.
365    pub fn clear(&mut self) {
366        let keys: Vec<String> = self.cache.keys().cloned().collect();
367        self.cache.clear();
368        for key in &keys {
369            let _ = self.emit_change_event(key, None);
370        }
371    }
372
373    /// Resets the store to its `default` value.
374    ///
375    /// If no default value has been set, this method behaves identical to [`clear`](Self::clear).
376    pub fn reset(&mut self) {
377        if let Some(defaults) = &self.defaults {
378            for (key, value) in &self.cache {
379                if defaults.get(key) != Some(value) {
380                    let _ = self.emit_change_event(key, defaults.get(key));
381                }
382            }
383            for (key, value) in defaults {
384                if !self.cache.contains_key(key) {
385                    let _ = self.emit_change_event(key, Some(value));
386                }
387            }
388            self.cache.clone_from(defaults);
389        } else {
390            self.clear()
391        }
392    }
393
394    /// An iterator visiting all keys in arbitrary order.
395    pub fn keys(&self) -> impl Iterator<Item = &String> {
396        self.cache.keys()
397    }
398
399    /// An iterator visiting all values in arbitrary order.
400    pub fn values(&self) -> impl Iterator<Item = &JsonValue> {
401        self.cache.values()
402    }
403
404    /// An iterator visiting all key-value pairs in arbitrary order.
405    pub fn entries(&self) -> impl Iterator<Item = (&String, &JsonValue)> {
406        self.cache.iter()
407    }
408
409    /// Returns the number of elements in the store.
410    pub fn len(&self) -> usize {
411        self.cache.len()
412    }
413
414    /// Returns true if the store contains no elements.
415    pub fn is_empty(&self) -> bool {
416        self.cache.is_empty()
417    }
418
419    fn emit_change_event(&self, key: &str, value: Option<&JsonValue>) -> crate::Result<()> {
420        let state = self.app.state::<StoreState>();
421        let stores = state.stores.read().unwrap();
422        let exists = value.is_some();
423        self.app.emit(
424            "store://change",
425            ChangePayload {
426                path: &self.path,
427                resource_id: stores.get(&self.path).copied(),
428                key,
429                value,
430                exists,
431            },
432        )?;
433        Ok(())
434    }
435}
436
437impl<R: Runtime> std::fmt::Debug for StoreInner<R> {
438    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
439        f.debug_struct("Store")
440            .field("path", &self.path)
441            .field("cache", &self.cache)
442            .finish()
443    }
444}
445
446/// A key-value store, persisted to a file resolved with [`resolve_store_path`].
447///
448/// The values are kept in memory and written to disk on [`Store::save`], and also automatically
449/// after each modification unless auto save has been disabled with
450/// [`StoreBuilder::disable_auto_save`]. Any pending auto save is applied when the store is dropped.
451///
452/// Create or load one with [`StoreExt::store`](crate::StoreExt::store) or [`StoreBuilder`].
453/// It is a [`Resource`], so it is also reachable from the frontend by its [`ResourceId`];
454/// closing that resource unregisters the store, meaning the next load creates a new instance.
455pub struct Store<R: Runtime> {
456    auto_save: Option<Duration>,
457    auto_save_debounce_sender: Arc<Mutex<Option<UnboundedSender<AutoSaveMessage>>>>,
458    store: Arc<Mutex<StoreInner<R>>>,
459}
460
461impl<R: Runtime> Resource for Store<R> {
462    fn close(self: Arc<Self>) {
463        let store = self.store.lock().unwrap();
464        let state = store.app.state::<StoreState>();
465        let mut stores = state.stores.write().unwrap();
466        stores.remove(&store.path);
467    }
468}
469
470impl<R: Runtime> Store<R> {
471    // /// Do something with the inner store,
472    // /// useful for batching some work if you need higher performance
473    // pub fn with_store<T>(&self, f: impl FnOnce(&mut StoreInner<R>) -> T) -> T {
474    //     let mut store = self.store.lock().unwrap();
475    //     f(&mut store)
476    // }
477
478    /// Inserts a key-value pair into the store.
479    pub fn set(&self, key: impl Into<String>, value: impl Into<JsonValue>) {
480        self.store.lock().unwrap().set(key.into(), value.into());
481        let _ = self.trigger_auto_save();
482    }
483
484    /// Returns the value for the given `key` or `None` if the key does not exist.
485    pub fn get(&self, key: impl AsRef<str>) -> Option<JsonValue> {
486        self.store.lock().unwrap().get(key).cloned()
487    }
488
489    /// Returns `true` if the given `key` exists in the store.
490    pub fn has(&self, key: impl AsRef<str>) -> bool {
491        self.store.lock().unwrap().has(key)
492    }
493
494    /// Removes a key-value pair from the store.
495    pub fn delete(&self, key: impl AsRef<str>) -> bool {
496        let deleted = self.store.lock().unwrap().delete(key);
497        if deleted {
498            let _ = self.trigger_auto_save();
499        }
500        deleted
501    }
502
503    /// Clears the store, removing all key-value pairs.
504    ///
505    /// Note: To clear the storage and reset it to its `default` value, use [`reset`](Self::reset) instead.
506    pub fn clear(&self) {
507        self.store.lock().unwrap().clear();
508        let _ = self.trigger_auto_save();
509    }
510
511    /// Resets the store to its `default` value.
512    ///
513    /// If no default value has been set, this method behaves identical to [`clear`](Self::clear).
514    pub fn reset(&self) {
515        self.store.lock().unwrap().reset();
516        let _ = self.trigger_auto_save();
517    }
518
519    /// Returns a list of all keys in the store.
520    pub fn keys(&self) -> Vec<String> {
521        self.store.lock().unwrap().keys().cloned().collect()
522    }
523
524    /// Returns a list of all values in the store.
525    pub fn values(&self) -> Vec<JsonValue> {
526        self.store.lock().unwrap().values().cloned().collect()
527    }
528
529    /// Returns a list of all key-value pairs in the store.
530    pub fn entries(&self) -> Vec<(String, JsonValue)> {
531        self.store
532            .lock()
533            .unwrap()
534            .entries()
535            .map(|(k, v)| (k.to_owned(), v.to_owned()))
536            .collect()
537    }
538
539    /// Returns the number of elements in the store.
540    pub fn length(&self) -> usize {
541        self.store.lock().unwrap().len()
542    }
543
544    /// Returns true if the store contains no elements.
545    pub fn is_empty(&self) -> bool {
546        self.store.lock().unwrap().is_empty()
547    }
548
549    /// Update the store from the on-disk state
550    ///
551    /// Note:
552    ///   - This method resets the store to its defaults and then merges the on-disk state into it,
553    ///     so keys that are neither in the defaults nor on disk are dropped.
554    ///     To fully match the store with the on-disk state (ignoring defaults),
555    ///     use [`reload_ignore_defaults`](Self::reload_ignore_defaults) instead
556    ///   - This method does not emit change events
557    pub fn reload(&self) -> crate::Result<()> {
558        self.store.lock().unwrap().load()
559    }
560
561    /// Load the store from the on-disk state, ignoring defaults
562    ///
563    /// Note: This method does not emit change events
564    pub fn reload_ignore_defaults(&self) -> crate::Result<()> {
565        self.store.lock().unwrap().load_ignore_defaults()
566    }
567
568    /// Saves the store to disk at the store's `path`.
569    pub fn save(&self) -> crate::Result<()> {
570        if let Some(sender) = self.auto_save_debounce_sender.lock().unwrap().take() {
571            let _ = sender.send(AutoSaveMessage::Cancel);
572        }
573        self.store.lock().unwrap().save()
574    }
575
576    /// Removes the store from the resource table
577    pub fn close_resource(&self) {
578        let store = self.store.lock().unwrap();
579        let app = store.app.clone();
580        let state = app.state::<StoreState>();
581        let stores = state.stores.read().unwrap();
582        if let Some(rid) = stores.get(&store.path).copied() {
583            drop(store);
584            drop(stores);
585            let _ = app.resources_table().close(rid);
586        }
587    }
588
589    fn trigger_auto_save(&self) -> crate::Result<()> {
590        let Some(auto_save_delay) = self.auto_save else {
591            return Ok(());
592        };
593        if auto_save_delay.is_zero() {
594            return self.save();
595        }
596        let mut auto_save_debounce_sender = self.auto_save_debounce_sender.lock().unwrap();
597        if let Some(ref sender) = *auto_save_debounce_sender {
598            let _ = sender.send(AutoSaveMessage::Reset);
599            return Ok(());
600        }
601        let (sender, mut receiver) = unbounded_channel();
602        auto_save_debounce_sender.replace(sender);
603        drop(auto_save_debounce_sender);
604        let store = self.store.clone();
605        let auto_save_debounce_sender = self.auto_save_debounce_sender.clone();
606        tauri::async_runtime::spawn(async move {
607            loop {
608                select! {
609                    should_cancel = receiver.recv() => {
610                        if matches!(should_cancel, Some(AutoSaveMessage::Cancel) | None) {
611                            return;
612                        }
613                    }
614                    _ = sleep(auto_save_delay) => {
615                        auto_save_debounce_sender.lock().unwrap().take();
616                        let _ = store.lock().unwrap().save();
617                        return;
618                    }
619                };
620            }
621        });
622        Ok(())
623    }
624
625    fn apply_pending_auto_save(&self) {
626        // Cancel and save if auto save is pending
627        let auto_save_debounce_sender = self.auto_save_debounce_sender.lock().unwrap().take();
628        if let Some(sender) = auto_save_debounce_sender {
629            let _ = sender.send(AutoSaveMessage::Cancel);
630            let _ = self.save();
631        };
632    }
633}
634
635impl<R: Runtime> Drop for Store<R> {
636    fn drop(&mut self) {
637        self.apply_pending_auto_save();
638    }
639}
640
641#[cfg(test)]
642mod tests {
643    use super::*;
644    use serde_json::json;
645    use tauri::{
646        App,
647        test::{MockRuntime, mock_app},
648    };
649
650    fn temp_store_path(name: &str) -> PathBuf {
651        let dir =
652            std::env::temp_dir().join(format!("tauri-plugin-store-{}-{name}", std::process::id()));
653        fs::create_dir_all(&dir).unwrap();
654        dir.join("store.json")
655    }
656
657    fn store(app: &App<MockRuntime>, path: PathBuf) -> StoreInner<MockRuntime> {
658        let mut defaults = HashMap::new();
659        defaults.insert("default-key".to_string(), json!("default"));
660        defaults.insert("shared-key".to_string(), json!("default"));
661        StoreInner::new(
662            app.handle().clone(),
663            path,
664            Some(defaults),
665            crate::default_serialize,
666            crate::default_deserialize,
667        )
668    }
669
670    #[test]
671    fn load_resets_to_defaults_before_merging_the_on_disk_state() {
672        let app = mock_app();
673        let path = temp_store_path("load");
674        let mut store = store(&app, path.clone());
675        fs::write(&path, r#"{ "disk-key": "disk", "shared-key": "disk" }"#).unwrap();
676        // neither in the defaults nor on disk, so it must be dropped
677        store
678            .cache
679            .insert("memory-key".to_string(), json!("memory"));
680
681        store.load().unwrap();
682
683        assert_eq!(store.get("default-key"), Some(&json!("default")));
684        assert_eq!(store.get("disk-key"), Some(&json!("disk")));
685        // the on-disk state takes precedence over the defaults
686        assert_eq!(store.get("shared-key"), Some(&json!("disk")));
687        assert_eq!(store.get("memory-key"), None);
688
689        let _ = fs::remove_dir_all(path.parent().unwrap());
690    }
691
692    #[test]
693    fn load_ignore_defaults_matches_the_on_disk_state() {
694        let app = mock_app();
695        let path = temp_store_path("load-ignore-defaults");
696        let mut store = store(&app, path.clone());
697        fs::write(&path, r#"{ "disk-key": "disk" }"#).unwrap();
698        store
699            .cache
700            .insert("memory-key".to_string(), json!("memory"));
701
702        store.load_ignore_defaults().unwrap();
703
704        assert_eq!(store.get("disk-key"), Some(&json!("disk")));
705        assert_eq!(store.get("default-key"), None);
706        assert_eq!(store.get("memory-key"), None);
707
708        let _ = fs::remove_dir_all(path.parent().unwrap());
709    }
710}