bext-plugin-api 0.2.0

Plugin trait definitions and shared types for bext — the public ABI for plugin authors
Documentation
//! Cache backend plugin trait and types for the two-tier (L1 in-memory / L2 plugin)
//! ISR cache, including [`CacheEntry`], lookup status, and tag-based invalidation.

/// Serializable cache entry for cross-backend compatibility.
///
/// When a `CacheBackend` plugin is registered, the ISR cache writes entries
/// to both the local in-memory LRU (L1) and the backend (L2). On reads,
/// L1 is checked first; on miss, the backend is queried and L1 is populated.
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct CacheEntry {
    pub html: String,
    pub headers: Vec<(String, String)>,
    pub etag: String,
    pub tags: Vec<String>,
    pub created_at_ms: u64,
}

/// Result of a cache lookup.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CacheLookupStatus {
    /// Fresh entry found.
    Hit,
    /// Entry found but past TTL (within SWR window).
    Stale,
    /// No entry found.
    Miss,
}

/// Alternative cache storage backend (e.g., Redis, S3, Cloudflare KV).
///
/// Replaces or wraps the built-in LRU cache for ISR entries.
/// Only one backend can be active at a time.
pub trait CacheBackend: Send + Sync {
    /// Unique identifier (e.g., `"redis"`, `"cloudflare-kv"`).
    fn name(&self) -> &str;

    /// Look up a cached entry. Returns `Ok(None)` on miss.
    fn get(&self, key: &str) -> Result<Option<CacheEntry>, String>;

    /// Store an entry with optional TTL in milliseconds.
    fn set(&self, key: &str, entry: CacheEntry, ttl_ms: Option<u64>) -> Result<(), String>;

    /// Delete a single entry by key. Returns whether it existed.
    fn delete(&self, key: &str) -> Result<bool, String>;

    /// Invalidate all entries tagged with `tag`. Returns count invalidated.
    fn invalidate_tag(&self, tag: &str) -> Result<u32, String>;

    /// Invalidate entries matching a path glob. Returns count invalidated.
    fn invalidate_path(&self, pattern: &str) -> Result<u32, String>;

    /// Health check. Default returns `true`.
    fn is_healthy(&self) -> bool {
        true
    }
}