use super::{OnLoadFn, StoreCollection, RESOURCE_ID};
use crate::collection::autosave::Autosave;
use crate::error::Result;
use crate::meta::Meta;
use crate::store::{SaveStrategy, Store, StoreId};
use dashmap::DashMap;
use std::collections::HashSet;
use std::path::{Path, PathBuf};
use std::sync::{Arc, Mutex};
use std::time::Duration;
use tauri::{Manager, Runtime};
#[cfg(feature = "plugin")]
use tauri::plugin::TauriPlugin;
#[cfg(feature = "unstable-migration")]
use crate::migration::{Migration, MigrationContext, Migrator};
#[cfg(tauri_store_tracing)]
use tracing::trace;
pub struct StoreCollectionBuilder<R: Runtime> {
path: Option<PathBuf>,
default_save_strategy: SaveStrategy,
autosave: Option<Duration>,
on_load: Option<Box<OnLoadFn<R>>>,
pretty: bool,
save_denylist: Option<HashSet<StoreId>>,
sync_denylist: Option<HashSet<StoreId>>,
#[cfg(feature = "unstable-migration")]
migrator: Migrator,
}
impl<R: Runtime> StoreCollectionBuilder<R> {
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn autosave(mut self, duration: Duration) -> Self {
self.autosave = Some(duration);
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>) -> Result<()> + Send + Sync + 'static,
{
self.on_load = Some(Box::new(f));
self
}
#[must_use]
pub fn path(mut self, path: impl AsRef<Path>) -> Self {
self.path = Some(path.as_ref().to_path_buf());
self
}
#[must_use]
pub fn pretty(mut self, yes: bool) -> Self {
self.pretty = yes;
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 = Some(
denylist
.into_iter()
.map(|it| StoreId::from(it.as_ref()))
.collect(),
);
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 = Some(
denylist
.into_iter()
.map(|it| StoreId::from(it.as_ref()))
.collect(),
);
self
}
#[must_use]
#[doc(hidden)]
#[cfg(feature = "unstable-migration")]
pub fn migrator(mut self, migrator: Migrator) -> Self {
self.migrator = migrator;
self
}
#[must_use]
#[cfg(feature = "unstable-migration")]
pub fn migration(mut self, id: impl Into<StoreId>, migration: Migration) -> Self {
self.migrator.add_migration(id.into(), migration);
self
}
#[must_use]
#[cfg(feature = "unstable-migration")]
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]
#[cfg(feature = "unstable-migration")]
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
}
#[cfg(feature = "plugin")]
pub fn build_plugin(self) -> TauriPlugin<R> {
crate::plugin::build(self)
}
#[doc(hidden)]
pub fn build<M>(mut self, app: &M, name: &str) -> Result<Arc<StoreCollection<R>>>
where
M: Manager<R>,
{
assert!(
RESOURCE_ID.get().is_none(),
"store collection is already initialized"
);
let app = app.app_handle();
let meta = Meta::read(app, name)?;
let path = meta
.inner
.path
.or_else(|| self.path.take())
.unwrap_or_else(|| {
app
.path()
.app_data_dir()
.expect("failed to resolve app data dir")
.join(name)
});
#[cfg(feature = "unstable-migration")]
if let Some(history) = meta.inner.migration_history {
self.migrator.history = history;
}
self.save_denylist = self.save_denylist.filter(|it| !it.is_empty());
self.sync_denylist = self.sync_denylist.filter(|it| !it.is_empty());
let collection = Arc::new(StoreCollection::<R> {
app: app.clone(),
name: Box::from(name),
path: Mutex::new(path),
stores: DashMap::new(),
on_load: self.on_load,
autosave: Mutex::new(Autosave::new(self.autosave)),
default_save_strategy: self.default_save_strategy,
save_denylist: self.save_denylist,
sync_denylist: self.sync_denylist,
pretty: self.pretty,
#[cfg(feature = "unstable-migration")]
migrator: Mutex::new(self.migrator),
});
#[cfg(tauri_store_tracing)]
trace!(?collection);
let rid = app
.resources_table()
.add_arc(Arc::clone(&collection));
let _ = RESOURCE_ID.set(rid);
collection.autosave.lock().unwrap().start(app);
Ok(collection)
}
}
impl<R: Runtime> Default for StoreCollectionBuilder<R> {
fn default() -> Self {
Self {
path: None,
default_save_strategy: SaveStrategy::Immediate,
autosave: None,
on_load: None,
pretty: false,
save_denylist: None,
sync_denylist: None,
#[cfg(feature = "unstable-migration")]
migrator: Migrator::default(),
}
}
}