config.rs (4700B)
1 //! Validated SQLite connection configuration. 2 3 use std::{ 4 path::{Path, PathBuf}, 5 time::Duration, 6 }; 7 8 use crate::{Error, OpenMode, Paths}; 9 use radroots_storage::{event::SourceGeneration, status::WriterPolicy}; 10 11 const DEFAULT_BUSY_TIMEOUT: Duration = Duration::from_secs(5); 12 const MIN_BUSY_TIMEOUT: Duration = Duration::from_millis(1); 13 const MAX_BUSY_TIMEOUT: Duration = Duration::from_secs(60); 14 15 /// Validated options for opening the runtime and private SQLite stores. 16 /// 17 /// Foreign-key enforcement is always enabled. Writable stores always use WAL; 18 /// neither invariant can be disabled through the public API. 19 #[derive(Clone, Debug, Eq, PartialEq)] 20 pub struct OpenOptions { 21 paths: Paths, 22 mode: OpenMode, 23 busy_timeout: Duration, 24 source_generation: Option<(SourceGeneration, u64)>, 25 backup_root: Option<PathBuf>, 26 } 27 28 impl OpenOptions { 29 /// Creates options with the governed five-second busy timeout. 30 pub fn new(paths: Paths, mode: OpenMode) -> Self { 31 Self { 32 paths, 33 mode, 34 busy_timeout: DEFAULT_BUSY_TIMEOUT, 35 source_generation: None, 36 backup_root: None, 37 } 38 } 39 40 /// Replaces the busy timeout after enforcing the supported bound. 41 pub fn with_busy_timeout(mut self, busy_timeout: Duration) -> Result<Self, Error> { 42 if !(MIN_BUSY_TIMEOUT..=MAX_BUSY_TIMEOUT).contains(&busy_timeout) { 43 return Err(Error::InvalidBusyTimeout { 44 minimum: MIN_BUSY_TIMEOUT, 45 maximum: MAX_BUSY_TIMEOUT, 46 actual: busy_timeout, 47 }); 48 } 49 self.busy_timeout = busy_timeout; 50 Ok(self) 51 } 52 53 /// Supplies the expected active source generation, or bootstraps it for a 54 /// fresh writable store, without reading hidden entropy or a wall clock. 55 pub fn with_source_generation( 56 mut self, 57 generation: SourceGeneration, 58 created_at_unix_ms: u64, 59 ) -> Result<Self, Error> { 60 if created_at_unix_ms == 0 || i64::try_from(created_at_unix_ms).is_err() { 61 return Err(Error::InvalidSourceGenerationTimestamp { 62 actual: created_at_unix_ms, 63 }); 64 } 65 self.source_generation = Some((generation, created_at_unix_ms)); 66 Ok(self) 67 } 68 69 /// Configures the existing host-owned directory where versioned backup 70 /// bundle directories will be staged and finalized. 71 pub fn with_backup_root(mut self, backup_root: impl Into<PathBuf>) -> Result<Self, Error> { 72 let backup_root = backup_root.into(); 73 crate::backup::validate_backup_root(&backup_root)?; 74 self.backup_root = Some(backup_root); 75 Ok(self) 76 } 77 78 /// Returns the two database paths owned by this backend instance. 79 pub fn paths(&self) -> &Paths { 80 &self.paths 81 } 82 83 /// Returns the requested lifecycle mode. 84 pub fn mode(&self) -> OpenMode { 85 self.mode 86 } 87 88 /// Returns the busy timeout applied to every owned connection. 89 pub fn busy_timeout(&self) -> Duration { 90 self.busy_timeout 91 } 92 93 /// Reports the non-configurable foreign-key policy. 94 pub fn foreign_keys_enabled(&self) -> bool { 95 true 96 } 97 98 /// Reports whether the mode requires WAL for writable connections. 99 pub fn wal_enabled(&self) -> bool { 100 self.mode.is_writable() 101 } 102 103 /// Reports the mandatory writer coordination policy for this mode. 104 pub fn writer_policy(&self) -> WriterPolicy { 105 if self.mode.is_writable() { 106 WriterPolicy::AdvisoryProcessLock 107 } else { 108 WriterPolicy::NoWriter 109 } 110 } 111 112 /// Returns the optional host-supplied source generation expectation. 113 pub fn source_generation(&self) -> Option<SourceGeneration> { 114 self.source_generation.map(|(generation, _)| generation) 115 } 116 117 /// Returns the host-supplied creation time paired with the generation. 118 pub fn source_generation_created_at_unix_ms(&self) -> Option<u64> { 119 self.source_generation.map(|(_, created_at)| created_at) 120 } 121 122 /// Returns the optional host-owned backup root. 123 pub fn backup_root(&self) -> Option<&Path> { 124 self.backup_root.as_deref() 125 } 126 127 pub(crate) const fn source_generation_bootstrap(&self) -> Option<(SourceGeneration, u64)> { 128 self.source_generation 129 } 130 131 /// Validates current filesystem state without creating or modifying files. 132 pub fn validate_filesystem(&self) -> Result<(), Error> { 133 self.paths.validate_filesystem(self.mode)?; 134 if let Some(backup_root) = &self.backup_root { 135 crate::backup::validate_backup_root(backup_root)?; 136 } 137 Ok(()) 138 } 139 }