use rust_embed::Embed;
use crate::i18n::tr;
#[derive(Embed)]
#[folder = "graph-viewer/dist"]
#[include = "index.html"]
#[include = "assets/**"]
#[include = "*.png"]
#[exclude = "*.lbug"]
#[allow_missing = true]
struct ViewerAssets;
#[must_use]
pub(super) fn content_type_for(rel: &str) -> String {
mime_guess::from_path(rel)
.first_or_octet_stream()
.essence_str()
.to_string()
}
#[must_use]
pub(super) fn get_asset(rel: &str) -> Option<(Vec<u8>, String)> {
let file = ViewerAssets::get(rel)?;
Some((file.data.into_owned(), content_type_for(rel)))
}
#[must_use]
pub(super) fn fallback_page() -> String {
let message = tr("graph-viewer-dist-missing")
.replace('&', "&")
.replace('<', "<")
.replace('>', ">");
let title = tr("graph-viewer-fallback-title")
.replace('&', "&")
.replace('<', "<")
.replace('>', ">");
let heading = tr("graph-viewer-fallback-heading")
.replace('&', "&")
.replace('<', "<")
.replace('>', ">");
format!(
"<!doctype html><html lang=\"en\"><head><meta charset=\"utf-8\">\
<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\
<title>{title}</title></head>\
<body style=\"font-family: system-ui, sans-serif; margin: 3rem auto; max-width: 40rem; line-height: 1.6;\">\
<h1>{heading}</h1><p>{message}</p></body></html>"
)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn lbug_fixtures_are_never_embedded() {
assert!(ViewerAssets::get("test_e2e.lbug").is_none());
assert!(ViewerAssets::get("e2e_test.lbug").is_none());
assert!(!ViewerAssets::iter().any(|p| p.ends_with(".lbug")));
}
#[test]
fn embedded_set_matches_dist_presence() {
if ViewerAssets::get("index.html").is_some() {
assert!(ViewerAssets::iter().any(|p| p.starts_with("assets/")));
let entry = get_asset("index.html");
assert!(entry.is_some_and(|(bytes, ct)| !bytes.is_empty() && ct == "text/html"));
} else {
assert_eq!(ViewerAssets::iter().count(), 0);
assert!(fallback_page().contains("npm run build"));
}
}
#[test]
#[serial_test::serial(i18n_catalog)]
fn fallback_page_title_and_heading_come_from_catalog() {
crate::i18n::init();
let page = fallback_page();
assert!(
page.contains(&tr("graph-viewer-fallback-title")),
"title must be the catalog entry: {page}"
);
assert!(
page.contains(&tr("graph-viewer-fallback-heading")),
"heading must be the catalog entry: {page}"
);
assert!(
page.contains(&tr("graph-viewer-dist-missing")),
"body guidance must stay translated: {page}"
);
}
#[test]
fn content_types_follow_extension() {
assert_eq!(content_type_for("index.html"), "text/html");
assert_eq!(content_type_for("assets/app.css"), "text/css");
assert!(content_type_for("assets/app.js").contains("javascript"));
assert_eq!(content_type_for("assets/engine.wasm"), "application/wasm");
assert_eq!(content_type_for("assets/font.woff2"), "font/woff2");
assert_eq!(content_type_for("CodeNexus-128.png"), "image/png");
}
}