use std::path::Path;
use rusqlite::Connection;
#[derive(Debug, thiserror::Error)]
pub enum StoreError {
#[error("sqlite error: {0}")]
Sqlite(#[from] rusqlite::Error),
}
pub struct Store {
conn: Connection,
}
const SCHEMA: &str = "
CREATE TABLE IF NOT EXISTS nodes (
id INTEGER PRIMARY KEY,
kind TEXT NOT NULL,
name TEXT NOT NULL,
path TEXT,
blob_hash TEXT
);
CREATE TABLE IF NOT EXISTS edges (
id INTEGER PRIMARY KEY,
src INTEGER NOT NULL REFERENCES nodes(id),
dst INTEGER NOT NULL REFERENCES nodes(id),
kind TEXT NOT NULL,
provenance TEXT NOT NULL CHECK (provenance IN ('derived','authored','inferred')),
confidence REAL
);
CREATE INDEX IF NOT EXISTS idx_edges_src ON edges(src);
CREATE INDEX IF NOT EXISTS idx_edges_dst ON edges(dst);
";
impl Store {
pub fn open(path: &Path) -> Result<Self, StoreError> {
let conn = Connection::open(path)?;
conn.execute_batch(SCHEMA)?;
Ok(Self { conn })
}
pub fn open_in_memory() -> Result<Self, StoreError> {
let conn = Connection::open_in_memory()?;
conn.execute_batch(SCHEMA)?;
Ok(Self { conn })
}
pub fn node_count(&self) -> Result<u64, StoreError> {
let n: i64 = self
.conn
.query_row("SELECT COUNT(*) FROM nodes", [], |r| r.get(0))?;
Ok(u64::try_from(n).unwrap_or(0))
}
}
#[cfg(test)]
mod tests {
use super::Store;
#[test]
fn open_in_memory_applies_schema() {
let store = Store::open_in_memory().expect("open");
assert_eq!(store.node_count().expect("count"), 0);
}
}