Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/dwarves

crates/nornir-funnel-embed/src/facet_react.rs

//! **dwarves' REACTION to a facett UI error code** (facett error-codes, Phase 2).
//!
//! facett panes raise a stable, unique UI error code — `facet-<component>-<n>`
//! ([`facett_core::errcode`]) — carrying BOTH the human message and the code, so a
//! consumer reacts to the CODE, not a matched string. This module is dwarves' half.
//!
//! ## How this COMPOSES with `dwarves-<subsystem>-<n>`
//! dwarves owns a frozen registry of its OWN application error codes
//! (`dwarves::errcode`), which is guarded two-ways so a declared code must actually be
//! raised in its declared file. The two vocabularies **compose, they never collide**:
//!
//! * a **facett UI** failure surfaced through a dwarves mount keeps facett's frozen
//!   code plus dwarves' mount prefix — `dwarves/funnel:facet-graphview-1`;
//! * dwarves' OWN failure keeps `dwarves-<subsystem>-<n>`.
//!
//! The same anti-fiction discipline applies HERE: every mount declares the `site`
//! file that really mounts the component, and the guard test in
//! `tests/facet_reaction_matrix.rs` fails if that file does not reference it.
//!
//! ## The mount
//! dwarves embeds `facett_graphview::DecoratedGraphView` as the **Funnel DAG** pane
//! (`dwarves/funnel`), see [`crate::graph`].

use facett_core::errcode::{
    matrix_for_app, reaction_for, ConsumerMount, FacetError, FacetRecorder, Reaction,
};

/// dwarves' application key in the shared reaction matrix.
pub const APP: &str = "dwarves";

/// The **funnel** mount — the Funnel DAG pane over `DecoratedGraphView`. `site` is the
/// ANTI-FICTION anchor: the guard test asserts this file exists and really mounts the
/// component whose codes it claims.
pub const FUNNEL: ConsumerMount = ConsumerMount {
    app: APP,
    surface: "funnel",
    site: "crates/nornir-funnel-embed/src/graph.rs",
};

/// Every dwarves mount.
pub const MOUNTS: &[ConsumerMount] = &[FUNNEL];

/// The reaction-matrix rows dwarves is contracted to satisfy — sliced from facett's
/// shared table, so declaring a mount in the registry creates the obligation here.
pub fn matrix() -> Vec<(String, &'static str, &'static str, Reaction)> {
    matrix_for_app(APP)
}

/// **React to a facett UI error raised at a dwarves mount.** Namespaces the code with
/// the mount, records the MOUNTED code into `rec`, and returns the canonical
/// [`Reaction`]. An unregistered code is [`Reaction::Diagnose`] — never a silent pass.
pub fn react(rec: &mut FacetRecorder, mount: ConsumerMount, err: &FacetError) -> Reaction {
    rec.record(mount, err)
}

/// React to whatever the driven pane PUBLISHED — the pane writes
/// `state_json.error_code` when it is in its error state, so dwarves branches on the
/// CODE in the data rather than scanning a rendered string.
pub fn react_to_state(
    rec: &mut FacetRecorder,
    mount: ConsumerMount,
    state: &serde_json::Value,
) -> Option<Reaction> {
    let code = state.get("error_code")?.as_str()?;
    let entry = facett_core::errcode::lookup(code);
    let (component, n, msg, stat) = match entry {
        Some(e) => (e.component, e.n, e.message.to_string(), e.code),
        None => (
            "unknown",
            0,
            code.to_string(),
            Box::leak(code.to_string().into_boxed_str()) as &'static str,
        ),
    };
    let err = FacetError::new(component, n, stat, msg);
    Some(react(rec, mount, &err))
}

/// **The `dwarves/funnel` CONSUMER SEAM** — the mount-checked entry point the live
/// pane calls (see [`crate::graph::FunnelGraphPane`]).
///
/// Whether a published code belongs to *this* mount is resolved from facett's shared
/// registry ([`facett_core::errcode::mounts_for`]) rather than a local list, so the
/// mount cannot be fiction in either direction: a code whose `consumer` column does
/// not name `dwarves/funnel` is left alone, and one that does is always reacted to.
/// Returns `None` when the pane raised nothing (or raised another mount's code).
pub fn react_to_pane_state(rec: &mut FacetRecorder, state: &serde_json::Value) -> Option<Reaction> {
    let code = state.get("error_code")?.as_str()?;
    let mount = FUNNEL.prefix();
    if !facett_core::errcode::mounts_for(code)
        .iter()
        .any(|m| *m == mount)
    {
        return None;
    }
    react_to_state(rec, FUNNEL, state)
}

/// The observable Phase-2 surface for the pane's `state_json`.
pub fn state_json(rec: &FacetRecorder) -> serde_json::Value {
    let rows: Vec<serde_json::Value> = matrix()
        .into_iter()
        .map(|(mounted, base, mount, reaction)| {
            serde_json::json!({
                "mounted_code": mounted,
                "code": base,
                "mount": mount,
                "reaction": reaction.token(),
            })
        })
        .collect();
    serde_json::json!({
        "scheme": "dwarves/<surface>:facet-<component>-<n>",
        "app_codes": "dwarves-<subsystem>-<n> (dwarves::errcode) — composes, never collides",
        "pairs": rows.len(),
        "mounts": MOUNTS.iter().map(|m| m.prefix()).collect::<Vec<_>>(),
        "matrix": rows,
        "recorded": rec.state_json(),
    })
}

/// Resolve the canonical reaction for a (possibly mounted) facett code.
pub fn reaction_of(code: &str) -> Option<Reaction> {
    reaction_for(code)
}

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

    #[test]
    fn mount_prefix_is_stable() {
        assert_eq!(FUNNEL.prefix(), "dwarves/funnel");
        assert_eq!(FUNNEL.app, APP);
    }

    #[test]
    fn reacting_namespaces_the_code_and_returns_the_canonical_reaction() {
        let mut rec = FacetRecorder::new();
        let err = facett_core::facet_err!(graphview, 1, "empty scene");
        assert_eq!(react(&mut rec, FUNNEL, &err), Reaction::EmptyState);
        assert!(rec.saw("dwarves/funnel:facet-graphview-1"));
        assert_eq!(
            rec.entries()[0].code,
            "facet-graphview-1",
            "facett's base code untouched"
        );
    }

    #[test]
    fn react_to_state_reads_the_code_out_of_the_panes_data() {
        let mut rec = FacetRecorder::new();
        let healthy = serde_json::json!({ "nodes": 7 });
        assert_eq!(react_to_state(&mut rec, FUNNEL, &healthy), None);
        let broken = serde_json::json!({ "error_code": "facet-graphview-1" });
        assert_eq!(
            react_to_state(&mut rec, FUNNEL, &broken),
            Some(Reaction::EmptyState)
        );
        assert!(rec.saw("dwarves/funnel:facet-graphview-1"));
    }

    /// A facett mounted code is NOT a dwarves app code and vice-versa.
    #[test]
    fn facett_codes_compose_with_dwarves_own_app_codes() {
        assert!(facett_core::errcode::lookup("facet-graphview-1").is_some());
        assert!(facett_core::errcode::lookup("dwarves-forge-1").is_none());
    }
}