const TYPES: &[(&str, &str)] = &[
("html", "text/html; charset=utf-8"),
("htm", "text/html; charset=utf-8"),
("css", "text/css; charset=utf-8"),
("js", "text/javascript; charset=utf-8"),
("mjs", "text/javascript; charset=utf-8"),
("json", "application/json"),
("map", "application/json"),
("txt", "text/plain; charset=utf-8"),
("md", "text/markdown; charset=utf-8"),
("xml", "application/xml"),
("svg", "image/svg+xml"),
("png", "image/png"),
("jpg", "image/jpeg"),
("jpeg", "image/jpeg"),
("gif", "image/gif"),
("webp", "image/webp"),
("avif", "image/avif"),
("ico", "image/x-icon"),
("woff", "font/woff"),
("woff2", "font/woff2"),
("ttf", "font/ttf"),
("otf", "font/otf"),
("wasm", "application/wasm"),
("pdf", "application/pdf"),
("zip", "application/zip"),
("mp4", "video/mp4"),
("webm", "video/webm"),
];
const FALLBACK: &str = "application/octet-stream";
#[must_use]
pub fn content_type(path: &str) -> &'static str {
let Some((_, extension)) = path.rsplit_once('.') else {
return FALLBACK;
};
TYPES
.iter()
.find(|(ext, _)| ext.eq_ignore_ascii_case(extension))
.map_or(FALLBACK, |(_, mime)| mime)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_css_file_gets_the_css_content_type() {
assert_eq!(content_type("style.css"), "text/css; charset=utf-8");
}
#[test]
fn an_unknown_extension_and_no_extension_both_fall_back() {
assert_eq!(content_type("archive.wat"), FALLBACK);
assert_eq!(content_type("Makefile"), FALLBACK);
}
#[test]
fn the_lookup_is_case_insensitive() {
assert_eq!(content_type("INDEX.HTML"), "text/html; charset=utf-8");
assert_eq!(content_type("Index.Html"), "text/html; charset=utf-8");
}
#[test]
fn a_double_extension_uses_only_the_last_one() {
assert_eq!(content_type("archive.tar.gz"), FALLBACK);
assert_eq!(content_type("archive.tar.zip"), "application/zip");
}
}