Skip to content

Commit 53dfcce

Browse files
Legend-Mastertweidingerlucasfernog
authored andcommitted
feat(store)!: fully rework and add auto save (tauri-apps#1550)
* Add auto save to store plugin * Put jsdoc at constructor instead of class level * Clippy * Use enum instead of bool * Some(AutoSaveMessage::Cancel) | None * from_millis * u64 * Add change file * Rename to emit_on_change * should use Duration in `with_store` * Add breaking change notice to change file * Emit change event for inserts by reset * Update readme example * Update example * Remove extra line * Make description clear it only works with managed * Fix links in docstring * Fix doc string closing * get_mut * Proof of concept * fmt * Load store on create * cargo fmt * Fix merge conflits * Format * small cleanup * update docs, use `impl Into<JsonValue>` * fix doctests, further simplification of api * add store options --------- Co-authored-by: Tillmann <[email protected]> Co-authored-by: Lucas Nogueira <[email protected]>
1 parent fe63544 commit 53dfcce

File tree

16 files changed

+603
-363
lines changed

16 files changed

+603
-363
lines changed

.changes/store-api-refactor.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"store-js": patch
3+
---
4+
5+
**Breaking change**: Removed the `Store` constructor and added the `createStore` API.

.changes/store-auto-save.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
"store": patch
3+
---
4+
5+
Add a setting `auto_save` to enable a store to debounce save on modification (on calls like set, clear, delete, reset)
6+
7+
**Breaking change**: Removed the `with_store` API and added `StoreExt::store_builder`.

Cargo.lock

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

plugins/store/Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ tauri = { workspace = true }
3030
log = { workspace = true }
3131
thiserror = { workspace = true }
3232
dunce = { workspace = true }
33+
tokio = { version = "1", features = ["sync", "time", "macros"] }
3334

3435
[target.'cfg(target_os = "ios")'.dependencies]
3536
tauri = { workspace = true, features = ["wry"] }
37+
38+
[dev-dependencies]
39+
tauri = { workspace = true, features = ["wry"] }

plugins/store/README.md

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -149,12 +149,10 @@ As you may have noticed, the `Store` crated above isn't accessible to the fronte
149149

150150
```rust
151151
use tauri::Wry;
152-
use tauri_plugin_store::with_store;
152+
use tauri_plugin_store::StoreExt;
153153

154-
let stores = app.state::<StoreCollection<Wry>>();
155-
let path = PathBuf::from("app_data.bin");
156-
157-
with_store(app_handle, stores, path, |store| store.insert("a".to_string(), json!("b")))
154+
let store = app.store_builder("app_data.bin").build();
155+
store.insert("key", "value");
158156
```
159157

160158
## Contributing

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: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,18 @@
33
// SPDX-License-Identifier: MIT
44

55
const COMMANDS: &[&str] = &[
6-
"set", "get", "has", "delete", "clear", "reset", "keys", "values", "length", "entries", "load",
6+
"create_store",
7+
"set",
8+
"get",
9+
"has",
10+
"delete",
11+
"clear",
12+
"reset",
13+
"keys",
14+
"values",
15+
"length",
16+
"entries",
17+
"load",
718
"save",
819
];
920

plugins/store/examples/AppSettingsManager/src-tauri/src/app/settings.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,8 @@ impl AppSettings {
2121

2222
let theme = store
2323
.get("appSettings.theme")
24-
.and_then(|v| v.as_str())
25-
.map(|s| s.to_string())
26-
.unwrap_or_else(|| "dark".to_string());
24+
.and_then(|v| v.as_str().map(String::from))
25+
.unwrap_or_else(|| "dark".to_owned());
2726

2827
Ok(AppSettings {
2928
launch_at_login,

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

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

8-
use tauri_plugin_store::StoreBuilder;
8+
use std::time::Duration;
9+
10+
use serde_json::json;
11+
use tauri_plugin_store::StoreExt;
912

1013
mod app;
1114
use app::settings::AppSettings;
1215

1316
fn main() {
1417
tauri::Builder::default()
15-
.plugin(tauri_plugin_store::Builder::default().build())
18+
.plugin(tauri_plugin_store::Builder::new().build())
1619
.setup(|app| {
1720
// Init store and load it from disk
18-
let mut store = StoreBuilder::new("settings.json").build(app.handle().clone());
21+
let store = app
22+
.handle()
23+
.store_builder("settings.json")
24+
.auto_save(Duration::from_millis(100))
25+
.build();
1926

2027
// If there are no saved settings yet, this will return an error so we ignore the return value.
2128
let _ = store.load();
@@ -27,17 +34,20 @@ fn main() {
2734
let theme = app_settings.theme;
2835
let launch_at_login = app_settings.launch_at_login;
2936

30-
println!("theme {}", theme);
31-
println!("launch_at_login {}", launch_at_login);
32-
33-
Ok(())
37+
println!("theme {theme}");
38+
println!("launch_at_login {launch_at_login}");
39+
store.set(
40+
"appSettings",
41+
json!({ "theme": theme, "launchAtLogin": launch_at_login }),
42+
);
3443
}
3544
Err(err) => {
36-
eprintln!("Error loading settings: {}", err);
45+
eprintln!("Error loading settings: {err}");
3746
// Handle the error case if needed
38-
Err(err) // Convert the error to a Box<dyn Error> and return Err(err) here
47+
return Err(err); // Convert the error to a Box<dyn Error> and return Err(err) here
3948
}
4049
}
50+
Ok(())
4151
})
4252
.run(tauri::generate_context!())
4353
.expect("error while running tauri application");

plugins/store/guest-js/index.ts

Lines changed: 42 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
import { listen, type UnlistenFn } from '@tauri-apps/api/event'
66

7-
import { invoke } from '@tauri-apps/api/core'
7+
import { invoke, Resource } from '@tauri-apps/api/core'
88

99
interface ChangePayload<T> {
1010
path: string
@@ -13,12 +13,36 @@ interface ChangePayload<T> {
1313
}
1414

1515
/**
16-
* A key-value store persisted by the backend layer.
16+
* Options to create a store
1717
*/
18-
export class Store {
19-
path: string
20-
constructor(path: string) {
21-
this.path = path
18+
export type StoreOptions = {
19+
/**
20+
* Auto save on modification with debounce duration in milliseconds
21+
*/
22+
autoSave?: boolean
23+
}
24+
25+
/**
26+
* @param path: Path to save the store in `app_data_dir`
27+
* @param options: Store configuration options
28+
*/
29+
export async function createStore(path: string, options?: StoreOptions) {
30+
const resourceId = await invoke<number>('plugin:store|create_store', {
31+
path,
32+
...options
33+
})
34+
return new Store(resourceId, path)
35+
}
36+
37+
/**
38+
* A lazy loaded key-value store persisted by the backend layer.
39+
*/
40+
export class Store extends Resource {
41+
constructor(
42+
rid: number,
43+
private readonly path: string
44+
) {
45+
super(rid)
2246
}
2347

2448
/**
@@ -30,7 +54,7 @@ export class Store {
3054
*/
3155
async set(key: string, value: unknown): Promise<void> {
3256
await invoke('plugin:store|set', {
33-
path: this.path,
57+
rid: this.rid,
3458
key,
3559
value
3660
})
@@ -44,7 +68,7 @@ export class Store {
4468
*/
4569
async get<T>(key: string): Promise<T | null> {
4670
return await invoke('plugin:store|get', {
47-
path: this.path,
71+
rid: this.rid,
4872
key
4973
})
5074
}
@@ -57,7 +81,7 @@ export class Store {
5781
*/
5882
async has(key: string): Promise<boolean> {
5983
return await invoke('plugin:store|has', {
60-
path: this.path,
84+
rid: this.rid,
6185
key
6286
})
6387
}
@@ -70,7 +94,7 @@ export class Store {
7094
*/
7195
async delete(key: string): Promise<boolean> {
7296
return await invoke('plugin:store|delete', {
73-
path: this.path,
97+
rid: this.rid,
7498
key
7599
})
76100
}
@@ -82,9 +106,7 @@ export class Store {
82106
* @returns
83107
*/
84108
async clear(): Promise<void> {
85-
await invoke('plugin:store|clear', {
86-
path: this.path
87-
})
109+
await invoke('plugin:store|clear', { rid: this.rid })
88110
}
89111

90112
/**
@@ -94,9 +116,7 @@ export class Store {
94116
* @returns
95117
*/
96118
async reset(): Promise<void> {
97-
await invoke('plugin:store|reset', {
98-
path: this.path
99-
})
119+
await invoke('plugin:store|reset', { rid: this.rid })
100120
}
101121

102122
/**
@@ -105,9 +125,7 @@ export class Store {
105125
* @returns
106126
*/
107127
async keys(): Promise<string[]> {
108-
return await invoke('plugin:store|keys', {
109-
path: this.path
110-
})
128+
return await invoke('plugin:store|keys', { rid: this.rid })
111129
}
112130

113131
/**
@@ -116,9 +134,7 @@ export class Store {
116134
* @returns
117135
*/
118136
async values<T>(): Promise<T[]> {
119-
return await invoke('plugin:store|values', {
120-
path: this.path
121-
})
137+
return await invoke('plugin:store|values', { rid: this.rid })
122138
}
123139

124140
/**
@@ -127,9 +143,7 @@ export class Store {
127143
* @returns
128144
*/
129145
async entries<T>(): Promise<Array<[key: string, value: T]>> {
130-
return await invoke('plugin:store|entries', {
131-
path: this.path
132-
})
146+
return await invoke('plugin:store|entries', { rid: this.rid })
133147
}
134148

135149
/**
@@ -138,9 +152,7 @@ export class Store {
138152
* @returns
139153
*/
140154
async length(): Promise<number> {
141-
return await invoke('plugin:store|length', {
142-
path: this.path
143-
})
155+
return await invoke('plugin:store|length', { rid: this.rid })
144156
}
145157

146158
/**
@@ -152,9 +164,7 @@ export class Store {
152164
* @returns
153165
*/
154166
async load(): Promise<void> {
155-
await invoke('plugin:store|load', {
156-
path: this.path
157-
})
167+
await invoke('plugin:store|load', { rid: this.rid })
158168
}
159169

160170
/**
@@ -165,9 +175,7 @@ export class Store {
165175
* @returns
166176
*/
167177
async save(): Promise<void> {
168-
await invoke('plugin:store|save', {
169-
path: this.path
170-
})
178+
await invoke('plugin:store|save', { rid: this.rid })
171179
}
172180

173181
/**

0 commit comments

Comments
 (0)