use std::{ collections::{HashMap, VecDeque}, str::FromStr, }; use lazy_static::lazy_static; use mime::Mime; lazy_static! { static ref ALIASES: HashMap = load_mime_aliases(); static ref PARENTS: Vec<(Mime, Mime)> = load_mime_parent_relations(); static ref EXTENSIONS: HashMap = load_mime_extensions(); } fn load_mime_aliases() -> HashMap { tree_magic_db::aliases() .lines() .filter_map(|line| line.split_once(' ')) .filter_map(|(alias, mime)| Some((Mime::from_str(alias).ok()?, Mime::from_str(mime).ok()?))) .collect() } fn load_mime_parent_relations() -> Vec<(Mime, Mime)> { tree_magic_db::subclasses() .lines() .filter_map(|line| line.split_once(' ')) .filter_map(|(child, parent)| { Some((Mime::from_str(child).ok()?, Mime::from_str(parent).ok()?)) }) .collect() } fn load_mime_extensions() -> HashMap { include_str!("../mime.types") .lines() .filter(|line| !line.is_empty() && !line.starts_with('#')) .map(str::split_whitespace) .filter_map(|mut elements| Some((Mime::from_str(elements.next()?).ok()?, elements.next()?))) .map(|(mime, extension)| (ALIASES.get(&mime).cloned().unwrap_or(mime), extension)) .collect() } pub(crate) fn get_alias(mimetype: Mime) -> Mime { ALIASES.get(&mimetype).cloned().unwrap_or(mimetype) } fn get_mime_parents(mimetype: &Mime) -> Vec<&Mime> { PARENTS .iter() .filter_map(|(child, parent)| (child == mimetype).then_some(parent)) .collect() } pub(crate) fn matches_text(mime: &Mime) -> bool { if mime.type_() == mime::TEXT { return true; } return get_mime_parents(mime).into_iter().any(matches_text); } pub(crate) fn get_extension(mimetype: &Mime) -> Option<&'static str> { let mut queue = VecDeque::new(); queue.push_back(mimetype); while let Some(mime) = queue.pop_front() { match EXTENSIONS.get(mimetype).copied() { Some(ext) => return Some(ext), None => queue.extend(get_mime_parents(mime)), } } None }