1use std::collections::HashMap;
2use std::path::{Path, PathBuf};
3use std::sync::Arc;
4use std::time::Duration;
5
6#[cfg(feature = "encryption")]
7use bonsaidb_core::document::KeyId;
8use bonsaidb_core::permissions::Permissions;
9use bonsaidb_core::schema::{Schema, SchemaName};
10use sysinfo::{CpuRefreshKind, RefreshKind, System, SystemExt};
11
12use crate::storage::{DatabaseOpener, StorageSchemaOpener};
13#[cfg(feature = "encryption")]
14use crate::vault::AnyVaultKeyStorage;
15use crate::Error;
16
17#[cfg(feature = "password-hashing")]
18mod argon;
19#[cfg(feature = "password-hashing")]
20pub use argon::*;
21
22#[derive(Clone)]
24#[non_exhaustive]
25pub struct StorageConfiguration {
26 pub path: Option<PathBuf>,
28
29 pub memory_only: bool,
33
34 pub unique_id: Option<u64>,
39
40 #[cfg(feature = "encryption")]
50 pub vault_key_storage: Option<Arc<dyn AnyVaultKeyStorage>>,
51
52 #[cfg(feature = "encryption")]
57 pub default_encryption_key: Option<KeyId>,
58
59 pub workers: Tasks,
61
62 pub views: Views,
64
65 pub key_value_persistence: KeyValuePersistence,
67
68 #[cfg(feature = "compression")]
70 pub default_compression: Option<Compression>,
71
72 pub authenticated_permissions: Permissions,
74
75 #[cfg(feature = "password-hashing")]
77 pub argon: ArgonConfiguration,
78
79 pub(crate) initial_schemas: HashMap<SchemaName, Arc<dyn DatabaseOpener>>,
80}
81
82impl Default for StorageConfiguration {
83 fn default() -> Self {
84 let system_specs = RefreshKind::new()
85 .with_cpu(CpuRefreshKind::new())
86 .with_memory();
87 let mut system = System::new_with_specifics(system_specs);
88 system.refresh_specifics(system_specs);
89 Self {
90 path: None,
91 memory_only: false,
92 unique_id: None,
93 #[cfg(feature = "encryption")]
94 vault_key_storage: None,
95 #[cfg(feature = "encryption")]
96 default_encryption_key: None,
97 #[cfg(feature = "compression")]
98 default_compression: None,
99 workers: Tasks::default_for(&system),
100 views: Views::default(),
101 key_value_persistence: KeyValuePersistence::default(),
102 authenticated_permissions: Permissions::default(),
103 #[cfg(feature = "password-hashing")]
104 argon: ArgonConfiguration::default_for(&system),
105 initial_schemas: HashMap::default(),
106 }
107 }
108}
109
110impl std::fmt::Debug for StorageConfiguration {
111 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
112 let mut schemas = self.initial_schemas.keys().collect::<Vec<_>>();
113 schemas.sort();
114 let mut f = f.debug_struct("StorageConfiguration");
115 f.field("path", &self.path)
116 .field("memory_only", &self.memory_only)
117 .field("unique_id", &self.unique_id)
118 .field("workers", &self.workers)
119 .field("views", &self.views)
120 .field("key_value_persistence", &self.key_value_persistence)
121 .field("authenticated_permissions", &self.authenticated_permissions)
122 .field("initial_schemas", &schemas);
123
124 #[cfg(feature = "encryption")]
125 f.field("vault_key_storage", &self.vault_key_storage)
126 .field("default_encryption_key", &self.default_encryption_key);
127
128 #[cfg(feature = "compression")]
129 f.field("default_compression", &self.default_compression);
130
131 #[cfg(feature = "password-hashing")]
132 f.field("argon", &self.argon);
133
134 f.finish()
135 }
136}
137
138impl StorageConfiguration {
139 pub fn register_schema<S: Schema>(&mut self) -> Result<(), Error> {
141 self.initial_schemas
143 .insert(S::schema_name(), Arc::new(StorageSchemaOpener::<S>::new()?));
144 Ok(())
145 }
146}
147
148#[derive(Debug, Clone)]
150pub struct Tasks {
151 pub worker_count: usize,
155
156 pub parallelization: usize,
160}
161
162impl SystemDefault for Tasks {
163 fn default_for(system: &System) -> Self {
164 let num_cpus = system
165 .physical_core_count()
166 .unwrap_or(0)
167 .max(system.cpus().len())
168 .max(1);
169 Self {
170 worker_count: num_cpus * 2,
171 parallelization: num_cpus,
172 }
173 }
174}
175
176#[derive(Clone, Debug, Default)]
178pub struct Views {
179 pub check_integrity_on_open: bool,
185}
186
187#[derive(Debug, Clone)]
236#[must_use]
237pub struct KeyValuePersistence(KeyValuePersistenceInner);
238
239#[derive(Debug, Clone)]
240enum KeyValuePersistenceInner {
241 Immediate,
242 Lazy(Vec<PersistenceThreshold>),
243}
244
245impl Default for KeyValuePersistence {
246 fn default() -> Self {
248 Self::immediate()
249 }
250}
251
252impl KeyValuePersistence {
253 pub const fn immediate() -> Self {
255 Self(KeyValuePersistenceInner::Immediate)
256 }
257
258 pub fn lazy<II>(rules: II) -> Self
260 where
261 II: IntoIterator<Item = PersistenceThreshold>,
262 {
263 let mut rules = rules.into_iter().collect::<Vec<_>>();
264 rules.sort_by(|a, b| a.number_of_changes.cmp(&b.number_of_changes));
265 Self(KeyValuePersistenceInner::Lazy(rules))
266 }
267
268 #[must_use]
270 pub fn should_commit(
271 &self,
272 number_of_changes: usize,
273 elapsed_since_last_commit: Duration,
274 ) -> bool {
275 self.duration_until_next_commit(number_of_changes, elapsed_since_last_commit)
276 == Some(Duration::ZERO)
277 }
278
279 pub(crate) fn duration_until_next_commit(
280 &self,
281 number_of_changes: usize,
282 elapsed_since_last_commit: Duration,
283 ) -> Option<Duration> {
284 if number_of_changes == 0 {
285 None
286 } else {
287 match &self.0 {
288 KeyValuePersistenceInner::Immediate => Some(Duration::ZERO),
289 KeyValuePersistenceInner::Lazy(rules) => {
290 let mut shortest_duration = Duration::MAX;
291 for rule in rules
292 .iter()
293 .take_while(|rule| rule.number_of_changes <= number_of_changes)
294 {
295 let remaining_time =
296 rule.duration.saturating_sub(elapsed_since_last_commit);
297 shortest_duration = shortest_duration.min(remaining_time);
298
299 if shortest_duration == Duration::ZERO {
300 break;
301 }
302 }
303 (shortest_duration < Duration::MAX).then_some(shortest_duration)
304 }
305 }
306 }
307 }
308}
309
310#[derive(Debug, Copy, Clone)]
317#[must_use]
318pub struct PersistenceThreshold {
319 pub number_of_changes: usize,
321 pub duration: Duration,
323}
324
325impl PersistenceThreshold {
326 pub const fn after_changes(number_of_changes: usize) -> Self {
328 Self {
329 number_of_changes,
330 duration: Duration::ZERO,
331 }
332 }
333
334 pub const fn and_duration(mut self, duration: Duration) -> Self {
336 self.duration = duration;
337 self
338 }
339}
340
341pub trait Builder: Sized {
343 #[must_use]
345 fn new<P: AsRef<Path>>(path: P) -> Self
346 where
347 Self: Default,
348 {
349 Self::default().path(path)
350 }
351 fn with_schema<S: Schema>(self) -> Result<Self, Error>;
353
354 #[must_use]
356 fn memory_only(self) -> Self;
357 #[must_use]
359 fn path<P: AsRef<Path>>(self, path: P) -> Self;
360 #[must_use]
362 fn unique_id(self, unique_id: u64) -> Self;
363 #[cfg(feature = "encryption")]
365 #[must_use]
366 fn vault_key_storage<VaultKeyStorage: AnyVaultKeyStorage>(
367 self,
368 key_storage: VaultKeyStorage,
369 ) -> Self;
370 #[cfg(feature = "encryption")]
372 #[must_use]
373 fn default_encryption_key(self, key: KeyId) -> Self;
374 #[must_use]
376 fn tasks_worker_count(self, worker_count: usize) -> Self;
377 #[must_use]
379 fn tasks_parallelization(self, parallelization: usize) -> Self;
380 #[must_use]
382 fn check_view_integrity_on_open(self, check: bool) -> Self;
383 #[cfg(feature = "compression")]
385 #[must_use]
386 fn default_compression(self, compression: Compression) -> Self;
387 #[must_use]
389 fn key_value_persistence(self, persistence: KeyValuePersistence) -> Self;
390 #[must_use]
392 fn authenticated_permissions<P: Into<Permissions>>(self, authenticated_permissions: P) -> Self;
393 #[cfg(feature = "password-hashing")]
395 #[must_use]
396 fn argon(self, argon: ArgonConfiguration) -> Self;
397}
398
399impl Builder for StorageConfiguration {
400 fn with_schema<S: Schema>(mut self) -> Result<Self, Error> {
401 self.register_schema::<S>()?;
402 Ok(self)
403 }
404
405 fn memory_only(mut self) -> Self {
406 self.memory_only = true;
407 self
408 }
409
410 fn path<P: AsRef<Path>>(mut self, path: P) -> Self {
411 self.path = Some(path.as_ref().to_owned());
412 self
413 }
414
415 fn unique_id(mut self, unique_id: u64) -> Self {
416 self.unique_id = Some(unique_id);
417 self
418 }
419
420 #[cfg(feature = "encryption")]
421 fn vault_key_storage<VaultKeyStorage: AnyVaultKeyStorage>(
422 mut self,
423 key_storage: VaultKeyStorage,
424 ) -> Self {
425 self.vault_key_storage = Some(Arc::new(key_storage));
426 self
427 }
428
429 #[cfg(feature = "encryption")]
430 fn default_encryption_key(mut self, key: KeyId) -> Self {
431 self.default_encryption_key = Some(key);
432 self
433 }
434
435 #[cfg(feature = "compression")]
436 fn default_compression(mut self, compression: Compression) -> Self {
437 self.default_compression = Some(compression);
438 self
439 }
440
441 fn tasks_worker_count(mut self, worker_count: usize) -> Self {
442 self.workers.worker_count = worker_count;
443 self
444 }
445
446 fn tasks_parallelization(mut self, parallelization: usize) -> Self {
447 self.workers.parallelization = parallelization;
448 self
449 }
450
451 fn check_view_integrity_on_open(mut self, check: bool) -> Self {
452 self.views.check_integrity_on_open = check;
453 self
454 }
455
456 fn key_value_persistence(mut self, persistence: KeyValuePersistence) -> Self {
457 self.key_value_persistence = persistence;
458 self
459 }
460
461 fn authenticated_permissions<P: Into<Permissions>>(
462 mut self,
463 authenticated_permissions: P,
464 ) -> Self {
465 self.authenticated_permissions = authenticated_permissions.into();
466 self
467 }
468
469 #[cfg(feature = "password-hashing")]
470 fn argon(mut self, argon: ArgonConfiguration) -> Self {
471 self.argon = argon;
472 self
473 }
474}
475
476pub(crate) trait SystemDefault: Sized {
477 fn default_for(system: &System) -> Self;
478 fn default() -> Self {
479 let system_specs = RefreshKind::new()
480 .with_cpu(CpuRefreshKind::new())
481 .with_memory();
482 let mut system = System::new_with_specifics(system_specs);
483 system.refresh_specifics(system_specs);
484 Self::default_for(&system)
485 }
486}
487
488#[derive(Debug, Clone, Copy)]
490pub enum Compression {
491 Lz4 = 1,
496}
497
498impl Compression {
499 #[must_use]
500 #[cfg(feature = "compression")]
501 pub(crate) fn from_u8(value: u8) -> Option<Self> {
502 match value {
503 1 => Some(Self::Lz4),
504 _ => None,
505 }
506 }
507}