Skip to main content

perspt_store/store/
handle.rs

1use super::*;
2
3use std::sync::Mutex;
4
5/// Session store for SRBN persistence
6pub struct SessionStore {
7    pub(crate) conn: Mutex<Connection>,
8    database_path: PathBuf,
9}
10
11impl SessionStore {
12    /// Create a new session store with default path
13    pub fn new() -> Result<Self> {
14        let db_path = Self::default_db_path()?;
15        Self::open(&db_path)
16    }
17
18    /// Open a session store at the given path
19    pub fn open(path: &std::path::Path) -> Result<Self> {
20        let path = absolute_database_path(path)?;
21        // Ensure parent directory exists
22        if let Some(parent) = path.parent() {
23            std::fs::create_dir_all(parent)?;
24        }
25
26        let conn = Connection::open(&path).context("Failed to open DuckDB")?;
27        init_schema(&conn)?;
28
29        Ok(Self {
30            conn: Mutex::new(conn),
31            database_path: path,
32        })
33    }
34
35    /// Open a session store in read-only mode for concurrent dashboard reads.
36    ///
37    /// Uses `AccessMode::ReadOnly` so the dashboard can read alongside the
38    /// agent's write lock. Does **not** call `init_schema()` (a write op).
39    /// The database file must already exist.
40    pub fn open_read_only(path: &std::path::Path) -> Result<Self> {
41        let path = absolute_database_path(path)?;
42        let config = duckdb::Config::default()
43            .access_mode(duckdb::AccessMode::ReadOnly)
44            .context("Failed to configure DuckDB read-only mode")?;
45        let conn = Connection::open_with_flags(&path, config)
46            .context("Failed to open DuckDB in read-only mode")?;
47        Ok(Self {
48            conn: Mutex::new(conn),
49            database_path: path,
50        })
51    }
52
53    /// Absolute path of the database backing this handle.
54    pub fn database_path(&self) -> &std::path::Path {
55        &self.database_path
56    }
57
58    /// Get the default database path (~/.local/share/perspt/perspt.db or similar)
59    pub fn default_db_path() -> Result<PathBuf> {
60        perspt_core::paths::database_path().context("Could not determine platform data directory")
61    }
62}
63
64fn absolute_database_path(path: &std::path::Path) -> Result<PathBuf> {
65    if path.is_absolute() {
66        Ok(path.to_path_buf())
67    } else {
68        Ok(std::env::current_dir()?.join(path))
69    }
70}