Skip to content

Commit 8c67d44

Browse files
Legend-Masteramrbashirlucasfernog
authored
refactor(store)!: more reworks (#1860)
* refactor(store): more reworks * Enable auto save by default * Store to resource table by default * Remove share store * Clean up * Add close store * Add store function * Add lazy store * Add init to lazy store * refresh cache in example * Add get-or-create-store * Revert "Add get-or-create-store" This reverts commit 7ffd769. * try get first * Docs * Use absolute path for store * more docs * Allow js to use pre-stored (de)serialize functions * Fix js get and close store * Show case how to use pretty json * Update readme * Use store instead of `store_builder` in example * Build * Fix example * More docs for StoreBuilder::build * Add default (de)serialize fn * Use pretty json by default * Use `undefined` for empty value in get * Change files * Differentiate json null from no value for events * Add create or existing * Build * Rename inner store's inset method to set * Update readme * Apply suggestions from code review * Use close instead * Update breaking changes * Return result in resolve_store_path * Change to close_resource and take &self * Clean up * Apply suggestions from code review Co-authored-by: Amr Bashir <[email protected]> * Remove unused pub(crate) * Update change file * Expose resolve_store_path * Remove with_store * Remove StoreInner from pub and expose is_empty * Fix wrong jsdoc param * Update readme * rename createOrExistingStore to createOrLoad * make api consistent with the JS implementation, add examples * fmt * reintroduce "get existing store" behavior for create_or_load * rename createOrLoad to newOrExisting * keep store lock throughout whole new_or_existing_inner * Remove load * Missed if load * Don't make StoreState public * Remove R: Runtime from Builder * rename newOrExisting to load, load to reload * update docs * rename missing reload fn * rename builder fn to build() * fix default permission * Fix description and create_new logic * Clippy * Update docs * Update docs * remove create_store command * remove close_store command since we extend from Resource * Revert "remove close_store command since we extend from Resource" This reverts commit 4a29fc8. * Reapply "remove close_store command since we extend from Resource" This reverts commit 70a1830. --------- Co-authored-by: Amr Bashir <[email protected]> Co-authored-by: Lucas Nogueira <[email protected]>
1 parent cfd48b3 commit 8c67d44

File tree

20 files changed

+1058
-507
lines changed

20 files changed

+1058
-507
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
"store-js": minor:feat
3+
---
4+
5+
- Add `getStore`
6+
- Add an option to use pre-stored (de)serialize functions (registered on rust)
7+
- Add `LazyStore`

.changes/store-plugin-rework.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
---
2+
"store": minor:breaking
3+
---
4+
5+
### Breaking changes:
6+
7+
- Renamed `StoreCollection` to `StoreState`
8+
- `StoreBuilder::build` now returns a `Result`
9+
- `StoreExt::store` now returns `Result<Arc<Store>>`
10+
11+
### Enhancements:
12+
13+
- Save and cancel pending auto save on drop
14+
- Use absolute path as store's key, fix #984
15+
- Share store to resource table by default
16+
- Enable auto save with 100ms debounce time by default
17+
- Use pretty json by default, close #1690
18+
19+
### New features:
20+
21+
- Add `get_store` to get shared stores across js and rust side
22+
- Add default (de)serialize functions settings `default_serialize_fn` and `default_deserialize_fn`
23+
- Allow js to use pre-stored (de)serialize functions registered by `register_serialize_fn` and `register_deserialize_fn`

examples/api/src-tauri/capabilities/base.json

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -79,10 +79,6 @@
7979
],
8080
"deny": ["$APPDATA/db/*.stronghold"]
8181
},
82-
"store:allow-entries",
83-
"store:allow-get",
84-
"store:allow-set",
85-
"store:allow-save",
86-
"store:allow-load"
82+
"store:default"
8783
]
8884
}

examples/api/src-tauri/src/lib.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,8 @@ pub fn run() {
6767
.user_agent(&format!("Tauri API - {}", std::env::consts::OS))
6868
.title("Tauri API Validation")
6969
.inner_size(1000., 800.)
70-
.min_inner_size(600., 400.);
70+
.min_inner_size(600., 400.)
71+
.visible(false);
7172
}
7273

7374
#[cfg(target_os = "windows")]

