use std::path::Path;
const OCTET_STREAM: &str = "application/octet-stream";
const TABLE: &[(&[&str], &str, &str)] = &[
(&["png"], "image/png", "image/png"),
(&["jpg", "jpeg"], "image/jpeg", "image/jpeg"),
(&["webp"], "image/webp", "image/webp"),
(&["gif"], "image/gif", "image/gif"),
(&["avif"], "image/avif", "image/avif"),
(&["bmp"], "image/bmp", "image/bmp"),
(&["tiff", "tif"], "image/tiff", "image/tiff"),
(&["ico"], "image/x-icon", "image/x-icon"),
(&["svg"], "image/svg+xml", "image/svg+xml"),
(&["mp4"], "video/mp4", "video/mp4"),
(&["webm"], "video/webm", "video/webm"),
(&["mov"], "video/quicktime", "video/quicktime"),
(&["avi"], "video/x-msvideo", "video/x-msvideo"),
(&["mkv"], "video/x-matroska", "video/x-matroska"),
(&["ogv"], "video/ogg", "video/ogg"),
(&["mp3"], "audio/mpeg", "audio/mpeg"),
(&["weba"], "audio/webm", "audio/webm"),
(&["wav"], "audio/wav", "audio/wav"),
(&["ogg"], "audio/ogg", "audio/ogg"),
(&["aac"], "audio/aac", "audio/aac"),
(&["flac"], "audio/flac", "audio/flac"),
(&["m4a"], "audio/mp4", "audio/mp4"),
(&["woff"], "font/woff", "font/woff"),
(&["woff2"], "font/woff2", "font/woff2"),
(&["ttf"], "font/ttf", "font/ttf"),
(&["otf"], "font/otf", "font/otf"),
(&["html", "htm"], "text/html", "text/html; charset=utf-8"),
(&["css"], "text/css", "text/css; charset=utf-8"),
(
&["js", "mjs"],
"text/javascript",
"text/javascript; charset=utf-8",
),
(&["json", "map"], "application/json", "application/json"),
(&["txt"], "text/plain", "text/plain; charset=utf-8"),
(&["csv"], "text/csv", "text/csv; charset=utf-8"),
(&["md"], "text/markdown", "text/markdown; charset=utf-8"),
(&["ftl"], "text/plain", "text/plain; charset=utf-8"),
(&["xml"], "application/xml", "application/xml"),
(&["yaml", "yml"], "application/yaml", "application/yaml"),
(&["toml"], "application/toml", "application/toml"),
(&["sh"], "application/x-sh", "application/x-sh"),
(&["wasm"], "application/wasm", "application/wasm"),
(&["pdf"], "application/pdf", "application/pdf"),
(&["rtf"], "application/rtf", "application/rtf"),
(&["doc"], "application/msword", "application/msword"),
(
&["docx"],
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
),
(
&["xls"],
"application/vnd.ms-excel",
"application/vnd.ms-excel",
),
(
&["xlsx"],
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
),
(
&["ppt"],
"application/vnd.ms-powerpoint",
"application/vnd.ms-powerpoint",
),
(
&["pptx"],
"application/vnd.openxmlformats-officedocument.presentationml.presentation",
"application/vnd.openxmlformats-officedocument.presentationml.presentation",
),
];
const ALIASES: &[(&str, &str)] = &[
("image/vnd.microsoft.icon", "image/x-icon"),
("text/xml", "application/xml"),
("application/javascript", "text/javascript"),
("text/yaml", "application/yaml"),
("audio/mp3", "audio/mpeg"),
("audio/wave", "audio/wav"),
("audio/x-wav", "audio/wav"),
("audio/x-m4a", "audio/mp4"),
];
fn lookup(ext: &str) -> Option<&'static (&'static [&'static str], &'static str, &'static str)> {
let lower = ext.to_ascii_lowercase();
TABLE
.iter()
.find(|(exts, _, _)| exts.iter().any(|e| *e == lower))
}
fn extension_of(path: &Path) -> Option<&str> {
path.extension().and_then(std::ffi::OsStr::to_str)
}
pub fn from_extension(ext: &str) -> Option<&'static str> {
lookup(ext).map(|(_, essence, _)| *essence)
}
pub fn from_path(path: &Path) -> &'static str {
extension_of(path)
.and_then(from_extension)
.unwrap_or(OCTET_STREAM)
}
pub fn http_content_type(path: &Path) -> &'static str {
http_content_type_opt(path).unwrap_or(OCTET_STREAM)
}
pub fn http_content_type_opt(path: &Path) -> Option<&'static str> {
extension_of(path)
.and_then(lookup)
.map(|(_, _, served)| *served)
}
pub fn extension_for(mime: &str) -> Option<&'static str> {
let essence = essence_of(mime);
let resolved = ALIASES
.iter()
.find(|(alias, _)| *alias == essence)
.map_or(essence.as_str(), |(_, canonical)| *canonical);
TABLE
.iter()
.find(|(_, e, _)| *e == resolved)
.and_then(|(exts, _, _)| exts.first().copied())
}
pub fn essence_of(mime: &str) -> String {
mime.split(';')
.next()
.unwrap_or(mime)
.trim()
.to_ascii_lowercase()
}