tauri_store/collection/
builder.rs

1use super::{OnLoadFn, StoreCollection, RESOURCE_ID};
2use crate::collection::autosave::Autosave;
3use crate::error::Result;
4use crate::meta::Meta;
5use crate::store::{SaveStrategy, Store, StoreId};
6use dashmap::DashMap;
7use std::collections::HashSet;
8use std::path::{Path, PathBuf};
9use std::sync::{Arc, Mutex};
10use std::time::Duration;
11use tauri::{Manager, Runtime};
12
13#[cfg(feature = "plugin")]
14use tauri::plugin::TauriPlugin;
15
16#[cfg(feature = "unstable-migration")]
17use crate::migration::{Migration, MigrationContext, Migrator};
18
19#[cfg(tauri_store_tracing)]
20use tracing::trace;
21
22/// Builder for the [`StoreCollection`](crate::collection::StoreCollection).
23pub struct StoreCollectionBuilder<R: Runtime> {
24  path: Option<PathBuf>,
25  default_save_strategy: SaveStrategy,
26  autosave: Option<Duration>,
27  on_load: Option<Box<OnLoadFn<R>>>,
28  pretty: bool,
29  save_denylist: Option<HashSet<StoreId>>,
30  sync_denylist: Option<HashSet<StoreId>>,
31
32  #[cfg(feature = "unstable-migration")]
33  migrator: Migrator,
34}
35
36impl<R: Runtime> StoreCollectionBuilder<R> {
37  /// Creates a new builder instance with default values.
38  pub fn new() -> Self {
39    Self::default()
40  }
41
42  /// Sets the autosave interval for all stores.
43  #[must_use]
44  pub fn autosave(mut self, duration: Duration) -> Self {
45    self.autosave = Some(duration);
46    self
47  }
48
49  /// Sets the default save strategy to be used by the stores.
50  #[must_use]
51  pub fn default_save_strategy(mut self, strategy: SaveStrategy) -> Self {
52    self.default_save_strategy = strategy;
53    self
54  }
55
56  /// Registers a closure to be called when a store is loaded.
57  #[must_use]
58  pub fn on_load<F>(mut self, f: F) -> Self
59  where
60    F: Fn(&Store<R>) -> Result<()> + Send + Sync + 'static,
61  {
62    self.on_load = Some(Box::new(f));
63    self
64  }
65
66  /// Directory where the stores will be saved.
67  #[must_use]
68  pub fn path(mut self, path: impl AsRef<Path>) -> Self {
69    self.path = Some(path.as_ref().to_path_buf());
70    self
71  }
72
73  /// Sets whether the store files should be pretty printed.
74  #[must_use]
75  pub fn pretty(mut self, yes: bool) -> Self {
76    self.pretty = yes;
77    self
78  }
79
80  /// Sets a list of stores that should not be saved to disk.
81  #[must_use]
82  pub fn save_denylist<I, T>(mut self, denylist: I) -> Self
83  where
84    I: IntoIterator<Item = T>,
85    T: AsRef<str>,
86  {
87    self.save_denylist = Some(
88      denylist
89        .into_iter()
90        .map(|it| StoreId::from(it.as_ref()))
91        .collect(),
92    );
93
94    self
95  }
96
97  /// Sets a list of stores that should not be synchronized across windows.
98  #[must_use]
99  pub fn sync_denylist<I, T>(mut self, denylist: I) -> Self
100  where
101    I: IntoIterator<Item = T>,
102    T: AsRef<str>,
103  {
104    self.sync_denylist = Some(
105      denylist
106        .into_iter()
107        .map(|it| StoreId::from(it.as_ref()))
108        .collect(),
109    );
110
111    self
112  }
113
114  #[must_use]
115  #[doc(hidden)]
116  #[cfg(feature = "unstable-migration")]
117  pub fn migrator(mut self, migrator: Migrator) -> Self {
118    self.migrator = migrator;
119    self
120  }
121
122  /// Defines a migration for a store.
123  #[must_use]
124  #[cfg(feature = "unstable-migration")]
125  pub fn migration(mut self, id: impl Into<StoreId>, migration: Migration) -> Self {
126    self.migrator.add_migration(id.into(), migration);
127    self
128  }
129
130  /// Defines multiple migrations for a store.
131  #[must_use]
132  #[cfg(feature = "unstable-migration")]
133  pub fn migrations<I>(mut self, id: impl Into<StoreId>, migrations: I) -> Self
134  where
135    I: IntoIterator<Item = Migration>,
136  {
137    self
138      .migrator
139      .add_migrations(id.into(), migrations);
140
141    self
142  }
143
144  /// Sets a closure to be called before each migration step.
145  #[must_use]
146  #[cfg(feature = "unstable-migration")]
147  pub fn on_before_each_migration<F>(mut self, f: F) -> Self
148  where
149    F: Fn(MigrationContext) + Send + Sync + 'static,
150  {
151    self.migrator.on_before_each(f);
152    self
153  }
154
155  /// Initializes the plugin with a [`StoreCollection`](crate::collection::StoreCollection).
156  ///
157  /// # Panics
158  ///
159  /// Panics if a store collection is already initialized.
160  #[cfg(feature = "plugin")]
161  pub fn build_plugin(self) -> TauriPlugin<R> {
162    crate::plugin::build(self)
163  }
164
165  /// Builds the [`StoreCollection`](crate::collection::StoreCollection).
166  ///
167  /// # Panics
168  ///
169  /// Panics if a store collection is already initialized.
170  #[doc(hidden)]
171  pub fn build<M>(mut self, app: &M, name: &str) -> Result<Arc<StoreCollection<R>>>
172  where
173    M: Manager<R>,
174  {
175    assert!(
176      RESOURCE_ID.get().is_none(),
177      "store collection is already initialized"
178    );
179
180    let app = app.app_handle();
181    let meta = Meta::read(app, name)?;
182
183    let path = meta
184      .inner
185      .path
186      .or_else(|| self.path.take())
187      .unwrap_or_else(|| {
188        app
189          .path()
190          .app_data_dir()
191          .expect("failed to resolve app data dir")
192          .join(name)
193      });
194
195    #[cfg(feature = "unstable-migration")]
196    if let Some(history) = meta.inner.migration_history {
197      self.migrator.history = history;
198    }
199
200    self.save_denylist = self.save_denylist.filter(|it| !it.is_empty());
201    self.sync_denylist = self.sync_denylist.filter(|it| !it.is_empty());
202
203    let collection = Arc::new(StoreCollection::<R> {
204      app: app.clone(),
205      name: Box::from(name),
206      path: Mutex::new(path),
207      stores: DashMap::new(),
208      on_load: self.on_load,
209      autosave: Mutex::new(Autosave::new(self.autosave)),
210      default_save_strategy: self.default_save_strategy,
211      save_denylist: self.save_denylist,
212      sync_denylist: self.sync_denylist,
213      pretty: self.pretty,
214
215      #[cfg(feature = "unstable-migration")]
216      migrator: Mutex::new(self.migrator),
217    });
218
219    #[cfg(tauri_store_tracing)]
220    trace!(?collection);
221
222    let rid = app
223      .resources_table()
224      .add_arc(Arc::clone(&collection));
225
226    let _ = RESOURCE_ID.set(rid);
227
228    collection.autosave.lock().unwrap().start(app);
229
230    Ok(collection)
231  }
232}
233
234impl<R: Runtime> Default for StoreCollectionBuilder<R> {
235  fn default() -> Self {
236    Self {
237      path: None,
238      default_save_strategy: SaveStrategy::Immediate,
239      autosave: None,
240      on_load: None,
241      pretty: false,
242      save_denylist: None,
243      sync_denylist: None,
244
245      #[cfg(feature = "unstable-migration")]
246      migrator: Migrator::default(),
247    }
248  }
249}