#![forbid(unsafe_code)]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![doc = include_str!("../README.md")]
#![doc(html_favicon_url = "https://tb.dev.br/tauri-store/favicon.ico")]
mod command;
mod manager;
mod vue;
use serde::de::DeserializeOwned;
use std::collections::{HashMap, HashSet};
use std::path::{Path, PathBuf};
use std::time::Duration;
use tauri::plugin::{PluginApi, TauriPlugin};
use tauri::{AppHandle, RunEvent, Runtime};
use tauri_store::Migrator;
pub use manager::ManagerExt;
pub use tauri_store::prelude::*;
pub use tauri_store::{Migration, MigrationContext};
pub use vue::{Vue, VueMarker};
#[cfg(target_os = "ios")]
tauri::ios_plugin_binding!(init_plugin_vue);
pub struct Builder<R: Runtime> {
default_path: Option<PathBuf>,
path_table: HashMap<StoreId, Box<Path>>,
default_marshaler: Option<Box<dyn Marshaler>>,
marshaler_table: HashMap<StoreId, Box<dyn Marshaler>>,
default_save_strategy: SaveStrategy,
autosave: Option<Duration>,
on_load: Option<Box<OnLoadFn<R, VueMarker>>>,
save_denylist: HashSet<StoreId>,
sync_denylist: HashSet<StoreId>,
migrator: Migrator,
debug_stores: bool,
}
impl<R: Runtime> Builder<R> {
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn autosave(mut self, interval: Duration) -> Self {
self.autosave = Some(interval);
self
}
#[must_use]
pub fn default_save_strategy(mut self, strategy: SaveStrategy) -> Self {
self.default_save_strategy = strategy;
self
}
#[must_use]
pub fn on_load<F>(mut self, f: F) -> Self
where
F: Fn(&Store<R, VueMarker>) -> Result<()> + Send + Sync + 'static,
{
self.on_load = Some(Box::new(f));
self
}
#[must_use]
pub fn path(mut self, path: impl AsRef<Path>) -> Self {
let path = path.as_ref().to_path_buf();
self.default_path = Some(path);
self
}
#[must_use]
pub fn path_of(mut self, id: impl AsRef<str>, path: impl AsRef<Path>) -> Self {
let id = StoreId::from(id.as_ref());
let path = Box::from(path.as_ref());
self.path_table.insert(id, path);
self
}
#[must_use]
pub fn save_denylist<I, T>(mut self, denylist: I) -> Self
where
I: IntoIterator<Item = T>,
T: AsRef<str>,
{
self.save_denylist.extend(
denylist
.into_iter()
.map(|it| StoreId::from(it.as_ref())),
);
self
}
#[must_use]
pub fn sync_denylist<I, T>(mut self, denylist: I) -> Self
where
I: IntoIterator<Item = T>,
T: AsRef<str>,
{
self.sync_denylist.extend(
denylist
.into_iter()
.map(|it| StoreId::from(it.as_ref())),
);
self
}
#[must_use]
pub fn marshaler(mut self, marshaler: Box<dyn Marshaler>) -> Self {
self.default_marshaler = Some(marshaler);
self
}
#[must_use]
pub fn marshaler_of(mut self, id: impl AsRef<str>, marshaler: Box<dyn Marshaler>) -> Self {
let id = StoreId::from(id.as_ref());
self.marshaler_table.insert(id, marshaler);
self
}
#[must_use]
pub fn enable_debug_stores(mut self, yes: bool) -> Self {
self.debug_stores = yes;
self
}
#[must_use]
pub fn migration(mut self, id: impl Into<StoreId>, migration: Migration) -> Self {
self.migrator.add_migration(id.into(), migration);
self
}
#[must_use]
pub fn migrations<I>(mut self, id: impl Into<StoreId>, migrations: I) -> Self
where
I: IntoIterator<Item = Migration>,
{
self
.migrator
.add_migrations(id.into(), migrations);
self
}
#[must_use]
pub fn on_before_each_migration<F>(mut self, f: F) -> Self
where
F: Fn(MigrationContext) + Send + Sync + 'static,
{
self.migrator.on_before_each(f);
self
}
fn build_collection(self, handle: Handle<R>) -> Result<()> {
let mut builder = StoreCollection::<R, VueMarker>::builder()
.default_save_strategy(self.default_save_strategy)
.save_denylist(&self.save_denylist)
.sync_denylist(&self.sync_denylist)
.migrator(self.migrator)
.enable_debug_stores(self.debug_stores);
if let Some(path) = self.default_path {
builder = builder.path(path);
}
if let Some(on_load) = self.on_load {
builder = builder.on_load(on_load);
}
if let Some(duration) = self.autosave {
builder = builder.autosave(duration);
}
if let Some(marshaler) = self.default_marshaler {
builder = builder.marshaler(marshaler);
}
for (id, path) in self.path_table {
builder = builder.path_of(id, path);
}
for (id, marshaler) in self.marshaler_table {
builder = builder.marshaler_of(id, marshaler);
}
builder.build(handle, env!("CARGO_PKG_NAME"))
}
pub fn build(self) -> TauriPlugin<R> {
tauri::plugin::Builder::new("vue")
.setup(|app, api| setup(app, api, self))
.on_event(on_event)
.invoke_handler(tauri::generate_handler![
command::allow_save,
command::allow_sync,
command::clear_autosave,
command::deny_save,
command::deny_sync,
command::destroy,
command::get_default_save_strategy,
command::get_store_collection_path,
command::get_save_strategy,
command::get_store_ids,
command::get_store_path,
command::get_store_state,
command::load,
command::patch,
command::save,
command::save_all,
command::save_all_now,
command::save_now,
command::save_some,
command::save_some_now,
command::set_autosave,
command::set_save_strategy,
command::set_store_options,
command::unload
])
.build()
}
}
impl<R: Runtime> Default for Builder<R> {
fn default() -> Self {
Self {
default_path: None,
path_table: HashMap::new(),
default_marshaler: None,
marshaler_table: HashMap::new(),
default_save_strategy: SaveStrategy::default(),
autosave: None,
on_load: None,
save_denylist: HashSet::default(),
sync_denylist: HashSet::default(),
migrator: Migrator::default(),
debug_stores: true,
}
}
}
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
fn setup<R, D>(app: &AppHandle<R>, _api: PluginApi<R, D>, builder: Builder<R>) -> BoxResult<()>
where
R: Runtime,
D: DeserializeOwned,
{
let handle = Handle::new(app.clone());
builder.build_collection(handle)?;
Ok(())
}
#[cfg(any(target_os = "android", target_os = "ios"))]
fn setup<R, D>(_app: &AppHandle<R>, api: PluginApi<R, D>, builder: Builder<R>) -> BoxResult<()>
where
R: Runtime,
D: DeserializeOwned,
{
#[cfg(target_os = "android")]
let handle = api.register_android_plugin("com.plugin.vue", "VuePlugin")?;
#[cfg(target_os = "ios")]
let handle = api.register_ios_plugin(init_plugin_vue)?;
builder.build_collection(Handle::new(handle))?;
Ok(())
}
fn on_event<R>(app: &AppHandle<R>, event: &RunEvent)
where
R: Runtime,
{
if let RunEvent::Exit = event {
let _ = app.vue().0.on_exit();
}
}
pub fn init<R: Runtime>() -> TauriPlugin<R> {
Builder::default().build()
}