Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(store)!: fully rework and add auto save #1550

Merged
merged 46 commits into from
Oct 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
94ab85d
Add auto save to store plugin
Legend-Master Jul 15, 2024
389e811
Merge branch 'v2' into store-auto-save
Legend-Master Jul 15, 2024
bb1db1c
Put jsdoc at constructor instead of class level
Legend-Master Jul 15, 2024
7ffb3d5
Clippy
Legend-Master Jul 15, 2024
2f91667
Use enum instead of bool
Legend-Master Jul 15, 2024
4012005
Some(AutoSaveMessage::Cancel) | None
Legend-Master Jul 15, 2024
fbfcba8
from_millis
Legend-Master Jul 15, 2024
571317a
u64
Legend-Master Jul 15, 2024
e12d3fc
Add change file
Legend-Master Jul 15, 2024
7ce9e60
Rename to emit_on_change
Legend-Master Jul 15, 2024
b85c863
should use Duration in `with_store`
Legend-Master Jul 15, 2024
5d35b1f
Add breaking change notice to change file
Legend-Master Jul 15, 2024
2ee919b
Emit change event for inserts by reset
Legend-Master Jul 15, 2024
d18370d
Update readme example
Legend-Master Jul 15, 2024
9336e7f
Update example
Legend-Master Jul 15, 2024
35435ec
Remove extra line
Legend-Master Jul 15, 2024
3d84eab
Make description clear it only works with managed
Legend-Master Jul 15, 2024
23c9c21
Fix links in docstring
Legend-Master Jul 15, 2024
22f11b9
Fix doc string closing
Legend-Master Jul 15, 2024
346ea33
Merge remote-tracking branch 'upstream/v2' into store-auto-save
Legend-Master Jul 15, 2024
738e0a8
Merge branch 'v2' into store-auto-save
Legend-Master Jul 17, 2024
f398859
get_mut
Legend-Master Jul 17, 2024
c2b0bd8
Proof of concept
Legend-Master Jul 19, 2024
195384f
fmt
Legend-Master Jul 19, 2024
5f808fd
Load store on create
Legend-Master Jul 19, 2024
436c762
Merge branch 'v2' into store-auto-save
Legend-Master Jul 27, 2024
bdc80d5
cargo fmt
Legend-Master Jul 27, 2024
5cadedf
Merge branch 'store-auto-save' of https://github.com/Legend-Master/pl…
Legend-Master Jul 27, 2024
6287665
Merge branch 'v2' into store-auto-save
Legend-Master Jul 30, 2024
7f99e47
Merge branch 'v2' into store-auto-save
Legend-Master Jul 31, 2024
75e9f0c
Merge branch 'v2' into store-auto-save
Legend-Master Aug 8, 2024
34dca14
Merge branch 'v2' into store-auto-save
Legend-Master Aug 28, 2024
ea351fb
Merge branch 'store-auto-save' of https://github.com/Legend-Master/pl…
Legend-Master Aug 28, 2024
7d6dbc3
Fix merge conflits
Legend-Master Aug 28, 2024
2ed4651
Merge branch 'v2' into store-auto-save
Legend-Master Aug 29, 2024
7f43d5e
Merge branch 'v2' into store-auto-save
tweidinger Sep 3, 2024
ed0619a
Merge branch 'v2' into store-auto-save
Legend-Master Sep 8, 2024
5a7d5aa
Merge branch 'store-auto-save' of https://github.com/Legend-Master/pl…
Legend-Master Sep 8, 2024
9ee0b7b
Format
Legend-Master Sep 8, 2024
456a0f5
Merge branch 'v2' into store-auto-save
Legend-Master Sep 28, 2024
77bc7f0
Merge branch 'v2' into store-auto-save
Legend-Master Sep 29, 2024
d1ab707
Merge remote-tracking branch 'origin/v2' into store-auto-save
lucasfernog Oct 1, 2024
578487a
small cleanup
lucasfernog Oct 1, 2024
84351a3
update docs, use `impl Into<JsonValue>`
lucasfernog Oct 1, 2024
5aaa591
fix doctests, further simplification of api
lucasfernog Oct 1, 2024
d8ae8b2
add store options
lucasfernog Oct 1, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changes/store-api-refactor.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"store-js": patch
---

**Breaking change**: Removed the `Store` constructor and added the `createStore` API.
7 changes: 7 additions & 0 deletions .changes/store-auto-save.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"store": patch
---

Add a setting `auto_save` to enable a store to debounce save on modification (on calls like set, clear, delete, reset)

