use rusqlite::Connection; use std::path::Path; use std::sync::{Arc, Mutex}; /// Cheap-to-clone handle to a single SQLite connection protected by a mutex. /// /// Using a single shared connection (rather than a pool) is correct for /// desktop use: SQLite's WAL mode allows concurrent reads from the OS level, /// and the mutex ensures we never issue overlapping writes from Tauri's /// async command threads. #[derive(Clone)] pub struct Database { conn: Arc>, } impl Database { /// Open (or create) the SQLite database at `path` and apply PRAGMAs. pub fn open(path: &Path) -> Result> { let conn = Connection::open(path)?; conn.execute_batch( "PRAGMA journal_mode=WAL; PRAGMA busy_timeout=5000; PRAGMA foreign_keys=ON;", )?; Ok(Self { conn: Arc::new(Mutex::new(conn)), }) } /// Acquire a lock on the underlying connection. /// /// Panics if the mutex was poisoned (which only happens if a thread /// panicked while holding the lock — a non-recoverable situation anyway). pub fn conn(&self) -> std::sync::MutexGuard<'_, Connection> { self.conn.lock().unwrap() } /// Run all embedded SQL migrations. /// /// The migration file is compiled into the binary via `include_str!`, /// so there is no runtime file-system dependency on it. pub fn migrate(&self) -> Result<(), Box> { let conn = self.conn(); conn.execute_batch(include_str!("migrations/001_initial.sql"))?; Ok(()) } }