facett-core 0.1.8

facett — visual kernel: render a node/edge Scene into egui (wgpu fast path to come)
Documentation
//! **In-memory render/exec trace** — the wasm-safe "what actually RAN" ledger.
//!
//! Unlike [`crate::trace`] (a JSONL file at `$FACETT_TRACE`, native-only — there is
//! no filesystem in a browser), this is a process-global, **in-memory** counter
//! map: every component's `ui()`/render and every interactive control handler calls
//! [`ran`] with a stable key, and the running tally is read back as a JSON object
//! with [`snapshot`]. It is folded into the demo's `state_json["trace"]["ran"]`, so
//! the SAME mechanism proves execution natively (a headless egui_kittest drive) AND
//! in the shipped wasm bundle (read via the `window.__facett_state()` JS hook).
//!
//! This is the mechanism that was missing: a way to verify the SHIPPED wasm artifact
//! — that each tab/control actually ran in the browser — as readable data, with no
//! human and no filesystem.
//!
//! Cheap + dependency-free: a `Mutex<BTreeMap<String,u64>>` behind a `OnceLock`,
//! incremented on edge-triggered control handlers + once per active-facet render.
//! `BTreeMap` keeps the snapshot key order stable (deterministic for tests).

use std::collections::BTreeMap;
use std::sync::{Mutex, OnceLock};

static LEDGER: OnceLock<Mutex<BTreeMap<String, u64>>> = OnceLock::new();

fn ledger() -> &'static Mutex<BTreeMap<String, u64>> {
    LEDGER.get_or_init(|| Mutex::new(BTreeMap::new()))
}

/// Record that the component/control identified by `key` RAN once (e.g.
/// `"PopGraphTab::ui"`, `"PopGraphTab::ctl:11M"`). Increments its tally. Never
/// panics (a poisoned lock is silently ignored — a trace must never crash the UI).
pub fn ran(key: &str) {
    if let Ok(mut g) = ledger().lock() {
        *g.entry(key.to_string()).or_insert(0) += 1;
    }
}

/// The current tally as a JSON object `{ "<key>": <count>, … }` — the readable
/// "what ran" list. Folded into the demo's `state_json` so a headless drive (native)
/// or the `window.__facett_state()` JS hook (wasm) reads exactly what executed.
pub fn snapshot() -> serde_json::Value {
    let g = match ledger().lock() {
        Ok(g) => g,
        Err(_) => return serde_json::json!({}),
    };
    let map: serde_json::Map<String, serde_json::Value> =
        g.iter().map(|(k, v)| (k.clone(), serde_json::json!(v))).collect();
    serde_json::Value::Object(map)
}

/// How many distinct keys have ever run (the breadth of the trace).
pub fn distinct() -> usize {
    ledger().lock().map(|g| g.len()).unwrap_or(0)
}

/// The count recorded for one key (0 if it never ran). The inject-assert seam: a
/// test reads back whether a specific control's handler actually executed.
pub fn count(key: &str) -> u64 {
    ledger().lock().map(|g| g.get(key).copied().unwrap_or(0)).unwrap_or(0)
}

/// Clear the ledger — for test isolation (each test starts from a clean slate so a
/// previous test's runs don't leak into its assertions). The global is shared, so a
/// test that asserts counts should `reset()` first.
pub fn reset() {
    if let Ok(mut g) = ledger().lock() {
        g.clear();
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    /// INJECT-ASSERT: recording real keys produces a real, readable tally — the
    /// snapshot reflects exactly what ran, with per-key counts, not "didn't panic".
    #[test]
    fn records_and_snapshots_real_counts() {
        reset();
        ran("PopGraphTab::ui");
        ran("PopGraphTab::ui");
        ran("PopGraphTab::ctl:11M");
        let s = snapshot();
        assert_eq!(s["PopGraphTab::ui"].as_u64(), Some(2), "ui ran twice");
        assert_eq!(s["PopGraphTab::ctl:11M"].as_u64(), Some(1), "the 11M button handler ran once");
        assert_eq!(count("PopGraphTab::ctl:11M"), 1);
        assert_eq!(count("never"), 0, "an unrun key reads 0");
        assert_eq!(distinct(), 2, "two distinct keys ran");
        // The snapshot is a real JSON object a JS hook / test reads field-by-field.
        assert!(s.is_object());
    }

    /// Reset really clears the ledger (test-isolation seam).
    #[test]
    fn reset_clears_the_ledger() {
        ran("a::b");
        reset();
        assert_eq!(distinct(), 0);
        assert_eq!(count("a::b"), 0);
    }
}