//! kei-cache — deterministic caching primitive for pure atom invocations.
//!
//! Entry point is [`wrap_with`]: given a cache [`rusqlite::Connection`], an
//! [`exec::AtomExecutor`], an atom id, JSON input, and a TTL, either
//! return the cached payload or invoke the executor, store the result,
//! and return it.
//!
//! Key derivation lives in [`key`]. Storage lives in [`store`]. Invocation
//! on miss lives in [`exec`]. `lib.rs` only composes them — it owns no
//! persistent state.
pub mod exec;
pub mod key;
pub mod store;
use anyhow::{Context, Result};
use rusqlite::Connection;
use serde_json::Value;
pub use exec::{AtomExecutor, SubprocessExecutor};
pub use store::Stats;
/// Outcome of a [`wrap_with`] call.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Outcome {
Hit,
Miss,
}
impl Outcome {
pub fn as_str(&self) -> &'static str {
match self {
Outcome::Hit => "hit",
Outcome::Miss => "miss",
}
}
}
/// Cache trait — library-level API for downstream consumers.
///
/// Production impl is [`SqliteCache`]. Tests may provide in-memory impls.
pub trait Cache {
/// Fetch from the cache; `None` if absent or expired.
fn get(&self, key: &str) -> Result