- SqliteOpts (poll_interval: Option<Duration>, None = 1 ms shipping default ADR-023 §4; max_readers, DEFAULT_MAX_READERS = 8; opts exemption from non_exhaustive per ADR-017 §3) - open(path, opts) -> Box<dyn Store>: writer slot, reader pool, SharedUpdateWatcher with fallible spawn mapped W-2-style into Error::Database; plain-path posture (ADR-023 §3) pinned by test - Substrate delta D-30: open_conn_bootstrapped — every connection (writer and pooled readers) carries the full bootstrap surface (pragmas, notify, alkstore functions, schema) per engine-sqlite.md; lineage opened readers pragmas-only. Registered in PROVENANCE.md - spawn_blocking seam helper + error mappings (string/rusqlite -> Database, source chains preserved); helper cfg(test)-gated until sqlite-engine-seam-tx wires the trait impls - Store trait stubbed with Database errors (no panics); close()/Drop join the watcher, clear subscribers (death-guard), close pool+writer - 15 new tests (open boot, watcher fanout/death-close, cadence accounting, plain-path literal filename, :memory:, pool bounding, drop teardown, seam smoke incl. panic mapping); gates green
46 lines
1.9 KiB
Rust
46 lines
1.9 KiB
Rust
//! The `spawn_blocking` seam (ADR-003, ADR-007) and the error
|
|
//! mapping — the two engine-wide postures established here that every
|
|
//! later mechanism task reuses.
|
|
//!
|
|
//! - Every engine call round-trips a blocking thread: rusqlite
|
|
//! connections are not `Send`-across-await, and the sync substrate
|
|
//! bridges at the trait seam (the family REQ-TTY-01 posture).
|
|
//! - Substrate errors map into the contract taxonomy's opaque
|
|
//! [`Error::Database`](alkstore::Error) fallback with the source
|
|
//! chain preserved (ADR-008 §5). No panics cross the seam: a panic
|
|
//! inside a bridged closure is the `JoinError` arm's `Database`
|
|
//! mapping, family-standard no-panics discipline maintained.
|
|
|
|
use alkstore::Error;
|
|
|
|
/// The blocking bridge every trait method's substrate round trip goes
|
|
/// through. Private: the seam is engine-internal — consumers meet it
|
|
/// only through the async trait surface. `cfg(test)`-gated until the
|
|
/// seam task (`sqlite-engine-seam-tx`) wires the trait impls; the
|
|
/// helper's shape and error path are pinned by this wave's smoke test.
|
|
#[cfg(test)]
|
|
pub(crate) async fn blocking<T, F>(f: F) -> Result<T, Error>
|
|
where
|
|
T: Send + 'static,
|
|
F: FnOnce() -> Result<T, Error> + Send + 'static,
|
|
{
|
|
match tokio::task::spawn_blocking(f).await {
|
|
Ok(result) => result,
|
|
Err(e) => Err(database_error(format!("blocking task failed: {e}"))),
|
|
}
|
|
}
|
|
|
|
/// Map a substrate string failure into the contract taxonomy. The
|
|
/// substrate is contract-blind — its fallible API surfaces strings
|
|
/// (the W-2 spawn posture, ADR-012 §4); the engine layer owns the
|
|
/// mapping, ADR-012 §2.
|
|
pub(crate) fn database_error(message: impl Into<String>) -> Error {
|
|
Error::database(std::io::Error::other(message.into()))
|
|
}
|
|
|
|
/// Map a `rusqlite::Error` into the contract taxonomy, source chain
|
|
/// preserved (ADR-008 §5 — no engine-specific variants minted).
|
|
pub(crate) fn sqlite_error(e: rusqlite::Error) -> Error {
|
|
Error::database(e)
|
|
}
|