pub extern crate mime;
extern crate unicase;
pub use mime::Mime;
use std::ffi::OsStr;
use std::iter::FusedIterator;
use std::path::Path;
use std::{iter, slice};
#[cfg(feature = "phf")]
#[path = "impl_phf.rs"]
mod impl_;
#[cfg(not(feature = "phf"))]
#[path = "impl_bin_search.rs"]
mod impl_;
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct MimeGuess(&'static [&'static str]);
impl MimeGuess {
pub fn from_ext(ext: &str) -> MimeGuess {
if ext.is_empty() {
return MimeGuess(&[]);
}
impl_::get_mime_types(ext).map_or(MimeGuess(&[]), |v| MimeGuess(v))
}
pub fn from_path<P: AsRef<Path>>(path: P) -> MimeGuess {
path.as_ref()
.extension()
.and_then(OsStr::to_str)
.map_or(MimeGuess(&[]), Self::from_ext)
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn count(&self) -> usize {
self.0.len()
}
pub fn first(&self) -> Option<Mime> {
self.first_raw().map(expect_mime)
}
pub fn first_raw(&self) -> Option<&'static str> {
self.0.get(0).cloned()
}
pub fn first_or_octet_stream(&self) -> Mime {
self.first_or(mime::APPLICATION_OCTET_STREAM)
}
pub fn first_or_text_plain(&self) -> Mime {
self.first_or(mime::TEXT_PLAIN)
}
pub fn first_or(&self, default: Mime) -> Mime {
self.first().unwrap_or(default)
}
pub fn first_or_else<F>(&self, default_fn: F) -> Mime
where
F: FnOnce() -> Mime,
{
self.first().unwrap_or_else(default_fn)
}
pub fn iter(&self) -> Iter {
Iter(self.iter_raw().map(expect_mime))
}
pub fn iter_raw(&self) -> IterRaw {
IterRaw(self.0.iter().cloned())
}
}
impl IntoIterator for MimeGuess {
type Item = Mime;
type IntoIter = Iter;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl<'a> IntoIterator for &'a MimeGuess {
type Item = Mime;
type IntoIter = Iter;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
#[derive(Clone, Debug)]
pub struct Iter(iter::Map<IterRaw, fn(&'static str) -> Mime>);
impl Iterator for Iter {
type Item = Mime;
fn next(&mut self) -> Option<Self::Item> {
self.0.next()
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.0.size_hint()
}
}
impl DoubleEndedIterator for Iter {
fn next_back(&mut self) -> Option<Self::Item> {
self.0.next_back()
}
}
impl FusedIterator for Iter {}
impl ExactSizeIterator for Iter {
fn len(&self) -> usize {
self.0.len()
}
}
#[derive(Clone, Debug)]
pub struct IterRaw(iter::Cloned<slice::Iter<'static, &'static str>>);
impl Iterator for IterRaw {
type Item = &'static str;
fn next(&mut self) -> Option<Self::Item> {
self.0.next()
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.0.size_hint()
}
}
impl DoubleEndedIterator for IterRaw {
fn next_back(&mut self) -> Option<Self::Item> {
self.0.next_back()
}
}
impl FusedIterator for IterRaw {}
impl ExactSizeIterator for IterRaw {
fn len(&self) -> usize {
self.0.len()
}
}
fn expect_mime(s: &str) -> Mime {
s.parse()
.unwrap_or_else(|e| panic!("failed to parse media-type {:?}: {}", s, e))
}
pub fn from_ext(ext: &str) -> MimeGuess {
MimeGuess::from_ext(ext)
}
pub fn from_path<P: AsRef<Path>>(path: P) -> MimeGuess {
MimeGuess::from_path(path)
}
#[deprecated(
since = "2.0.0",
note = "Use `from_path(path).first_or_octet_stream()` instead"
)]
pub fn guess_mime_type<P: AsRef<Path>>(path: P) -> Mime {
from_path(path).first_or_octet_stream()
}
#[deprecated(since = "2.0.0", note = "Use `from_path(path).first()` instead")]
pub fn guess_mime_type_opt<P: AsRef<Path>>(path: P) -> Option<Mime> {
from_path(path).first()
}
#[deprecated(since = "2.0.0", note = "Use `from_path(path).first_raw()` instead")]
pub fn mime_str_for_path_ext<P: AsRef<Path>>(path: P) -> Option<&'static str> {
from_path(path).first_raw()
}
#[deprecated(
since = "2.0.0",
note = "use `from_ext(search_ext).first_or_octet_stream()` instead"
)]
pub fn get_mime_type(search_ext: &str) -> Mime {
from_ext(search_ext).first_or_octet_stream()
}
#[deprecated(since = "2.0.0", note = "use `from_ext(search_ext).first()` instead")]
pub fn get_mime_type_opt(search_ext: &str) -> Option<Mime> {
from_ext(search_ext).first()
}
#[deprecated(
since = "2.0.0",
note = "use `from_ext(search_ext).first_raw()` instead"
)]
pub fn get_mime_type_str(search_ext: &str) -> Option<&'static str> {
from_ext(search_ext).first_raw()
}
#[cfg(feature = "rev-mappings")]
pub fn get_mime_extensions(mime: &Mime) -> Option<&'static [&'static str]> {
get_extensions(mime.type_().as_ref(), mime.subtype().as_ref())
}
#[cfg(feature = "rev-mappings")]
pub fn get_mime_extensions_str(mut mime_str: &str) -> Option<&'static [&'static str]> {
mime_str = mime_str.trim();
if let Some(sep_idx) = mime_str.find(';') {
mime_str = &mime_str[..sep_idx];
}
let (top, sub) = {
let split_idx = mime_str.find('/').unwrap();
(&mime_str[..split_idx], &mime_str[split_idx + 1..])
};
get_extensions(top, sub)
}
#[cfg(feature = "rev-mappings")]
pub fn get_extensions(toplevel: &str, sublevel: &str) -> Option<&'static [&'static str]> {
impl_::get_extensions(toplevel, sublevel)
}
#[deprecated(since = "2.0.0", note = "use `mime::APPLICATION_OCTET_STREAM` instead")]
pub fn octet_stream() -> Mime {
"application/octet-stream".parse().unwrap()
}
#[cfg(test)]
mod tests {
include!("mime_types.rs");
use super::{from_ext, from_path, expect_mime};
#[allow(deprecated, unused_imports)]
use std::ascii::AsciiExt;
use std::fmt::Debug;
use std::path::Path;
#[test]
fn check_type_bounds() {
fn assert_type_bounds<T: Clone + Debug + Send + Sync + 'static>() {}
assert_type_bounds::<super::MimeGuess>();
assert_type_bounds::<super::Iter>();
assert_type_bounds::<super::IterRaw>();
}
#[test]
fn test_mime_type_guessing() {
assert_eq!(
from_ext("gif").first_or_octet_stream().to_string(),
"image/gif".to_string()
);
assert_eq!(
from_ext("TXT").first_or_octet_stream().to_string(),
"text/plain".to_string()
);
assert_eq!(
from_ext("blahblah").first_or_octet_stream().to_string(),
"application/octet-stream".to_string()
);
assert_eq!(
from_path(Path::new("/path/to/file.gif"))
.first_or_octet_stream()
.to_string(),
"image/gif".to_string()
);
assert_eq!(
from_path("/path/to/file.gif").first_or_octet_stream().to_string(),
"image/gif".to_string()
);
}
#[test]
fn test_mime_type_guessing_opt() {
assert_eq!(
from_ext("gif").first().unwrap().to_string(),
"image/gif".to_string()
);
assert_eq!(
from_ext("TXT").first().unwrap().to_string(),
"text/plain".to_string()
);
assert_eq!(from_ext("blahblah").first(), None);
assert_eq!(
from_path("/path/to/file.gif").first().unwrap().to_string(),
"image/gif".to_string()
);
assert_eq!(from_path("/path/to/file").first(), None);
}
#[test]
fn test_are_mime_types_parseable() {
for (_, mimes) in MIME_TYPES {
mimes.iter().for_each(|s| { expect_mime(s); });
}
}
#[test]
fn test_are_extensions_ascii() {
for (ext, _) in MIME_TYPES {
assert!(ext.is_ascii(), "Extension not ASCII: {:?}", ext);
}
}
#[test]
fn test_are_extensions_sorted() {
for (&(ext, _), &(n_ext, _)) in MIME_TYPES.iter().zip(MIME_TYPES.iter().skip(1)) {
assert!(
ext <= n_ext,
"Extensions in src/mime_types should be sorted lexicographically
in ascending order. Failed assert: {:?} <= {:?}",
ext,
n_ext
);
}
}
}