//! 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(f: F) -> Result where T: Send + 'static, F: FnOnce() -> Result + 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) -> 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) }