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")
44 .map_err(db_err)?;
45 let _ = conn.pragma_update(None, "journal_mode", "WAL");
47 conn.pragma_update(None, "synchronous", "NORMAL")
48 .map_err(db_err)?;
49 conn.pragma_update(None, "temp_store", "MEMORY")
50 .map_err(db_err)?;
51
52 let catalog = Self {
53 conn: Mutex::new(conn),
54 path,
55 };
56 migrations::run_pending(&catalog)?;
57 Ok(catalog)
58 }
59
60 pub fn lock(&self) -> MutexGuard<'_, Connection> {
63 self.conn
64 .lock()
65 .expect("catalog connection mutex poisoned — a repository panicked mid-write")
66 }
67
68 pub fn path(&self) -> &Path {
70 &self.path
71 }
72
73 pub fn schema_version(&self) -> OrbokResult<i64> {
75 let conn = self.lock();
76 let version = conn
77 .query_row(
78 "SELECT COALESCE(MAX(version), 0) FROM schema_migrations",
79 [],
80 |row| row.get(0),
81 )
82 .map_err(db_err)?;
83 Ok(version)
84 }
85}
86
87pub(crate) fn db_err(e: rusqlite::Error) -> OrbokError {
89 OrbokError::Database(e.to_string())
90}