feat: gate the store behind a feature and document the schema as a contract

This commit is contained in:
randogoth 2026-09-28 23:41:11 +03:00
parent 8fb5661b53
commit aa3950bc80
6 changed files with 122 additions and 16 deletions

View file

@ -84,12 +84,29 @@ pub type ContactRow = (String, [u8; KEY_LEN], bool, Option<i64>);
pub const SCHEMA_VERSION: i32 = 1;
impl Store {
/// Opens the store at `path`. SQLite's own `:memory:` path works too;
/// `open_in_memory` spells that case without the magic string.
pub fn open(path: &Path) -> Result<Store, Error> {
let db = Connection::open(path)?;
Store::init(Connection::open(path)?)
}
/// An in-memory store: the same schema, version and contract, gone with
/// the handle. For tests, foreign importers assembling rows in code,
/// and FFI round-trips that never touch disk.
pub fn open_in_memory() -> Result<Store, Error> {
Store::init(Connection::open_in_memory()?)
}
fn init(db: Connection) -> Result<Store, Error> {
// WAL keeps concurrent readers cheap while a writer commits, and a
// short busy timeout absorbs the contention the mutex cannot (a
// second connection in the same process, or a CLI run alongside).
db.pragma_update(None, "journal_mode", "WAL")?;
// An in-memory database journals in memory, so WAL applies to
// files only.
let journal: String = db.query_row("PRAGMA journal_mode", [], |row| row.get(0))?;
if journal != "memory" {
db.pragma_update(None, "journal_mode", "WAL")?;
}
db.busy_timeout(std::time::Duration::from_secs(5))?;
let version: i32 = db.query_row("PRAGMA user_version", [], |row| row.get(0))?;
if version == 0 {
@ -706,6 +723,22 @@ mod tests {
}
}
#[test]
fn in_memory_store_round_trips() {
let store = Store::open_in_memory().expect("memory store opens");
let addr = Address::parse("alice@example.org").unwrap();
store.set_account(&addr).unwrap();
assert_eq!(
store.account().unwrap().unwrap().short(),
"alice@example.org"
);
store.store_inbox(&[1u8; 32], b"envelope", 5, false).unwrap();
assert_eq!(store.mail("inbox").unwrap().len(), 1);
// The same handle through the path spelling.
let path = std::path::Path::new(":memory:");
assert!(Store::open(path).is_ok());
}
#[test]
fn store_is_shareable_across_threads() {
fn assert_send_sync<T: Send + Sync>() {}