lib

Core libraries for Radroots
git clone https://radroots.dev/git/lib.git
Log | Files | Refs | README

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 }