use egui::{Align2, Color32, FontId, Rect, Sense, Stroke, Ui};
pub const DEVID_ATOM: &str = "dev-id:";
pub const FOCUSED_MARK: &str = "[focused]";
pub const UNREGISTERED: &str = "⟨unregistered⟩";
pub fn normalize(raw: &str) -> &str {
let head = raw.split("::").next().unwrap_or(raw);
head.strip_prefix("facett_").unwrap_or(head)
}
pub fn resolve(component: &str) -> Option<(&'static str, &'static str)> {
let want = normalize(component);
crate::errcode::REGISTRY
.iter()
.find(|e| e.component == want)
.map(|e| (e.crate_dir, e.src_file))
}
pub fn is_registered(component: &str) -> bool {
resolve(component).is_some()
}
pub fn badge_text(component: &str, title: &str) -> (String, bool) {
if !component.is_empty() && is_registered(component) {
let comp = normalize(component);
let t = title.trim();
if t.is_empty() { (comp.to_string(), true) } else { (format!("{comp}/{t}"), true) }
} else {
let what = if component.is_empty() { title } else { normalize(component) };
(format!("{UNREGISTERED} {what}"), false)
}
}
pub fn chip_colors(registered: bool) -> (Color32, Color32) {
if registered {
(Color32::from_rgb(150, 200, 255), Color32::from_rgb(12, 14, 20))
} else {
(Color32::from_gray(150), Color32::from_rgb(20, 20, 22))
}
}
pub fn chip_colors_focused(registered: bool) -> (Color32, Color32) {
if registered {
(Color32::from_rgb(235, 245, 255), Color32::from_rgb(24, 48, 90))
} else {
(Color32::from_rgb(225, 225, 230), Color32::from_rgb(52, 52, 58))
}
}
pub fn badge(ui: &mut Ui, rect: Rect, component: &str, title: &str) {
if !enabled() {
return;
}
let (text, registered) = badge_text(component, title);
let font = FontId::monospace(10.0);
let focused = ui.rect_contains_pointer(rect);
let (fg, bg) = if focused { chip_colors_focused(registered) } else { chip_colors(registered) };
let galley = ui.painter().layout_no_wrap(text.clone(), font, fg);
let size = galley.size();
let anchor = egui::pos2(rect.right() - 4.0, rect.top() + 4.0);
let chip = Rect::from_min_size(egui::pos2(anchor.x - size.x - 6.0, anchor.y), size)
.expand2(egui::vec2(4.0, 2.0));
let resp = crate::a11y::node(
ui,
ui.id(),
("devid", &text),
Sense::click(),
chip,
crate::a11y::Semantics::button(if focused {
format!("{DEVID_ATOM} {text} {FOCUSED_MARK}")
} else {
format!("{DEVID_ATOM} {text}")
}),
);
let p = ui.painter();
p.rect_filled(chip, 3.0, bg);
if resp.hovered() {
p.rect_stroke(chip, 3.0, Stroke::new(1.0, fg), egui::StrokeKind::Inside);
}
p.text(chip.center(), Align2::CENTER_CENTER, &text, FontId::monospace(10.0), fg);
if resp.clicked() {
let payload =
if registered { normalize(component).to_string() } else { format!("{title} (unregistered pane)") };
crate::clipboard::put(ui.ctx(), payload);
}
let hover = match resolve(component) {
Some((dir, file)) => format!("{text}\n{dir}/{file}\nclick to copy"),
None => format!("{text}\nthis pane has not declared Facet::component()\nclick to copy"),
};
resp.on_hover_text(hover);
}
pub fn resolve_enabled(var: Option<&str>, debug_assertions: bool) -> bool {
match var {
Some("1" | "true" | "on" | "yes") => true,
Some("0" | "false" | "off" | "no") => false,
_ => debug_assertions,
}
}
pub fn enabled() -> bool {
static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
*ON.get_or_init(|| {
resolve_enabled(std::env::var("FACETT_DEVID").ok().as_deref(), cfg!(debug_assertions))
})
}
pub fn gate_json() -> serde_json::Value {
let env = std::env::var("FACETT_DEVID").ok();
let understood = matches!(
env.as_deref(),
Some("1" | "true" | "on" | "yes" | "0" | "false" | "off" | "no")
);
serde_json::json!({
"enabled": enabled(),
"env": env,
"debug_assertions": cfg!(debug_assertions),
"source": if understood { "env" } else { "profile" },
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn an_unknown_component_is_marked_unregistered_not_rendered_as_an_id() {
let (text, ok) = badge_text("definitely-not-a-registered-component", "Some Pane");
assert!(!ok, "an unknown component must NOT report as registered");
assert!(text.starts_with(UNREGISTERED), "must be visibly marked, got {text:?}");
assert!(resolve("definitely-not-a-registered-component").is_none());
let (text, ok) = badge_text("", "Some Pane");
assert!(!ok);
assert!(text.contains("Some Pane"), "the fallback still identifies the pane: {text:?}");
assert!(text.starts_with(UNREGISTERED));
}
#[test]
fn a_module_path_normalises_to_its_registry_component() {
assert_eq!(normalize("facett_geomap"), "geomap");
assert_eq!(normalize("facett_map3d::inner::deep"), "map3d");
assert_eq!(normalize("geomap"), "geomap", "a hand-written bare component passes through");
assert!(is_registered("facett_geomap"), "the bridged spelling must resolve");
let (text, ok) = badge_text("facett_map3d", "3-D Map");
assert!(ok);
assert_eq!(text, "map3d/3-D Map", "the chip shows the registry name + the pane");
let (a, _) = badge_text("korp", "ingest_load");
let (b, _) = badge_text("korp", "map");
assert_ne!(a, b, "same component, different pane, identical chip: {a} vs {b}");
}
#[test]
fn the_gate_honours_the_env_var_in_a_release_profile_too() {
assert!(!resolve_enabled(None, false), "release + unset must be OFF");
for on in ["1", "true", "on", "yes"] {
assert!(
resolve_enabled(Some(on), false),
"FACETT_DEVID={on} must switch the chip ON in a RELEASE build — that is \
the whole escape hatch, and without it the aid is invisible to the \
binaries people run"
);
}
for off in ["0", "false", "off", "no"] {
assert!(!resolve_enabled(Some(off), false), "FACETT_DEVID={off} must stay OFF");
}
assert!(!resolve_enabled(Some("banana"), false), "an unknown value must not enable it");
assert!(!resolve_enabled(Some(""), false), "an empty value must not enable it");
assert!(resolve_enabled(None, true), "debug + unset must be ON");
assert!(!resolve_enabled(Some("0"), true), "FACETT_DEVID=0 must silence it in debug");
assert!(resolve_enabled(Some("banana"), true), "an unknown value falls back to profile");
if std::env::var_os("FACETT_DEVID").is_none() {
assert_eq!(
enabled(),
cfg!(debug_assertions),
"enabled() disagrees with resolve_enabled for this build profile"
);
}
}
#[test]
fn the_chip_plate_is_opaque_so_it_does_not_superimpose_two_strings() {
for registered in [true, false] {
let (fg, bg) = chip_colors(registered);
assert_eq!(
bg.a(),
255,
"the chip plate is translucent (alpha {}) for registered={registered} — the \
pane's own text bleeds through it and the id becomes unreadable",
bg.a()
);
let lum = |c: Color32| 0.299 * c.r() as f32 + 0.587 * c.g() as f32 + 0.114 * c.b() as f32;
assert!(
(lum(fg) - lum(bg)).abs() > 60.0,
"chip fg/bg are too close to read (registered={registered}): {fg:?} on {bg:?}"
);
}
assert_ne!(chip_colors(true).0, chip_colors(false).0, "registered/unregistered look identical");
}
#[test]
fn the_reported_gate_matches_the_one_the_badge_acts_on() {
let g = gate_json();
assert_eq!(
g["enabled"], enabled(),
"state_json reports a different gate than badge() obeys: {g}"
);
assert_eq!(
g["debug_assertions"], cfg!(debug_assertions),
"the reported profile is not this binary's profile: {g}"
);
let by_env = std::env::var("FACETT_DEVID")
.map(|v| matches!(v.as_str(), "1" | "true" | "on" | "yes" | "0" | "false" | "off" | "no"))
.unwrap_or(false);
assert_eq!(g["source"], if by_env { "env" } else { "profile" }, "wrong source: {g}");
}
#[test]
fn a_registered_component_resolves_to_its_crate_and_file() {
let known = crate::errcode::REGISTRY.first().expect("the registry is not empty");
let (text, ok) = badge_text(known.component, "ignored title");
assert!(ok, "{} is in the registry but did not report registered", known.component);
assert_eq!(text, format!("{}/ignored title", known.component), "registered ⇒ component/pane");
let (dir, file) = resolve(known.component).expect("resolves");
assert!(!dir.is_empty() && !file.is_empty(), "a resolved id must name a real place");
}
}