mirror of
https://github.com/tauri-apps/plugins-workspace.git
synced 2026-09-20 21:20:42 +02:00
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:
@@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
store: minor
|
||||||
|
store-js: minor
|
||||||
|
---
|
||||||
|
|
||||||
|
Allow setting defaults from the JavaScript API
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
store: minor
|
||||||
|
store-js: minor
|
||||||
|
---
|
||||||
|
|
||||||
|
Add an new option `overrideDefaults` for creating/loading and reloading the store that overrides the store with the on-disk state, ignoring defaults
|
||||||
@@ -1,71 +1,85 @@
|
|||||||
<script>
|
<script>
|
||||||
import { LazyStore } from "@tauri-apps/plugin-store";
|
import { appDataDir, resolve } from '@tauri-apps/api/path'
|
||||||
import { onMount } from "svelte";
|
import { LazyStore } from '@tauri-apps/plugin-store'
|
||||||
|
import { onMount } from 'svelte'
|
||||||
|
|
||||||
export let onMessage;
|
let { onMessage } = $props()
|
||||||
|
|
||||||
let key;
|
let key = $state()
|
||||||
let value;
|
let value = $state()
|
||||||
|
|
||||||
let store = new LazyStore("cache.json");
|
const storeName = 'cache.json'
|
||||||
let cache = {};
|
|
||||||
|
let store = new LazyStore(storeName)
|
||||||
|
let path = $state('')
|
||||||
|
let cache = $state({})
|
||||||
|
|
||||||
async function refreshEntries() {
|
async function refreshEntries() {
|
||||||
try {
|
try {
|
||||||
const values = await store.entries();
|
const values = await store.entries()
|
||||||
cache = {};
|
cache = {}
|
||||||
for (const [key, value] of values) {
|
for (const [key, value] of values) {
|
||||||
cache[key] = value;
|
cache[key] = value
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
onMessage(error);
|
onMessage(error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
await refreshEntries();
|
path = await resolve(await appDataDir(), storeName)
|
||||||
});
|
await refreshEntries()
|
||||||
|
})
|
||||||
|
|
||||||
async function write(key, value) {
|
async function write(key, value) {
|
||||||
try {
|
try {
|
||||||
if (value) {
|
if (value) {
|
||||||
await store.set(key, value);
|
await store.set(key, value)
|
||||||
} else {
|
} else {
|
||||||
await store.delete(key);
|
await store.delete(key)
|
||||||
}
|
}
|
||||||
const v = await store.get(key);
|
const v = await store.get(key)
|
||||||
if (v === undefined) {
|
if (v === undefined) {
|
||||||
delete cache[key];
|
delete cache[key]
|
||||||
cache = cache;
|
cache = cache
|
||||||
} else {
|
} else {
|
||||||
cache[key] = v;
|
cache[key] = v
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
onMessage(error);
|
onMessage(error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function reset() {
|
async function reset() {
|
||||||
try {
|
try {
|
||||||
await store.reset();
|
await store.reset()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
onMessage(error);
|
onMessage(error)
|
||||||
}
|
}
|
||||||
await refreshEntries();
|
await refreshEntries()
|
||||||
|
}
|
||||||
|
|
||||||
|
async function reload() {
|
||||||
|
try {
|
||||||
|
await store.reload({ overrideDefaults: true })
|
||||||
|
} catch (error) {
|
||||||
|
onMessage(error)
|
||||||
|
}
|
||||||
|
await refreshEntries()
|
||||||
}
|
}
|
||||||
|
|
||||||
async function close() {
|
async function close() {
|
||||||
try {
|
try {
|
||||||
await store.close();
|
await store.close()
|
||||||
onMessage("Store is now closed, any new operations will error out");
|
onMessage('Store is now closed, any new operations will error out')
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
onMessage(error);
|
onMessage(error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function reopen() {
|
function reopen() {
|
||||||
store = new LazyStore("cache.json");
|
store = new LazyStore(storeName)
|
||||||
onMessage("We made a new `LazyStore` instance, operations will now work");
|
onMessage('We made a new `LazyStore` instance, operations will now work')
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -82,14 +96,17 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<button class="btn" on:click={() => write(key, value)}>Write</button>
|
<button class="btn" onclick={() => write(key, value)}>Write</button>
|
||||||
<button class="btn" on:click={() => reset()}>Reset</button>
|
<button class="btn" onclick={() => reset()}>Reset</button>
|
||||||
<button class="btn" on:click={() => close()}>Close</button>
|
<button class="btn" onclick={() => reload()}>Reload</button>
|
||||||
<button class="btn" on:click={() => reopen()}>Re-open</button>
|
<button class="btn" onclick={() => close()}>Close</button>
|
||||||
|
<button class="btn" onclick={() => reopen()}>Re-open</button>
|
||||||
</div>
|
</div>
|
||||||
|
<div>Store at <code>{path}</code> on disk</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
|
<h2>Store Values</h2>
|
||||||
{#each Object.entries(cache) as [k, v]}
|
{#each Object.entries(cache) as [k, v]}
|
||||||
<div>{k} = {v}</div>
|
<div>{k} = {v}</div>
|
||||||
{/each}
|
{/each}
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
if("__TAURI__"in window){var __TAURI_PLUGIN_STORE__=function(t){"use strict";var e,a;function r(t,e=!1){return window.__TAURI_INTERNALS__.transformCallback(t,e)}async function s(t,e={},a){return window.__TAURI_INTERNALS__.invoke(t,e,a)}"function"==typeof SuppressedError&&SuppressedError;class i{get rid(){return function(t,e,a,r){if("function"==typeof e?t!==e||!r:!e.has(t))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===a?r:"a"===a?r.call(t):r?r.value:e.get(t)}(this,e,"f")}constructor(t){e.set(this,void 0),function(t,e,a){if("function"==typeof e||!e.has(t))throw new TypeError("Cannot write private member to an object whose class did not declare it");e.set(t,a)}(this,e,t)}async close(){return s("plugin:resources|close",{rid:this.rid})}}async function n(t,e,a){const i={kind:"Any"};return s("plugin:event|listen",{event:t,target:i,handler:r(e)}).then((e=>async()=>async function(t,e){window.__TAURI_EVENT_PLUGIN_INTERNALS__.unregisterListener(t,e),await s("plugin:event|unlisten",{event:t,eventId:e})}(t,e)))}async function o(t,e){return await u.load(t,e)}e=new WeakMap,function(t){t.WINDOW_RESIZED="tauri://resize",t.WINDOW_MOVED="tauri://move",t.WINDOW_CLOSE_REQUESTED="tauri://close-requested",t.WINDOW_DESTROYED="tauri://destroyed",t.WINDOW_FOCUS="tauri://focus",t.WINDOW_BLUR="tauri://blur",t.WINDOW_SCALE_FACTOR_CHANGED="tauri://scale-change",t.WINDOW_THEME_CHANGED="tauri://theme-changed",t.WINDOW_CREATED="tauri://window-created",t.WEBVIEW_CREATED="tauri://webview-created",t.DRAG_ENTER="tauri://drag-enter",t.DRAG_OVER="tauri://drag-over",t.DRAG_DROP="tauri://drag-drop",t.DRAG_LEAVE="tauri://drag-leave"}(a||(a={}));class u extends i{constructor(t){super(t)}static async load(t,e){const a=await s("plugin:store|load",{path:t,...e});return new u(a)}static async get(t){return await s("plugin:store|get_store",{path:t}).then((t=>t?new u(t):null))}async set(t,e){await s("plugin:store|set",{rid:this.rid,key:t,value:e})}async get(t){const[e,a]=await s("plugin:store|get",{rid:this.rid,key:t});return a?e:void 0}async has(t){return await s("plugin:store|has",{rid:this.rid,key:t})}async delete(t){return await s("plugin:store|delete",{rid:this.rid,key:t})}async clear(){await s("plugin:store|clear",{rid:this.rid})}async reset(){await s("plugin:store|reset",{rid:this.rid})}async keys(){return await s("plugin:store|keys",{rid:this.rid})}async values(){return await s("plugin:store|values",{rid:this.rid})}async entries(){return await s("plugin:store|entries",{rid:this.rid})}async length(){return await s("plugin:store|length",{rid:this.rid})}async reload(){await s("plugin:store|reload",{rid:this.rid})}async save(){await s("plugin:store|save",{rid:this.rid})}async onKeyChange(t,e){return await n("store://change",(a=>{a.payload.resourceId===this.rid&&a.payload.key===t&&e(a.payload.exists?a.payload.value:void 0)}))}async onChange(t){return await n("store://change",(e=>{e.payload.resourceId===this.rid&&t(e.payload.key,e.payload.exists?e.payload.value:void 0)}))}}return t.LazyStore=class{get store(){return this._store||(this._store=o(this.path,this.options)),this._store}constructor(t,e){this.path=t,this.options=e}async init(){await this.store}async set(t,e){return(await this.store).set(t,e)}async get(t){return(await this.store).get(t)}async has(t){return(await this.store).has(t)}async delete(t){return(await this.store).delete(t)}async clear(){await(await this.store).clear()}async reset(){await(await this.store).reset()}async keys(){return(await this.store).keys()}async values(){return(await this.store).values()}async entries(){return(await this.store).entries()}async length(){return(await this.store).length()}async reload(){await(await this.store).reload()}async save(){await(await this.store).save()}async onKeyChange(t,e){return(await this.store).onKeyChange(t,e)}async onChange(t){return(await this.store).onChange(t)}async close(){this._store&&await(await this._store).close()}},t.Store=u,t.getStore=async function(t){return await u.get(t)},t.load=o,t}({});Object.defineProperty(window.__TAURI__,"store",{value:__TAURI_PLUGIN_STORE__})}
|
if("__TAURI__"in window){var __TAURI_PLUGIN_STORE__=function(t){"use strict";var e,a;function r(t,e=!1){return window.__TAURI_INTERNALS__.transformCallback(t,e)}async function s(t,e={},a){return window.__TAURI_INTERNALS__.invoke(t,e,a)}"function"==typeof SuppressedError&&SuppressedError;class i{get rid(){return function(t,e,a,r){if("function"==typeof e?t!==e||!r:!e.has(t))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===a?r:"a"===a?r.call(t):r?r.value:e.get(t)}(this,e,"f")}constructor(t){e.set(this,void 0),function(t,e,a){if("function"==typeof e||!e.has(t))throw new TypeError("Cannot write private member to an object whose class did not declare it");e.set(t,a)}(this,e,t)}async close(){return s("plugin:resources|close",{rid:this.rid})}}async function n(t,e,a){const i={kind:"Any"};return s("plugin:event|listen",{event:t,target:i,handler:r(e)}).then((e=>async()=>async function(t,e){window.__TAURI_EVENT_PLUGIN_INTERNALS__.unregisterListener(t,e),await s("plugin:event|unlisten",{event:t,eventId:e})}(t,e)))}async function o(t,e){return await u.load(t,e)}e=new WeakMap,function(t){t.WINDOW_RESIZED="tauri://resize",t.WINDOW_MOVED="tauri://move",t.WINDOW_CLOSE_REQUESTED="tauri://close-requested",t.WINDOW_DESTROYED="tauri://destroyed",t.WINDOW_FOCUS="tauri://focus",t.WINDOW_BLUR="tauri://blur",t.WINDOW_SCALE_FACTOR_CHANGED="tauri://scale-change",t.WINDOW_THEME_CHANGED="tauri://theme-changed",t.WINDOW_CREATED="tauri://window-created",t.WEBVIEW_CREATED="tauri://webview-created",t.DRAG_ENTER="tauri://drag-enter",t.DRAG_OVER="tauri://drag-over",t.DRAG_DROP="tauri://drag-drop",t.DRAG_LEAVE="tauri://drag-leave"}(a||(a={}));class u extends i{constructor(t){super(t)}static async load(t,e){const a=await s("plugin:store|load",{path:t,options:e});return new u(a)}static async get(t){return await s("plugin:store|get_store",{path:t}).then((t=>t?new u(t):null))}async set(t,e){await s("plugin:store|set",{rid:this.rid,key:t,value:e})}async get(t){const[e,a]=await s("plugin:store|get",{rid:this.rid,key:t});return a?e:void 0}async has(t){return await s("plugin:store|has",{rid:this.rid,key:t})}async delete(t){return await s("plugin:store|delete",{rid:this.rid,key:t})}async clear(){await s("plugin:store|clear",{rid:this.rid})}async reset(){await s("plugin:store|reset",{rid:this.rid})}async keys(){return await s("plugin:store|keys",{rid:this.rid})}async values(){return await s("plugin:store|values",{rid:this.rid})}async entries(){return await s("plugin:store|entries",{rid:this.rid})}async length(){return await s("plugin:store|length",{rid:this.rid})}async reload(t){await s("plugin:store|reload",{rid:this.rid,...t})}async save(){await s("plugin:store|save",{rid:this.rid})}async onKeyChange(t,e){return await n("store://change",(a=>{a.payload.resourceId===this.rid&&a.payload.key===t&&e(a.payload.exists?a.payload.value:void 0)}))}async onChange(t){return await n("store://change",(e=>{e.payload.resourceId===this.rid&&t(e.payload.key,e.payload.exists?e.payload.value:void 0)}))}}return t.LazyStore=class{get store(){return this._store||(this._store=o(this.path,this.options)),this._store}constructor(t,e){this.path=t,this.options=e}async init(){await this.store}async set(t,e){return(await this.store).set(t,e)}async get(t){return(await this.store).get(t)}async has(t){return(await this.store).has(t)}async delete(t){return(await this.store).delete(t)}async clear(){await(await this.store).clear()}async reset(){await(await this.store).reset()}async keys(){return(await this.store).keys()}async values(){return(await this.store).values()}async entries(){return(await this.store).entries()}async length(){return(await this.store).length()}async reload(t){await(await this.store).reload(t)}async save(){await(await this.store).save()}async onKeyChange(t,e){return(await this.store).onKeyChange(t,e)}async onChange(t){return(await this.store).onChange(t)}async close(){this._store&&await(await this._store).close()}},t.Store=u,t.getStore=async function(t){return await u.get(t)},t.load=o,t}({});Object.defineProperty(window.__TAURI__,"store",{value:__TAURI_PLUGIN_STORE__})}
|
||||||
|
|||||||
@@ -18,6 +18,10 @@ interface ChangePayload<T> {
|
|||||||
* Options to create a store
|
* Options to create a store
|
||||||
*/
|
*/
|
||||||
export type StoreOptions = {
|
export type StoreOptions = {
|
||||||
|
/**
|
||||||
|
* Default value of the store
|
||||||
|
*/
|
||||||
|
defaults: { [key: string]: unknown }
|
||||||
/**
|
/**
|
||||||
* Auto save on modification with debounce duration in milliseconds, it's 100ms by default, pass in `false` to disable it
|
* Auto save on modification with debounce duration in milliseconds, it's 100ms by default, pass in `false` to disable it
|
||||||
*/
|
*/
|
||||||
@@ -34,6 +38,10 @@ export type StoreOptions = {
|
|||||||
* Force create a new store with default values even if it already exists.
|
* Force create a new store with default values even if it already exists.
|
||||||
*/
|
*/
|
||||||
createNew?: boolean
|
createNew?: boolean
|
||||||
|
/**
|
||||||
|
* When creating the store, override the store with the on-disk state if it exists, ignoring defaults
|
||||||
|
*/
|
||||||
|
overrideDefaults?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -145,8 +153,8 @@ export class LazyStore implements IStore {
|
|||||||
return (await this.store).length()
|
return (await this.store).length()
|
||||||
}
|
}
|
||||||
|
|
||||||
async reload(): Promise<void> {
|
async reload(options?: ReloadOptions): Promise<void> {
|
||||||
await (await this.store).reload()
|
await (await this.store).reload(options)
|
||||||
}
|
}
|
||||||
|
|
||||||
async save(): Promise<void> {
|
async save(): Promise<void> {
|
||||||
@@ -196,7 +204,7 @@ export class Store extends Resource implements IStore {
|
|||||||
static async load(path: string, options?: StoreOptions): Promise<Store> {
|
static async load(path: string, options?: StoreOptions): Promise<Store> {
|
||||||
const rid = await invoke<number>('plugin:store|load', {
|
const rid = await invoke<number>('plugin:store|load', {
|
||||||
path,
|
path,
|
||||||
...options
|
options
|
||||||
})
|
})
|
||||||
return new Store(rid)
|
return new Store(rid)
|
||||||
}
|
}
|
||||||
@@ -280,8 +288,8 @@ export class Store extends Resource implements IStore {
|
|||||||
return await invoke('plugin:store|length', { rid: this.rid })
|
return await invoke('plugin:store|length', { rid: this.rid })
|
||||||
}
|
}
|
||||||
|
|
||||||
async reload(): Promise<void> {
|
async reload(options?: ReloadOptions): Promise<void> {
|
||||||
await invoke('plugin:store|reload', { rid: this.rid })
|
await invoke('plugin:store|reload', { rid: this.rid, ...options })
|
||||||
}
|
}
|
||||||
|
|
||||||
async save(): Promise<void> {
|
async save(): Promise<void> {
|
||||||
@@ -396,10 +404,15 @@ interface IStore {
|
|||||||
*
|
*
|
||||||
* This method is useful if the on-disk state was edited by the user and you want to synchronize the changes.
|
* This method is useful if the on-disk state was edited by the user and you want to synchronize the changes.
|
||||||
*
|
*
|
||||||
* Note: This method does not emit change events.
|
* 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 does not emit change events.
|
||||||
|
*
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
reload(): Promise<void>
|
reload(options?: ReloadOptions): Promise<void>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Saves the store to disk at the store's `path`.
|
* Saves the store to disk at the store's `path`.
|
||||||
@@ -437,3 +450,13 @@ interface IStore {
|
|||||||
*/
|
*/
|
||||||
close(): Promise<void>
|
close(): Promise<void>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Options to {@linkcode IStore.reload} a {@linkcode IStore}
|
||||||
|
*/
|
||||||
|
export type ReloadOptions = {
|
||||||
|
/**
|
||||||
|
* To fully match the store with the on-disk state, ignoring defaults
|
||||||
|
*/
|
||||||
|
ignoreDefaults?: boolean
|
||||||
|
}
|
||||||
|
|||||||
+43
-23
@@ -53,17 +53,36 @@ enum AutoSave {
|
|||||||
Bool(bool),
|
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>(
|
fn builder<R: Runtime>(
|
||||||
app: AppHandle<R>,
|
app: AppHandle<R>,
|
||||||
store_state: State<'_, StoreState>,
|
store_state: State<'_, StoreState>,
|
||||||
path: PathBuf,
|
path: PathBuf,
|
||||||
auto_save: Option<AutoSave>,
|
options: Option<LoadStoreOptions>,
|
||||||
serialize_fn_name: Option<String>,
|
|
||||||
deserialize_fn_name: Option<String>,
|
|
||||||
create_new: bool,
|
|
||||||
) -> Result<StoreBuilder<R>> {
|
) -> Result<StoreBuilder<R>> {
|
||||||
let mut builder = app.store_builder(path);
|
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 {
|
match auto_save {
|
||||||
AutoSave::DebounceDuration(duration) => {
|
AutoSave::DebounceDuration(duration) => {
|
||||||
builder = builder.auto_save(Duration::from_millis(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
|
let serialize_fn = store_state
|
||||||
.serialize_fns
|
.serialize_fns
|
||||||
.get(&serialize_fn_name)
|
.get(&serialize_fn_name)
|
||||||
@@ -83,7 +102,7 @@ fn builder<R: Runtime>(
|
|||||||
builder = builder.serialize(*serialize_fn);
|
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
|
let deserialize_fn = store_state
|
||||||
.deserialize_fns
|
.deserialize_fns
|
||||||
.get(&deserialize_fn_name)
|
.get(&deserialize_fn_name)
|
||||||
@@ -91,10 +110,14 @@ fn builder<R: Runtime>(
|
|||||||
builder = builder.deserialize(*deserialize_fn);
|
builder = builder.deserialize(*deserialize_fn);
|
||||||
}
|
}
|
||||||
|
|
||||||
if create_new {
|
if options.create_new {
|
||||||
builder = builder.create_new();
|
builder = builder.create_new();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if options.override_defaults {
|
||||||
|
builder = builder.override_defaults();
|
||||||
|
}
|
||||||
|
|
||||||
Ok(builder)
|
Ok(builder)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -103,20 +126,9 @@ async fn load<R: Runtime>(
|
|||||||
app: AppHandle<R>,
|
app: AppHandle<R>,
|
||||||
store_state: State<'_, StoreState>,
|
store_state: State<'_, StoreState>,
|
||||||
path: PathBuf,
|
path: PathBuf,
|
||||||
auto_save: Option<AutoSave>,
|
options: Option<LoadStoreOptions>,
|
||||||
serialize_fn_name: Option<String>,
|
|
||||||
deserialize_fn_name: Option<String>,
|
|
||||||
create_new: Option<bool>,
|
|
||||||
) -> Result<ResourceId> {
|
) -> Result<ResourceId> {
|
||||||
let builder = builder(
|
let builder = builder(app, store_state, path, options)?;
|
||||||
app,
|
|
||||||
store_state,
|
|
||||||
path,
|
|
||||||
auto_save,
|
|
||||||
serialize_fn_name,
|
|
||||||
deserialize_fn_name,
|
|
||||||
create_new.unwrap_or_default(),
|
|
||||||
)?;
|
|
||||||
let (_, rid) = builder.build_inner()?;
|
let (_, rid) = builder.build_inner()?;
|
||||||
Ok(rid)
|
Ok(rid)
|
||||||
}
|
}
|
||||||
@@ -209,9 +221,17 @@ async fn length<R: Runtime>(app: AppHandle<R>, rid: ResourceId) -> Result<usize>
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[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)?;
|
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]
|
#[tauri::command]
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ pub struct StoreBuilder<R: Runtime> {
|
|||||||
deserialize_fn: DeserializeFn,
|
deserialize_fn: DeserializeFn,
|
||||||
auto_save: Option<Duration>,
|
auto_save: Option<Duration>,
|
||||||
create_new: bool,
|
create_new: bool,
|
||||||
|
override_defaults: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R: Runtime> StoreBuilder<R> {
|
impl<R: Runtime> StoreBuilder<R> {
|
||||||
@@ -66,6 +67,7 @@ impl<R: Runtime> StoreBuilder<R> {
|
|||||||
deserialize_fn,
|
deserialize_fn,
|
||||||
auto_save: Some(Duration::from_millis(100)),
|
auto_save: Some(Duration::from_millis(100)),
|
||||||
create_new: false,
|
create_new: false,
|
||||||
|
override_defaults: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -178,6 +180,12 @@ impl<R: Runtime> StoreBuilder<R> {
|
|||||||
self
|
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)> {
|
pub(crate) fn build_inner(mut self) -> crate::Result<(Arc<Store<R>>, ResourceId)> {
|
||||||
let stores = self.app.state::<StoreState>().stores.clone();
|
let stores = self.app.state::<StoreState>().stores.clone();
|
||||||
let mut stores = stores.lock().unwrap();
|
let mut stores = stores.lock().unwrap();
|
||||||
@@ -205,7 +213,11 @@ impl<R: Runtime> StoreBuilder<R> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if !self.create_new {
|
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 {
|
let store = Store {
|
||||||
@@ -284,6 +296,8 @@ impl<R: Runtime> StoreInner<R> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Update the store from the on-disk state
|
/// 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<()> {
|
pub fn load(&mut self) -> crate::Result<()> {
|
||||||
let bytes = fs::read(&self.path)?;
|
let bytes = fs::read(&self.path)?;
|
||||||
|
|
||||||
@@ -293,6 +307,13 @@ impl<R: Runtime> StoreInner<R> {
|
|||||||
Ok(())
|
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.
|
/// Inserts a key-value pair into the store.
|
||||||
pub fn set(&mut self, key: impl Into<String>, value: impl Into<JsonValue>) {
|
pub fn set(&mut self, key: impl Into<String>, value: impl Into<JsonValue>) {
|
||||||
let key = key.into();
|
let key = key.into();
|
||||||
@@ -499,10 +520,24 @@ impl<R: Runtime> Store<R> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Update the store from the on-disk state
|
/// 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<()> {
|
pub fn reload(&self) -> crate::Result<()> {
|
||||||
self.store.lock().unwrap().load()
|
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`.
|
/// Saves the store to disk at the store's `path`.
|
||||||
pub fn save(&self) -> crate::Result<()> {
|
pub fn save(&self) -> crate::Result<()> {
|
||||||
if let Some(sender) = self.auto_save_debounce_sender.lock().unwrap().take() {
|
if let Some(sender) = self.auto_save_debounce_sender.lock().unwrap().take() {
|
||||||
|
|||||||
Reference in New Issue
Block a user