feat(store): load override defaults (#2857)

* feat(store): load override defaults

* Update docs

* Update example

* Allow setting defaults from js

* Tweak resolve

* Merge remote-tracking branch 'upstream/v2' into store-load-override-defaults

* Merge branch 'v2' of https://github.com/tauri-apps/plugins-workspace into store-load-override-defaults

* Merge branch 'v2' into store-load-override-defaults

* Rename to ignore defaults

* Merge remote-tracking branch 'upstream/v2' into store-load-override-defaults
This commit is contained in:
Tony
2025-08-05 18:45:09 +08:00
committed by GitHub
parent e0323ec752
commit 5ac8fbb1fa
7 changed files with 171 additions and 64 deletions
+43 -23
View File
@@ -53,17 +53,36 @@ enum AutoSave {
Bool(bool),
}
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
struct LoadStoreOptions {
defaults: Option<HashMap<String, JsonValue>>,
auto_save: Option<AutoSave>,
serialize_fn_name: Option<String>,
deserialize_fn_name: Option<String>,
#[serde(default)]
create_new: bool,
#[serde(default)]
override_defaults: bool,
}
fn builder<R: Runtime>(
app: AppHandle<R>,
store_state: State<'_, StoreState>,
path: PathBuf,
auto_save: Option<AutoSave>,
serialize_fn_name: Option<String>,
deserialize_fn_name: Option<String>,
create_new: bool,
options: Option<LoadStoreOptions>,
) -> Result<StoreBuilder<R>> {
let mut builder = app.store_builder(path);
if let Some(auto_save) = auto_save {
let Some(options) = options else {
return Ok(builder);
};
if let Some(defaults) = options.defaults {
builder = builder.defaults(defaults);
}
if let Some(auto_save) = options.auto_save {
match auto_save {
AutoSave::DebounceDuration(duration) => {
builder = builder.auto_save(Duration::from_millis(duration));
@@ -75,7 +94,7 @@ fn builder<R: Runtime>(
}
}
if let Some(serialize_fn_name) = serialize_fn_name {
if let Some(serialize_fn_name) = options.serialize_fn_name {
let serialize_fn = store_state
.serialize_fns
.get(&serialize_fn_name)
@@ -83,7 +102,7 @@ fn builder<R: Runtime>(
builder = builder.serialize(*serialize_fn);
}
if let Some(deserialize_fn_name) = deserialize_fn_name {
if let Some(deserialize_fn_name) = options.deserialize_fn_name {
let deserialize_fn = store_state
.deserialize_fns
.get(&deserialize_fn_name)
@@ -91,10 +110,14 @@ fn builder<R: Runtime>(
builder = builder.deserialize(*deserialize_fn);
}
if create_new {
if options.create_new {
builder = builder.create_new();
}
if options.override_defaults {
builder = builder.override_defaults();
}
Ok(builder)
}
@@ -103,20 +126,9 @@ async fn load<R: Runtime>(
app: AppHandle<R>,
store_state: State<'_, StoreState>,
path: PathBuf,
auto_save: Option<AutoSave>,
serialize_fn_name: Option<String>,
deserialize_fn_name: Option<String>,
create_new: Option<bool>,
options: Option<LoadStoreOptions>,
) -> Result<ResourceId> {
let builder = builder(
app,
store_state,
path,
auto_save,
serialize_fn_name,
deserialize_fn_name,
create_new.unwrap_or_default(),
)?;
let builder = builder(app, store_state, path, options)?;
let (_, rid) = builder.build_inner()?;
Ok(rid)
}
@@ -209,9 +221,17 @@ async fn length<R: Runtime>(app: AppHandle<R>, rid: ResourceId) -> Result<usize>
}
#[tauri::command]
async fn reload<R: Runtime>(app: AppHandle<R>, rid: ResourceId) -> Result<()> {
async fn reload<R: Runtime>(
app: AppHandle<R>,
rid: ResourceId,
ignore_defaults: Option<bool>,
) -> Result<()> {
let store = app.resources_table().get::<Store<R>>(rid)?;
store.reload()
if ignore_defaults.unwrap_or_default() {
store.reload_ignore_defaults()
} else {
store.reload()
}
}
#[tauri::command]
+36 -1
View File
@@ -39,6 +39,7 @@ pub struct StoreBuilder<R: Runtime> {
deserialize_fn: DeserializeFn,
auto_save: Option<Duration>,
create_new: bool,
override_defaults: bool,
}
impl<R: Runtime> StoreBuilder<R> {
@@ -66,6 +67,7 @@ impl<R: Runtime> StoreBuilder<R> {
deserialize_fn,
auto_save: Some(Duration::from_millis(100)),
create_new: false,
override_defaults: false,
}
}
@@ -178,6 +180,12 @@ impl<R: Runtime> StoreBuilder<R> {
self
}
/// Override the store values when creating the store, ignoring defaults.
pub fn override_defaults(mut self) -> Self {
self.override_defaults = true;
self
}
pub(crate) fn build_inner(mut self) -> crate::Result<(Arc<Store<R>>, ResourceId)> {
let stores = self.app.state::<StoreState>().stores.clone();
let mut stores = stores.lock().unwrap();
@@ -205,7 +213,11 @@ impl<R: Runtime> StoreBuilder<R> {
);
if !self.create_new {
let _ = store_inner.load();
if self.override_defaults {
let _ = store_inner.load_ignore_defaults();
} else {
let _ = store_inner.load();
}
}
let store = Store {
@@ -284,6 +296,8 @@ 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
pub fn load(&mut self) -> crate::Result<()> {
let bytes = fs::read(&self.path)?;
@@ -293,6 +307,13 @@ impl<R: Runtime> StoreInner<R> {
Ok(())
}
/// Load the store from the on-disk state, ignoring defaults
pub fn load_ignore_defaults(&mut self) -> crate::Result<()> {
let bytes = fs::read(&self.path)?;
self.cache = (self.deserialize_fn)(&bytes).map_err(crate::Error::Deserialize)?;
Ok(())
}
/// Inserts a key-value pair into the store.
pub fn set(&mut self, key: impl Into<String>, value: impl Into<JsonValue>) {
let key = key.into();
@@ -499,10 +520,24 @@ 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,
/// use [`reload_override_defaults`](Self::reload_override_defaults) instead
/// - This method does not emit change events
pub fn reload(&self) -> crate::Result<()> {
self.store.lock().unwrap().load()
}
/// Load the store from the on-disk state, ignoring defaults
///
/// Note: This method does not emit change events
pub fn reload_ignore_defaults(&self) -> crate::Result<()> {
self.store.lock().unwrap().load_ignore_defaults()
}
/// Saves the store to disk at the store's `path`.
pub fn save(&self) -> crate::Result<()> {
if let Some(sender) = self.auto_save_debounce_sender.lock().unwrap().take() {