Source code
Revision control
Copy as Markdown
Other Tools
#[cfg(feature = "phf")]
extern crate phf_codegen;
extern crate unicase;
use unicase::UniCase;
use std::env;
use std::fs::File;
use std::io::prelude::*;
use std::io::BufWriter;
use std::path::Path;
use std::collections::BTreeMap;
use mime_types::MIME_TYPES;
#[path = "src/mime_types.rs"]
mod mime_types;
#[cfg(feature = "phf")]
const PHF_PATH: &str = "::impl_::phf";
fn main() {
let out_dir = env::var("OUT_DIR").unwrap();
let dest_path = Path::new(&out_dir).join("mime_types_generated.rs");
let mut outfile = BufWriter::new(File::create(dest_path).unwrap());
#[cfg(feature = "phf")]
build_forward_map(&mut outfile);
#[cfg(feature = "rev-mappings")]
build_rev_map(&mut outfile);
}
// Build forward mappings (ext -> mime type)
#[cfg(feature = "phf")]
fn build_forward_map<W: Write>(out: &mut W) {
use phf_codegen::Map as PhfMap;
let mut forward_map = PhfMap::new();
forward_map.phf_path(PHF_PATH);
let mut map_entries: Vec<(&str, Vec<&str>)> = Vec::new();
for &(key, types) in MIME_TYPES {
if let Some(&mut (key_, ref mut values)) = map_entries.last_mut() {
// deduplicate extensions
if key == key_ {
values.extend_from_slice(types);
continue;
}
}
map_entries.push((key, types.into()));
}
for (key, values) in map_entries {
forward_map.entry(
UniCase::new(key),
&format!("&{:?}", values),
);
}
writeln!(
out,
"static MIME_TYPES: phf::Map<UniCase<&'static str>, &'static [&'static str]> = \n{};",
forward_map.build()
)
.unwrap();
}
// Build reverse mappings (mime type -> ext)
#[cfg(all(feature = "phf", feature = "rev-mappings"))]
fn build_rev_map<W: Write>(out: &mut W) {
use phf_codegen::Map as PhfMap;
let dyn_map = get_rev_mappings();
let mut rev_map = PhfMap::new();
rev_map.phf_path(PHF_PATH);
let mut exts = Vec::new();
for (top, subs) in dyn_map {
let top_start = exts.len();
let mut sub_map = PhfMap::new();
sub_map.phf_path(PHF_PATH);
for (sub, sub_exts) in subs {
let sub_start = exts.len();
exts.extend(sub_exts);
let sub_end = exts.len();
sub_map.entry(sub, &format!("({}, {})", sub_start, sub_end));
}
let top_end = exts.len();
rev_map.entry(
top,
&format!(
"TopLevelExts {{ start: {}, end: {}, subs: {} }}",
top_start, top_end, sub_map.build()
),
);
}
writeln!(
out,
"static REV_MAPPINGS: phf::Map<UniCase<&'static str>, TopLevelExts> = \n{};",
rev_map.build()
).unwrap();
writeln!(out, "const EXTS: &'static [&'static str] = &{:?};", exts).unwrap();
}
#[cfg(all(not(feature = "phf"), feature = "rev-mappings"))]
fn build_rev_map<W: Write>(out: &mut W) {
use std::fmt::Write as _;
macro_rules! unicase_const {
($s:expr) => ({
format_args!("{}({:?})", (if $s.is_ascii() {
"UniCase::ascii"
} else {
"UniCase::unicode"
}), $s)
})
}
let dyn_map = get_rev_mappings();
write!(out, "static REV_MAPPINGS: &'static [(UniCase<&'static str>, TopLevelExts)] = &[").unwrap();
let mut exts = Vec::new();
for (top, subs) in dyn_map {
let top_start = exts.len();
let mut sub_map = String::new();
for (sub, sub_exts) in subs {
let sub_start = exts.len();
exts.extend(sub_exts);
let sub_end = exts.len();
write!(
sub_map,
"({}, ({}, {})),",
unicase_const!(sub), sub_start, sub_end
).unwrap();
}
let top_end = exts.len();
write!(
out,
"({}, TopLevelExts {{ start: {}, end: {}, subs: &[{}] }}),",
unicase_const!(top), top_start, top_end, sub_map
).unwrap();
}
writeln!(out, "];").unwrap();
writeln!(out, "const EXTS: &'static [&'static str] = &{:?};", exts).unwrap();
}
#[cfg(feature = "rev-mappings")]
fn get_rev_mappings(
) -> BTreeMap<UniCase<&'static str>, BTreeMap<UniCase<&'static str>, Vec<&'static str>>> {
// First, collect all the mime type -> ext mappings)
let mut dyn_map = BTreeMap::new();
for &(key, types) in MIME_TYPES {
for val in types {
let (top, sub) = split_mime(val);
dyn_map
.entry(UniCase::new(top))
.or_insert_with(BTreeMap::new)
.entry(UniCase::new(sub))
.or_insert_with(Vec::new)
.push(key);
}
}
dyn_map
}
fn split_mime(mime: &str) -> (&str, &str) {
let split_idx = mime.find('/').unwrap();
(&mime[..split_idx], &mime[split_idx + 1..])
}