mod dist_embed { include!(concat!(env!("OUT_DIR"), "/dist_embed.rs")); } #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct Asset { pub path: &'static str, pub mime: &'static str, pub content: &'static [u8], } pub fn index() -> Asset { Asset { path: "index.html", mime: "text/html; charset=utf-8", content: dist_embed::INDEX, } } pub fn file(path: &str) -> Option { let key = normalize(path)?; if key.is_empty() || key == "index.html" { return Some(index()); } dist_embed::FILES .iter() .find(|(p, _)| *p == key.as_str()) .map(|(p, content)| Asset { path: p, mime: mime_of(p), content, }) } pub fn exists(path: &str) -> bool { match normalize(path) { None => false, Some(key) => { key.is_empty() || key == "index.html" || dist_embed::FILES.iter().any(|(p, _)| *p == key.as_str()) } } } pub fn paths() -> &'static [&'static str] { dist_embed::PATHS } fn normalize(path: &str) -> Option { let path = path.split(['?', '#']).next().unwrap_or(path); let mut segs: Vec<&str> = Vec::new(); for seg in path.split('/') { match seg { "" | "." => {} ".." => return None, s => segs.push(s), } } Some(segs.join("/")) } fn mime_of(path: &str) -> &'static str { let ext = path .rsplit_once('.') .map_or("", |(_, e)| e) .to_ascii_lowercase(); match ext.as_str() { "html" | "htm" => "text/html; charset=utf-8", "js" | "mjs" => "text/javascript; charset=utf-8", "css" => "text/css; charset=utf-8", "json" | "map" => "application/json", "webmanifest" => "application/manifest+json", "svg" => "image/svg+xml", "png" => "image/png", "jpg" | "jpeg" => "image/jpeg", "gif" => "image/gif", "webp" => "image/webp", "avif" => "image/avif", "ico" => "image/x-icon", "wasm" => "application/wasm", "ttf" => "font/ttf", "otf" => "font/otf", "woff" => "font/woff", "woff2" => "font/woff2", "eot" => "application/vnd.ms-fontobject", "txt" => "text/plain; charset=utf-8", "xml" => "application/xml", "pdf" => "application/pdf", _ => "application/octet-stream", } }