tauri_store/collection/
builder.rs1use 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
22pub 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 pub fn new() -> Self {
39 Self::default()
40 }
41
42 #[must_use]
44 pub fn autosave(mut self, duration: Duration) -> Self {
45 self.autosave = Some(duration);
46 self
47 }
48
49 #[must_use]
51 pub fn default_save_strategy(mut self, strategy: SaveStrategy) -> Self {
52 self.default_save_strategy = strategy;
53 self
54 }
55
56 #[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 #[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 #[must_use]
75 pub fn pretty(mut self, yes: bool) -> Self {
76 self.pretty = yes;
77 self
78 }
79
80 #[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 #[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 #[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 #[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 #[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 #[cfg(feature = "plugin")]
161 pub fn build_plugin(self) -> TauriPlugin<R> {
162 crate::plugin::build(self)
163 }
164
165 #[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}