**Breaking change**: Removed the `with_store` API and added `StoreExt::store_builder`.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions plugins/store/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ tauri = { workspace = true }
log = { workspace = true }
thiserror = { workspace = true }
dunce = { workspace = true }
tokio = { version = "1", features = ["sync", "time", "macros"] }

[target.'cfg(target_os = "ios")'.dependencies]
tauri = { workspace = true, features = ["wry"] }

[dev-dependencies]
tauri = { workspace = true, features = ["wry"] }
8 changes: 3 additions & 5 deletions plugins/store/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,12 +149,10 @@ As you may have noticed, the `Store` crated above isn't accessible to the fronte

```rust
use tauri::Wry;
use tauri_plugin_store::with_store;
use tauri_plugin_store::StoreExt;

let stores = app.state::<StoreCollection<Wry>>();
let path = PathBuf::from("app_data.bin");

with_store(app_handle, stores, path, |store| store.insert("a".to_string(), json!("b")))
let store = app.store_builder("app_data.bin").build();
store.insert("key", "value");
```

## Contributing
Expand Down
2 changes: 1 addition & 1 deletion plugins/store/api-iife.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 12 additions & 1 deletion plugins/store/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,18 @@
// SPDX-License-Identifier: MIT

const COMMANDS: &[&str] = &[
"set", "get", "has", "delete", "clear", "reset", "keys", "values", "length", "entries", "load",
"create_store",
"set",
"get",
"has",
"delete",
"clear",
"reset",
"keys",
"values",
"length",
"entries",
"load",
"save",
];

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,8 @@ impl AppSettings {

let theme = store
.get("appSettings.theme")
.and_then(|v| v.as_str())
.map(|s| s.to_string())
.unwrap_or_else(|| "dark".to_string());
.and_then(|v| v.as_str().map(String::from))
.unwrap_or_else(|| "dark".to_owned());

Ok(AppSettings {
launch_at_login,
Expand Down
28 changes: 19 additions & 9 deletions plugins/store/examples/AppSettingsManager/src-tauri/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,24 @@
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]

use tauri_plugin_store::StoreBuilder;
use std::time::Duration;

use serde_json::json;
use tauri_plugin_store::StoreExt;

mod app;
use app::settings::AppSettings;

fn main() {
tauri::Builder::default()
.plugin(tauri_plugin_store::Builder::default().build())
.plugin(tauri_plugin_store::Builder::new().build())
.setup(|app| {
// Init store and load it from disk
let mut store = StoreBuilder::new("settings.json").build(app.handle().clone());
let store = app
.handle()
.store_builder("settings.json")
.auto_save(Duration::from_millis(100))
.build();

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

println!("theme {}", theme);
println!("launch_at_login {}", launch_at_login);

Ok(())
println!("theme {theme}");
println!("launch_at_login {launch_at_login}");
store.set(
"appSettings",
json!({ "theme": theme, "launchAtLogin": launch_at_login }),
);
}
Err(err) => {
eprintln!("Error loading settings: {}", err);
eprintln!("Error loading settings: {err}");
// Handle the error case if needed
Err(err) // Convert the error to a Box<dyn Error> and return Err(err) here
return Err(err); // Convert the error to a Box<dyn Error> and return Err(err) here
}
}
Ok(())
})
.run(tauri::generate_context!())
.expect("error while running tauri application");
Expand Down
76 changes: 42 additions & 34 deletions plugins/store/guest-js/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

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

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

interface ChangePayload<T> {
path: string
Expand All @@ -13,12 +13,36 @@ interface ChangePayload<T> {
}

/**
* A key-value store persisted by the backend layer.
* Options to create a store
*/
export class Store {
path: string
constructor(path: string) {
this.path = path
export type StoreOptions = {
/**
* Auto save on modification with debounce duration in milliseconds
*/
autoSave?: boolean
}

/**
* @param path: Path to save the store in `app_data_dir`
* @param options: Store configuration options
*/
export async function createStore(path: string, options?: StoreOptions) {
const resourceId = await invoke<number>('plugin:store|create_store', {
path,
...options
})
return new Store(resourceId, path)
}

/**
* A lazy loaded key-value store persisted by the backend layer.
*/
export class Store extends Resource {
constructor(
rid: number,
private readonly path: string
) {
super(rid)
}

/**
Expand All @@ -30,7 +54,7 @@ export class Store {
*/
async set(key: string, value: unknown): Promise<void> {
await invoke('plugin:store|set', {
path: this.path,
rid: this.rid,
key,
value
})
Expand All @@ -44,7 +68,7 @@ export class Store {
*/
async get<T>(key: string): Promise<T | null> {
return await invoke('plugin:store|get', {
path: this.path,
rid: this.rid,
key
})
}
Expand All @@ -57,7 +81,7 @@ export class Store {
*/
async has(key: string): Promise<boolean> {
return await invoke('plugin:store|has', {
path: this.path,
rid: this.rid,
key
})
}
Expand All @@ -70,7 +94,7 @@ export class Store {
*/
async delete(key: string): Promise<boolean> {
return await invoke('plugin:store|delete', {
path: this.path,
rid: this.rid,
key
})
}
Expand All @@ -82,9 +106,7 @@ export class Store {
* @returns
*/
async clear(): Promise<void> {
await invoke('plugin:store|clear', {
path: this.path
})
await invoke('plugin:store|clear', { rid: this.rid })
}

/**
Expand All @@ -94,9 +116,7 @@ export class Store {
* @returns
*/
async reset(): Promise<void> {
await invoke('plugin:store|reset', {
path: this.path
})
await invoke('plugin:store|reset', { rid: this.rid })
}

/**
Expand All @@ -105,9 +125,7 @@ export class Store {
* @returns
*/
async keys(): Promise<string[]> {
return await invoke('plugin:store|keys', {
path: this.path
})
return await invoke('plugin:store|keys', { rid: this.rid })
}

/**
Expand All @@ -116,9 +134,7 @@ export class Store {
* @returns
*/
async values<T>(): Promise<T[]> {
return await invoke('plugin:store|values', {
path: this.path
})
return await invoke('plugin:store|values', { rid: this.rid })
}

/**
Expand All @@ -127,9 +143,7 @@ export class Store {
* @returns
*/
async entries<T>(): Promise<Array<[key: string, value: T]>> {
return await invoke('plugin:store|entries', {
path: this.path
})
return await invoke('plugin:store|entries', { rid: this.rid })
}

/**
Expand All @@ -138,9 +152,7 @@ export class Store {
* @returns
*/
async length(): Promise<number> {
return await invoke('plugin:store|length', {
path: this.path
})
return await invoke('plugin:store|length', { rid: this.rid })
}

/**
Expand All @@ -152,9 +164,7 @@ export class Store {
* @returns
*/
async load(): Promise<void> {
await invoke('plugin:store|load', {
path: this.path
})
await invoke('plugin:store|load', { rid: this.rid })
}

/**
Expand All @@ -165,9 +175,7 @@ export class Store {
* @returns
*/
async save(): Promise<void> {
await invoke('plugin:store|save', {
path: this.path
})
await invoke('plugin:store|save', { rid: this.rid })
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Automatically generated - DO NOT EDIT!

"$schema" = "../../schemas/schema.json"

[[permission]]
identifier = "allow-create-store"
description = "Enables the create_store command without any pre-configured scope."
commands.allow = ["create_store"]

[[permission]]
identifier = "deny-create-store"
description = "Denies the create_store command without any pre-configured scope."
commands.deny = ["create_store"]
27 changes: 27 additions & 0 deletions plugins/store/permissions/autogenerated/reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ All operations are enabled by default.



- `allow-create-store`
- `allow-clear`
- `allow-delete`
- `allow-entries`
Expand Down Expand Up @@ -60,6 +61,32 @@ Denies the clear command without any pre-configured scope.
<tr>
<td>

`store:allow-create-store`

</td>
<td>

Enables the create_store command without any pre-configured scope.

</td>
</tr>

<tr>
<td>

`store:deny-create-store`

</td>
<td>

Denies the create_store command without any pre-configured scope.

</td>
</tr>

<tr>
<td>

`store:allow-delete`

</td>
Expand Down
1 change: 1 addition & 0 deletions plugins/store/permissions/default.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ All operations are enabled by default.

"""
permissions = [
"allow-create-store",
"allow-clear",
"allow-delete",
"allow-entries",
Expand Down
10 changes: 10 additions & 0 deletions plugins/store/permissions/schemas/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,16 @@
"type": "string",
"const": "deny-clear"
},
{
"description": "Enables the create_store command without any pre-configured scope.",
"type": "string",
"const": "allow-create-store"
},
{
"description": "Denies the create_store command without any pre-configured scope.",
"type": "string",
"const": "deny-create-store"
},
{
"description": "Enables the delete command without any pre-configured scope.",
"type": "string",
Expand Down
Loading
Loading