Skip to content

Commit

Permalink
Add create or existing
Browse files Browse the repository at this point in the history
  • Loading branch information
Legend-Master committed Oct 5, 2024
1 parent 7faf8c3 commit dc5be00
Show file tree
Hide file tree
Showing 9 changed files with 193 additions and 21 deletions.
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.

1 change: 1 addition & 0 deletions plugins/store/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

const COMMANDS: &[&str] = &[
"create_store",
"create_or_existing_store",
"get_store",
"close_store",
"set",
Expand Down
37 changes: 34 additions & 3 deletions plugins/store/guest-js/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,19 @@ export async function createStore(
return await Store.createStore(path, options)
}

/**
* Create a new Store or get the existing store with the path
*
* @param path: Path to save the store in `app_data_dir`
* @param options: Store configuration options
*/
export async function createOrExistingStore(
path: string,
options?: StoreOptions
): Promise<Store> {
return await Store.createOrExistingStore(path, options)
}

/**
* @param path: Path of the store in the rust side
*/
Expand All @@ -60,9 +73,7 @@ export class LazyStore implements IStore {

private get store(): Promise<Store> {
if (!this._store) {
this._store = getStore(this.path).then(
async (store) => store || (await createStore(this.path, this.options))
)
this._store = createOrExistingStore(this.path, this.options)
}
return this._store
}
Expand Down Expand Up @@ -183,6 +194,26 @@ export class Store extends Resource implements IStore {
)
}

/**
* Create a new Store or get the existing store with the path
*
* @param path: Path to save the store in `app_data_dir`
* @param options: Store configuration options
*/
static async createOrExistingStore(
path: string,
options?: StoreOptions
): Promise<Store> {
const rid = await invoke<number>('plugin:store|create_or_existing_store', {
path,
...options
})
return new Store(
rid
// path
)
}

/**
* @param path: Path of the store in the rust side
*/
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-or-existing-store"
description = "Enables the create_or_existing_store command without any pre-configured scope."
commands.allow = ["create_or_existing_store"]

[[permission]]
identifier = "deny-create-or-existing-store"
description = "Denies the create_or_existing_store command without any pre-configured scope."
commands.deny = ["create_or_existing_store"]
26 changes: 26 additions & 0 deletions plugins/store/permissions/autogenerated/reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,32 @@ Denies the close_store command without any pre-configured scope.
<tr>
<td>

`store:allow-create-or-existing-store`

</td>
<td>

Enables the create_or_existing_store command without any pre-configured scope.

</td>
</tr>

<tr>
<td>

`store:deny-create-or-existing-store`

</td>
<td>

Denies the create_or_existing_store command without any pre-configured scope.

</td>
</tr>

<tr>
<td>

`store:allow-create-store`

</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 @@ -12,6 +12,7 @@ All operations are enabled by default.
"""
permissions = [
"allow-create-store",
"allow-create-or-existing-store",
"allow-get-store",
"allow-close-store",
"allow-clear",
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 @@ -314,6 +314,16 @@
"type": "string",
"const": "deny-close-store"
},
{
"description": "Enables the create_or_existing_store command without any pre-configured scope.",
"type": "string",
"const": "allow-create-or-existing-store"
},
{
"description": "Denies the create_or_existing_store command without any pre-configured scope.",
"type": "string",
"const": "deny-create-or-existing-store"
},
{
"description": "Enables the create_store command without any pre-configured scope.",
"type": "string",
Expand Down
56 changes: 47 additions & 9 deletions plugins/store/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,17 +57,15 @@ enum AutoSave {
Bool(bool),
}

#[tauri::command]
async fn create_store<R: Runtime>(
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>,
) -> Result<ResourceId> {
let mut builder = app.store_builder(path.clone());

) -> Result<StoreBuilder<R>> {
let mut builder = app.store_builder(path);
if let Some(auto_save) = auto_save {
match auto_save {
AutoSave::DebounceDuration(duration) => {
Expand Down Expand Up @@ -95,19 +93,59 @@ async fn create_store<R: Runtime>(
.ok_or_else(|| crate::Error::DeserializeFunctionNotFound(deserialize_fn_name))?;
builder = builder.deserialize(*deserialize_fn);
}
Ok(builder)
}

#[tauri::command]
async fn create_store<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>,
) -> Result<ResourceId> {
let builder = builder(
app,
store_state,
path,
auto_save,
serialize_fn_name,
deserialize_fn_name,
)?;
let (_, rid) = builder.build_inner()?;
Ok(rid)
}

#[tauri::command]
async fn create_or_existing_store<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>,
) -> Result<ResourceId> {
let builder = builder(
app,
store_state,
path,
auto_save,
serialize_fn_name,
deserialize_fn_name,
)?;
let (_, rid) = builder.build_or_existing_inner();
Ok(rid)
}

#[tauri::command]
async fn get_store<R: Runtime>(
app: AppHandle<R>,
store_state: State<'_, StoreState>,
path: PathBuf,
) -> Result<Option<ResourceId>> {
let stores = store_state.stores.lock().unwrap();
Ok(stores.get(&resolve_store_path(app, path)).copied())
Ok(stores.get(&resolve_store_path(&app, path)).copied())
}

#[tauri::command]
Expand Down Expand Up @@ -217,8 +255,7 @@ pub trait StoreExt<R: Runtime> {

impl<R: Runtime, T: Manager<R>> StoreExt<R> for T {
fn store(&self, path: impl AsRef<Path>) -> Arc<Store<R>> {
self.get_store(&path)
.unwrap_or_else(|| self.create_store(&path).unwrap())
StoreBuilder::new(self.app_handle(), path).build_or_existing()
}

fn create_store(&self, path: impl AsRef<Path>) -> Result<Arc<Store<R>>> {
Expand All @@ -233,7 +270,7 @@ impl<R: Runtime, T: Manager<R>> StoreExt<R> for T {
let collection = self.state::<StoreState>();
let stores = collection.stores.lock().unwrap();
stores
.get(path.as_ref())
.get(&resolve_store_path(self.app_handle(), path.as_ref()))
.and_then(|rid| self.resources_table().get(*rid).ok())
}
}
Expand Down Expand Up @@ -351,6 +388,7 @@ impl<R: Runtime> Builder<R> {
create_store,
get_store,
close_store,
create_or_existing_store,
set,
get,
has,
Expand Down
68 changes: 60 additions & 8 deletions plugins/store/src/store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,16 @@ pub type SerializeFn =
pub type DeserializeFn =
fn(&[u8]) -> Result<HashMap<String, JsonValue>, Box<dyn std::error::Error + Send + Sync>>;

pub(crate) fn resolve_store_path<R: Runtime>(app: AppHandle<R>, path: impl AsRef<Path>) -> PathBuf {
app.path()
.resolve(path, BaseDirectory::AppData)
.expect("failed to resolve app dir")
pub(crate) fn resolve_store_path<R: Runtime>(
app: &AppHandle<R>,
path: impl AsRef<Path>,
) -> PathBuf {
dunce::simplified(
&app.path()
.resolve(path, BaseDirectory::AppData)
.expect("failed to resolve app dir"),
)
.to_path_buf()
}

/// Builds a [`Store`]
Expand Down Expand Up @@ -55,14 +61,13 @@ impl<R: Runtime> StoreBuilder<R> {
/// ```
pub fn new<M: Manager<R>, P: AsRef<Path>>(manager: &M, path: P) -> Self {
let app = manager.app_handle().clone();
let path = resolve_store_path(app.clone(), path);
let path = resolve_store_path(&app, path);
let state = app.state::<StoreState>();
let serialize_fn = state.default_serialize;
let deserialize_fn = state.default_deserialize;
Self {
app,
// Since Store.path is only exposed to the user in emit calls we may as well simplify it here already.
path: dunce::simplified(&path).to_path_buf(),
path,
defaults: None,
serialize_fn,
deserialize_fn,
Expand Down Expand Up @@ -212,7 +217,38 @@ impl<R: Runtime> StoreBuilder<R> {
Ok((store, rid))
}

/// Builds the [`Store`].
pub(crate) fn build_or_existing_inner(mut self) -> (Arc<Store<R>>, ResourceId) {
let state = self.app.state::<StoreState>();
let mut stores = state.stores.lock().unwrap();

if let Some(rid) = stores.get(&self.path) {
(self.app.resources_table().get(*rid).unwrap(), *rid)
} else {
let mut store_inner = StoreInner::new(
self.app.clone(),
self.path.clone(),
self.defaults.take(),
self.serialize_fn,
self.deserialize_fn,
);
if self.load_on_build {
let _ = store_inner.load();
}

let store = Store {
auto_save: self.auto_save,
auto_save_debounce_sender: Arc::new(Mutex::new(None)),
store: Arc::new(Mutex::new(store_inner)),
};

let store = Arc::new(store);
let rid = self.app.resources_table().add_arc(store.clone());
stores.insert(self.path, rid);
(store, rid)
}
}

/// Builds the [`Store`], also see [`build_or_existing`](Self::build_or_existing).
///
/// This loads the store from disk and put the store in the app's resource table,
/// to remove it from the resource table, call [`Store::close_store`]
Expand All @@ -235,6 +271,22 @@ impl<R: Runtime> StoreBuilder<R> {
let (store, _) = self.build_inner()?;
Ok(store)
}

/// Get the existing store with the same path or builds a new [`Store`], also see [`build`](Self::build).
///
/// # Examples
/// ```
/// tauri::Builder::default()
/// .plugin(tauri_plugin_store::Builder::default().build())
/// .setup(|app| {
/// let store = tauri_plugin_store::StoreBuilder::new(app, "store.json").build_or_existing();
/// Ok(())
/// });
/// ```
pub fn build_or_existing(self) -> Arc<Store<R>> {
let (store, _) = self.build_or_existing_inner();
store
}
}

pub(crate) enum AutoSaveMessage {
Expand Down

0 comments on commit dc5be00

Please sign in to comment.