1use crate::migrations;
9use orbok_core::{OrbokError, OrbokResult};
10use rusqlite::Connection;
11use std::path::{Path, PathBuf};
12use std::sync::{Mutex, MutexGuard};
13
14pub const CATALOG_FILE_NAME: &str = "orbok-catalog.sqlite3";
16
17pub const CACHE_FILE_NAME: &str = "orbok-cache.sqlite3";
19
20pub struct Catalog {
22 conn: Mutex<Connection>,
23 path: PathBuf,
24}
25
26impl Catalog {
27 pub fn open(path: impl AsRef<Path>) -> OrbokResult<Self> {
31 let path = path.as_ref().to_path_buf();
32 let conn = Connection::open(&path).map_err(db_err)?;
33 Self::from_connection(conn, path)
34 }
35
36 pub fn open_in_memory() -> OrbokResult<Self> {
38 let conn = Connection::open_in_memory().map_err(db_err)?;
39 Self::from_connection(conn, PathBuf::from(":memory:"))
40 }
41
42 fn from_connection(conn: Connection, path: PathBuf) -> OrbokResult<Self> {
43 conn.pragma_update(None, "foreign_keys", "ON").map_err(db_err)?;
44 let _ = conn.pragma_update(None, "journal_mode", "WAL");
46 conn.pragma_update(None, "synchronous", "NORMAL").map_err(db_err)?;
47 conn.pragma_update(None, "temp_store", "MEMORY").map_err(db_err)?;
48
49 let catalog = Self {
50 conn: Mutex::new(conn),
51 path,
52 };
53 migrations::run_pending(&catalog)?;
54 Ok(catalog)
55 }
56
57 pub fn lock(&self) -> MutexGuard<'_, Connection> {
60 self.conn
61 .lock()
62 .expect("catalog connection mutex poisoned — a repository panicked mid-write")
63 }
64
65 pub fn path(&self) -> &Path {
67 &self.path
68 }
69
70 pub fn schema_version(&self) -> OrbokResult<i64> {
72 let conn = self.lock();
73 let version = conn
74 .query_row(
75 "SELECT COALESCE(MAX(version), 0) FROM schema_migrations",
76 [],
77 |row| row.get(0),
78 )
79 .map_err(db_err)?;
80 Ok(version)
81 }
82}
83
84pub(crate) fn db_err(e: rusqlite::Error) -> OrbokError {
86 OrbokError::Database(e.to_string())
87}