chronogrid/crates/chronogrid_ffi/src/api/state.rs

59 lines
2.1 KiB
Rust

//! Internal engine state — not part of the public FFI API.
//!
//! The [`Engine`] struct bundles an open [`Database`] connection and a
//! [`Hlc`] (Hybrid Logical Clock) for the local node. A single global
//! instance is held behind a [`Mutex`] so that FRB's thread-pool workers
//! can safely share access.
use std::sync::Mutex;
use chronogrid_core::{Database, Hlc};
/// Runtime engine holding the encrypted database and the node's HLC.
pub(crate) struct Engine {
/// Encrypted SQLCipher database connection with ChronoGrid schema.
pub(crate) db: Database,
/// Hybrid Logical Clock for causally ordered CRDT mutations.
pub(crate) hlc: Hlc,
}
/// Global engine singleton.
///
/// `None` before [`super::engine::init_engine`] is called, `Some` while the
/// engine is active, and `None` again after [`super::engine::close_engine`].
pub(crate) static ENGINE: Mutex<Option<Engine>> = Mutex::new(None);
// ── Convenience helpers ──────────────────────────────────────────────────────
/// Acquires the engine lock and calls `f` with a shared reference.
///
/// Returns `Err` if the engine has not been initialized or the lock is
/// poisoned.
pub(crate) fn with_engine<T>(
f: impl FnOnce(&Engine) -> anyhow::Result<T>,
) -> anyhow::Result<T> {
let guard = ENGINE
.lock()
.map_err(|e| anyhow::anyhow!("engine lock poisoned: {e}"))?;
let engine = guard
.as_ref()
.ok_or_else(|| anyhow::anyhow!("engine not initialized — call init_engine first"))?;
f(engine)
}
/// Acquires the engine lock and calls `f` with a mutable reference.
///
/// Returns `Err` if the engine has not been initialized or the lock is
/// poisoned.
pub(crate) fn with_engine_mut<T>(
f: impl FnOnce(&mut Engine) -> anyhow::Result<T>,
) -> anyhow::Result<T> {
let mut guard = ENGINE
.lock()
.map_err(|e| anyhow::anyhow!("engine lock poisoned: {e}"))?;
let engine = guard
.as_mut()
.ok_or_else(|| anyhow::anyhow!("engine not initialized — call init_engine first"))?;
f(engine)
}