examples/api/src/views/Store.svelte

Lines changed: 60 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,71 @@
11
<script>
2-
import { Store } from "@tauri-apps/plugin-store";
2+
import { LazyStore } from "@tauri-apps/plugin-store";
33
import { onMount } from "svelte";
44
55
export let onMessage;
66
77
let key;
88
let value;
99
10-
const store = new Store("cache.json");
10+
let store = new LazyStore("cache.json");
1111
let cache = {};
1212
13-
onMount(async () => {
14-
await store.load();
15-
const values = await store.entries();
16-
for (const [key, value] of values) {
17-
cache[key] = value;
13+
async function refreshEntries() {
14+
try {
15+
const values = await store.entries();
16+
cache = {};
17+
for (const [key, value] of values) {
18+
cache[key] = value;
19+
}
20+
} catch (error) {
21+
onMessage(error);
1822
}
19-
cache = cache;
23+
}
24+
25+
onMount(async () => {
26+
await refreshEntries();
2027
});
2128
22-
function write(key, value) {
23-
store
24-
.set(key, value)
25-
.then(() => store.get(key))
26-
.then((v) => {
27-
cache[key] = v;
29+
async function write(key, value) {
30+
try {
31+
if (value) {
32+
await store.set(key, value);
33+
} else {
34+
await store.delete(key);
35+
}
36+
const v = await store.get(key);
37+
if (v === undefined) {
38+
delete cache[key];
2839
cache = cache;
29-
})
30-
.then(() => store.save())
31-
.catch(onMessage);
40+
} else {
41+
cache[key] = v;
42+
}
43+
} catch (error) {
44+
onMessage(error);
45+
}
46+
}
47+
48+
async function reset() {
49+
try {
50+
await store.reset();
51+
} catch (error) {
52+
onMessage(error);
53+
}
54+
await refreshEntries();
55+
}
56+
57+
async function close() {
58+
try {
59+
await store.close();
60+
onMessage("Store is now closed, any new operations will error out");
61+
} catch (error) {
62+
onMessage(error);
63+
}
64+
}
65+
66+
function reopen() {
67+
store = new LazyStore("cache.json");
68+
onMessage("We made a new `LazyStore` instance, operations will now work");
3269
}
3370
</script>
3471

@@ -44,7 +81,12 @@
4481
<input class="grow input" bind:value />
4582
</div>
4683

47-
<button class="btn" on:click={() => write(key, value)}> Write </button>
84+
<div>
85+
<button class="btn" on:click={() => write(key, value)}>Write</button>
86+
<button class="btn" on:click={() => reset()}>Reset</button>
87+
<button class="btn" on:click={() => close()}>Close</button>
88+
<button class="btn" on:click={() => reopen()}>Re-open</button>
89+
</div>
4890
</div>
4991

5092
<div>

plugins/store/README.md

Lines changed: 17 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ Afterwards all the plugin's APIs are available through the JavaScript guest bind
7070
```typescript
7171
import { Store } from '@tauri-apps/plugin-store'
7272

73-
const store = new Store('.settings.dat')
73+
const store = await Store.load('settings.json')
7474

7575
await store.set('some-key', { value: 5 })
7676

@@ -81,14 +81,11 @@ if (val) {
8181
} else {
8282
console.log('val is null')
8383
}
84-
85-
// This manually saves the store.
86-
await store.save()
8784
```
8885

8986
### Persisting Values
9087

91-
As seen above, values added to the store are not persisted between application loads unless the application is closed gracefully.
88+
Modifications made to the store are automatically saved by default
9289

9390
You can manually save a store with:
9491

@@ -103,65 +100,43 @@ However, you can also load them manually later like so:
103100
await store.load()
104101
```
105102

103+
### LazyStore
104+
105+
There's also a high level API `LazyStore` which only loads the store on first access, note that the options will be ignored if a `Store` with that path has already been created
106+
107+
```typescript
108+
import { LazyStore } from '@tauri-apps/plugin-store'
109+
110+
const store = new LazyStore('settings.json')
111+
```
112+
106113
## Usage from Rust
107114

108115
You can also create `Store` instances directly in Rust:
109116

110117
```rust
111-
use tauri_plugin_store::StoreBuilder;
118+
use tauri_plugin_store::StoreExt;
112119
use serde_json::json;
113120

114121
fn main() {
115122
tauri::Builder::default()
116123
.plugin(tauri_plugin_store::Builder::default().build())
117124
.setup(|app| {
118-
let mut store = StoreBuilder::new("app_data.bin").build(app.handle().clone());
119-
120-
// Attempt to load the store, if it's saved already.
121-
store.load().expect("Failed to load store from disk");
125+
// This loads the store from disk
126+
let store = app.store("app_data.json")?;
122127

123128
// Note that values must be serde_json::Value instances,
124129
// otherwise, they will not be compatible with the JavaScript bindings.
125-
store.insert("a".to_string(), json!("b"));
126-
127-
// You can manually save the store after making changes.
128-
// Otherwise, it will save upon graceful exit as described above.
129-
store.save()
130+
store.set("a".to_string(), json!("b"));
130131
})
131132
.run(tauri::generate_context!())
132133
.expect("error while running tauri application");
133134
}
134135
```
135136

136-
### Loading Gracefully
137-
138-
If you call `load` on a `Store` that hasn't yet been written to the disk, it will return an error. You must handle this error if you want to gracefully continue and use the default store until you save it to the disk. The example above shows how to do this.
139-
140-
For example, this would cause a panic if the store has not yet been created:
141-
142-
```rust
143-
store.load().unwrap();
144-
```
145-
146-
Rather than silently continuing like you may expect.
147-
148-
You should always handle the error appropriately rather than unwrapping, or you may experience unexpected app crashes:
149-
150-
```rust
151-
store.load().expect("Failed to load store from disk");
152-
```
153-
154137
### Frontend Interoperability
155138

156-
As you may have noticed, the `Store` crated above isn't accessible to the frontend. To interoperate with stores created by JavaScript use the exported `with_store` method:
157-
158-
```rust
159-
use tauri::Wry;
160-
use tauri_plugin_store::StoreExt;
161-
162-
let store = app.store_builder("app_data.bin").build();
163-
store.insert("key", "value");
164-
```
139+
The store created from both Rust side and JavaScript side are stored in the app's resource table and can be accessed by both sides, you can access it by using the same path, with `getStore` and `LazyStore` in the JavaScript side and `get_store` and `store` in the Rust side
165140

166141
## Contributing
167142

plugins/store/api-iife.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

plugins/store/build.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
// SPDX-License-Identifier: MIT
44

55
const COMMANDS: &[&str] = &[
6-
"create_store",
6+
"load",
7+
"get_store",
78
"set",
89
"get",
910
"has",
@@ -12,9 +13,9 @@ const COMMANDS: &[&str] = &[
1213
"reset",
1314
"keys",
1415
"values",
15-
"length",
1616
"entries",
17-
"load",
17+
"length",
18+
"reload",
1819
"save",
1920
];
2021

plugins/store/examples/AppSettingsManager/src-tauri/src/main.rs

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,8 @@
55
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
66
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
77

8-
use std::time::Duration;
9-
108
use serde_json::json;
9+
use tauri::Listener;
1110
use tauri_plugin_store::StoreExt;
1211

1312
mod app;
@@ -18,17 +17,11 @@ fn main() {
1817
.plugin(tauri_plugin_store::Builder::new().build())
1918
.setup(|app| {
2019
// Init store and load it from disk
21-
let store = app
22-
.handle()
23-
.store_builder("settings.json")
24-
.auto_save(Duration::from_millis(100))
25-
.build();
26-
27-
// If there are no saved settings yet, this will return an error so we ignore the return value.
28-
let _ = store.load();
29-
20+
let store = app.store("settings.json")?;
21+
app.listen("store://change", |event| {
22+
dbg!(event);
23+
});
3024
let app_settings = AppSettings::load_from_store(&store);
31-
3225
match app_settings {
3326
Ok(app_settings) => {
3427
let theme = app_settings.theme;

plugins/store/examples/AppSettingsManager/src-tauri/tauri.conf.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
"version": "0.1.0",
44
"identifier": "com.tauri.app-settings-manager",
55
"build": {
6+
"devUrl": "http://localhost:1420",
67
"frontendDist": "../dist"
78
},
89
"app": {

0 commit comments

Comments
 (0)