mannequin/src/facet_react.rs
//! **holger's 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 holger's half.
//!
//! ## How this COMPOSES with `holger-<subsystem>-<n>`
//! holger owns a frozen registry of its OWN application error codes
//! (`holger-errcode`, `holger-<subsystem>-<n>`, exhaustively matched so an un-coded
//! variant is a COMPILE error). The two vocabularies **compose, they never collide**:
//!
//! * a **facett UI** failure surfaced through a holger mount keeps facett's frozen
//! code plus holger's mount prefix — `holger/mannequin:facet-syschart-1`;
//! * holger's OWN backend failure keeps `holger-<subsystem>-<n>`.
//!
//! ## The mount
//! holger embeds `facett_syschart::SystemChart` as the mannequin PKI/OIDC/Nexus
//! system map (`holger/mannequin`). It is declared in facett's shared registry, so
//! the reaction obligations below are generated, not hand-authored.
use facett_core::errcode::{
matrix_for_app, reaction_for, ConsumerMount, FacetError, FacetRecorder, Reaction,
};
/// holger's application key in the shared reaction matrix.
pub const APP: &str = "holger";
/// The **mannequin** mount — holger's PKI / OIDC / Nexus system map. `site` is the
/// ANTI-FICTION anchor: the guard test asserts this file exists and really mounts the
/// component whose codes it claims.
pub const MANNEQUIN: ConsumerMount = ConsumerMount {
app: APP,
surface: "mannequin",
site: "mannequin/src/gui.rs",
};
/// Every holger mount.
pub const MOUNTS: &[ConsumerMount] = &[MANNEQUIN];
/// The reaction-matrix rows holger is contracted to satisfy — sliced from facett's
/// shared table.
pub fn matrix() -> Vec<(String, &'static str, &'static str, Reaction)> {
matrix_for_app(APP)
}
/// **React to a facett UI error raised at a holger mount.** Namespaces the code with
/// the mount, records the MOUNTED code into `rec` (folded into the GUI's
/// `state_json`), 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 a driven facett pane PUBLISHED — the pane writes
/// `state_json.error_code` when it is in its error state, so holger 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 observable Phase-2 surface for holger'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": "holger/<surface>:facet-<component>-<n>",
"app_codes": "holger-<subsystem>-<n> (holger-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!(MANNEQUIN.prefix(), "holger/mannequin");
}
#[test]
fn reacting_namespaces_the_code_and_returns_the_canonical_reaction() {
let mut rec = FacetRecorder::new();
let err = facett_core::facet_err!(syschart, 1, "no peers to show");
assert_eq!(react(&mut rec, MANNEQUIN, &err), Reaction::EmptyState);
assert!(rec.saw("holger/mannequin:facet-syschart-1"));
assert_eq!(
rec.entries()[0].code,
"facet-syschart-1",
"facett's base code is untouched"
);
}
#[test]
fn react_to_state_reads_the_code_out_of_the_panes_data() {
let mut rec = FacetRecorder::new();
let healthy = serde_json::json!({ "peers": 3 });
assert_eq!(react_to_state(&mut rec, MANNEQUIN, &healthy), None);
let broken = serde_json::json!({ "error_code": "facet-syschart-1" });
assert_eq!(
react_to_state(&mut rec, MANNEQUIN, &broken),
Some(Reaction::EmptyState)
);
assert!(rec.saw("holger/mannequin:facet-syschart-1"));
}
/// The facett code and holger's OWN app code COMPOSE — neither shadows the other.
#[test]
fn facett_codes_compose_with_holgers_own_app_codes() {
assert!(facett_core::errcode::lookup("facet-syschart-1").is_some());
// A facett mounted code is not a holger app code.
assert!(holger_errcode::by_code("holger/mannequin:facet-syschart-1").is_none());
// And a holger app code is not a facett code.
assert!(facett_core::errcode::lookup("holger-pki-1").is_none());
}
}