Skip to main content

scud/db/
mod.rs

1//! SQLite database for event logging, transcript storage, and session history.
2
3use anyhow::Result;
4use rusqlite::{Connection, OpenFlags};
5use std::path::{Path, PathBuf};
6use std::sync::Mutex;
7
8pub mod events;
9pub mod schema;
10pub mod sessions;
11pub mod transcripts;
12
13/// Database connection wrapper with lazy initialization
14pub struct Database {
15    path: PathBuf,
16    conn: Mutex<Option<Connection>>,
17}
18
19impl Database {
20    pub fn new(project_root: &Path) -> Self {
21        let path = project_root.join(".scud").join("scud.db");
22        Self {
23            path,
24            conn: Mutex::new(None),
25        }
26    }
27
28    /// Get or create database connection
29    pub fn connection(&self) -> Result<std::sync::MutexGuard<'_, Option<Connection>>> {
30        let mut guard = self.conn.lock().unwrap();
31        if guard.is_none() {
32            let conn = Connection::open_with_flags(
33                &self.path,
34                OpenFlags::SQLITE_OPEN_READ_WRITE
35                    | OpenFlags::SQLITE_OPEN_CREATE
36                    | OpenFlags::SQLITE_OPEN_FULL_MUTEX,
37            )?;
38            // Enable WAL mode for better concurrent access
39            // Note: foreign_keys not enforced - events can be written before session records
40            conn.execute_batch("PRAGMA journal_mode=WAL;")?;
41            *guard = Some(conn);
42        }
43        Ok(guard)
44    }
45
46    /// Initialize database with schema
47    pub fn initialize(&self) -> Result<()> {
48        let guard = self.connection()?;
49        let conn = guard.as_ref().unwrap();
50        schema::create_tables(conn)?;
51        Ok(())
52    }
53
54    pub fn path(&self) -> &Path {
55        &self.path
56    }
57}