mirror of
https://github.com/tauri-apps/plugins-workspace.git
synced 2026-09-22 21:30:44 +02:00
refactor(store)!: reset to defaults before merging the on-disk state in reload (#3599)
* refactor(store)!: reset to defaults before merging the on-disk state in reload
`Store::reload` / `reload()` previously merged the on-disk state into the
current in-memory store. It now resets the store to its defaults first,
so in-memory keys that are neither in the defaults nor on disk are
dropped. `reload_ignore_defaults` / `reload({ ignoreDefaults: true })`
is unchanged.
* test(store): cover reload resetting to the defaults before merging the on-disk state
---------
Co-authored-by: Lucas Nogueira <lucas@crabnebula.dev>
This commit is contained in:
co-authored by
Lucas Nogueira
parent
b7897cab5d
commit
b566f09124
@@ -35,3 +35,6 @@ tracing = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
dunce = { workspace = true }
|
||||
tokio = { version = "1", features = ["sync", "time", "macros"] }
|
||||
|
||||
[dev-dependencies]
|
||||
tauri = { workspace = true, features = ["test"] }
|
||||
|
||||
@@ -405,9 +405,9 @@ interface IStore {
|
||||
* This method is useful if the on-disk state was edited by the user and you want to synchronize the changes.
|
||||
*
|
||||
* Note:
|
||||
* - This method loads the data and merges it with the current store,
|
||||
* this behavior will be changed to resetting to default first and then merging with the on-disk state in v3,
|
||||
* to fully match the store with the on-disk state, set {@linkcode ReloadOptions | ignoreDefaults} to `true`
|
||||
* - This method resets the store to its defaults and then merges the on-disk state into it,
|
||||
* so keys that are neither in the defaults nor on disk are dropped.
|
||||
* To fully match the store with the on-disk state (ignoring defaults), set {@linkcode ReloadOptions | ignoreDefaults} to `true`
|
||||
* - This method does not emit change events.
|
||||
*
|
||||
* @returns
|
||||
|
||||
@@ -300,12 +300,13 @@ impl<R: Runtime> StoreInner<R> {
|
||||
|
||||
/// Update the store from the on-disk state
|
||||
///
|
||||
/// Note: This method loads the data and merges it with the current store
|
||||
/// Note: This method resets the store to its defaults and then merges the on-disk state into it
|
||||
pub fn load(&mut self) -> crate::Result<()> {
|
||||
let bytes = fs::read(&self.path)?;
|
||||
let entries = (self.deserialize_fn)(&bytes).map_err(crate::Error::Deserialize)?;
|
||||
|
||||
self.cache
|
||||
.extend((self.deserialize_fn)(&bytes).map_err(crate::Error::Deserialize)?);
|
||||
self.cache = self.defaults.clone().unwrap_or_default();
|
||||
self.cache.extend(entries);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -525,9 +526,9 @@ impl<R: Runtime> Store<R> {
|
||||
/// Update the store from the on-disk state
|
||||
///
|
||||
/// Note:
|
||||
/// - This method loads the data and merges it with the current store,
|
||||
/// this behavior will be changed to resetting to default first and then merging with the on-disk state in v3,
|
||||
/// to fully match the store with the on-disk state,
|
||||
/// - This method resets the store to its defaults and then merges the on-disk state into it,
|
||||
/// so keys that are neither in the defaults nor on disk are dropped.
|
||||
/// To fully match the store with the on-disk state (ignoring defaults),
|
||||
/// use [`reload_ignore_defaults`](Self::reload_ignore_defaults) instead
|
||||
/// - This method does not emit change events
|
||||
pub fn reload(&self) -> crate::Result<()> {
|
||||
@@ -613,3 +614,74 @@ impl<R: Runtime> Drop for Store<R> {
|
||||
self.apply_pending_auto_save();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use serde_json::json;
|
||||
use tauri::{
|
||||
test::{mock_app, MockRuntime},
|
||||
App,
|
||||
};
|
||||
|
||||
fn temp_store_path(name: &str) -> PathBuf {
|
||||
let dir =
|
||||
std::env::temp_dir().join(format!("tauri-plugin-store-{}-{name}", std::process::id()));
|
||||
fs::create_dir_all(&dir).unwrap();
|
||||
dir.join("store.json")
|
||||
}
|
||||
|
||||
fn store(app: &App<MockRuntime>, path: PathBuf) -> StoreInner<MockRuntime> {
|
||||
let mut defaults = HashMap::new();
|
||||
defaults.insert("default-key".to_string(), json!("default"));
|
||||
defaults.insert("shared-key".to_string(), json!("default"));
|
||||
StoreInner::new(
|
||||
app.handle().clone(),
|
||||
path,
|
||||
Some(defaults),
|
||||
crate::default_serialize,
|
||||
crate::default_deserialize,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn load_resets_to_defaults_before_merging_the_on_disk_state() {
|
||||
let app = mock_app();
|
||||
let path = temp_store_path("load");
|
||||
let mut store = store(&app, path.clone());
|
||||
fs::write(&path, r#"{ "disk-key": "disk", "shared-key": "disk" }"#).unwrap();
|
||||
// neither in the defaults nor on disk, so it must be dropped
|
||||
store
|
||||
.cache
|
||||
.insert("memory-key".to_string(), json!("memory"));
|
||||
|
||||
store.load().unwrap();
|
||||
|
||||
assert_eq!(store.get("default-key"), Some(&json!("default")));
|
||||
assert_eq!(store.get("disk-key"), Some(&json!("disk")));
|
||||
// the on-disk state takes precedence over the defaults
|
||||
assert_eq!(store.get("shared-key"), Some(&json!("disk")));
|
||||
assert_eq!(store.get("memory-key"), None);
|
||||
|
||||
let _ = fs::remove_dir_all(path.parent().unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn load_ignore_defaults_matches_the_on_disk_state() {
|
||||
let app = mock_app();
|
||||
let path = temp_store_path("load-ignore-defaults");
|
||||
let mut store = store(&app, path.clone());
|
||||
fs::write(&path, r#"{ "disk-key": "disk" }"#).unwrap();
|
||||
store
|
||||
.cache
|
||||
.insert("memory-key".to_string(), json!("memory"));
|
||||
|
||||
store.load_ignore_defaults().unwrap();
|
||||
|
||||
assert_eq!(store.get("disk-key"), Some(&json!("disk")));
|
||||
assert_eq!(store.get("default-key"), None);
|
||||
assert_eq!(store.get("memory-key"), None);
|
||||
|
||||
let _ = fs::remove_dir_all(path.parent().unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user