pub const FALLBACK_MIME: &str = "application/octet-stream";
const EXT_TO_MIME: &[(&str, &str)] = &[
("md", "text/markdown"), ("markdown", "text/markdown"), ("txt", "text/plain"), ("json", "application/json"), ("xml", "application/xml"), ("yaml", "application/yaml"), ("yml", "application/yaml"), ("toml", "application/toml"), ("rst", "text/x-rst"), ("png", "image/png"),
("jpg", "image/jpeg"),
("jpeg", "image/jpeg"),
("gif", "image/gif"),
("webp", "image/webp"),
("bmp", "image/bmp"),
("ico", "image/vnd.microsoft.icon"),
("svg", "image/svg+xml"), ("pdf", "application/pdf"),
("zip", "application/zip"),
("gz", "application/gzip"),
("tar", "application/x-tar"),
("wasm", "application/wasm"),
("woff", "font/woff"),
("woff2", "font/woff2"),
("ttf", "font/ttf"),
("otf", "font/otf"),
("mp3", "audio/mpeg"),
("wav", "audio/wav"),
("mp4", "video/mp4"),
("webm", "video/webm"),
];
const TEXT_MIME_ESSENCES: &[&str] = &[
"application/json",
"application/xml",
"application/yaml",
"application/x-yaml",
"application/toml",
"application/javascript",
];
const TEXT_STRUCTURED_SUFFIXES: &[&str] = &["+json", "+xml", "+yaml"];
fn ext_of(filename: &str) -> Option<String> {
let last_seg = filename.rsplit(['/', '\\']).next().unwrap_or(filename);
let dot = last_seg.rfind('.')?;
if dot == 0 || dot + 1 >= last_seg.len() {
return None;
}
Some(last_seg[dot + 1..].to_ascii_lowercase())
}
pub fn guess_mime(filename: &str) -> &'static str {
match ext_of(filename) {
Some(ext) => EXT_TO_MIME
.iter()
.find(|(k, _)| *k == ext)
.map(|(_, v)| *v)
.unwrap_or(FALLBACK_MIME),
None => FALLBACK_MIME,
}
}
pub fn is_text_mime(mime: &str) -> bool {
let essence = mime
.split(';')
.next()
.unwrap_or(mime)
.trim()
.to_ascii_lowercase();
if essence.starts_with("text/") {
return true;
}
if TEXT_STRUCTURED_SUFFIXES
.iter()
.any(|suf| essence.ends_with(suf))
{
return true;
}
TEXT_MIME_ESSENCES.contains(&essence.as_str())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn guess_mime_text_baseline_matches_spec_64_3() {
assert_eq!(guess_mime("a.md"), "text/markdown");
assert_eq!(guess_mime("a.markdown"), "text/markdown");
assert_eq!(guess_mime("a.txt"), "text/plain");
assert_eq!(guess_mime("a.json"), "application/json");
assert_eq!(guess_mime("a.xml"), "application/xml");
assert_eq!(guess_mime("a.yaml"), "application/yaml");
assert_eq!(guess_mime("a.yml"), "application/yaml");
assert_eq!(guess_mime("a.toml"), "application/toml");
assert_eq!(guess_mime("a.rst"), "text/x-rst");
}
#[test]
fn guess_mime_is_case_insensitive_and_path_aware() {
assert_eq!(guess_mime("README.MD"), "text/markdown");
assert_eq!(guess_mime("pkg/sub/config.YAML"), "application/yaml");
assert_eq!(guess_mime("a\\b\\data.JSON"), "application/json");
}
#[test]
fn guess_mime_unknown_and_extless_fall_back() {
assert_eq!(guess_mime("noext"), FALLBACK_MIME);
assert_eq!(guess_mime(".bashrc"), FALLBACK_MIME); assert_eq!(guess_mime("trailingdot."), FALLBACK_MIME);
assert_eq!(guess_mime("a.unknownext"), FALLBACK_MIME);
}
#[test]
fn guess_mime_common_binary() {
assert_eq!(guess_mime("a.png"), "image/png");
assert_eq!(guess_mime("a.svg"), "image/svg+xml");
assert_eq!(guess_mime("a.pdf"), "application/pdf");
}
#[test]
fn is_text_mime_branch1_text_star() {
assert!(is_text_mime("text/plain"));
assert!(is_text_mime("text/markdown"));
assert!(is_text_mime("text/x-rst"));
assert!(is_text_mime("text/plain; charset=utf-8"));
assert!(is_text_mime("TEXT/PLAIN")); }
#[test]
fn is_text_mime_branch2_structured_suffix() {
assert!(is_text_mime("image/svg+xml"));
assert!(is_text_mime("application/vnd.api+json"));
assert!(is_text_mime("application/ld+json"));
assert!(is_text_mime("application/foo+yaml"));
}
#[test]
fn is_text_mime_branch3_essence_whitelist() {
assert!(is_text_mime("application/json"));
assert!(is_text_mime("application/json; charset=utf-8"));
assert!(is_text_mime("application/xml"));
assert!(is_text_mime("application/yaml"));
assert!(is_text_mime("application/x-yaml"));
assert!(is_text_mime("application/toml"));
assert!(is_text_mime("application/javascript"));
}
#[test]
fn is_text_mime_binary_is_not_text() {
assert!(!is_text_mime("application/octet-stream"));
assert!(!is_text_mime("image/png"));
assert!(!is_text_mime("application/pdf"));
assert!(!is_text_mime("application/zip"));
assert!(!is_text_mime(""));
}
}