Skip to content

Commit

Permalink
Merge pull request #15 from ririsoft/static-api
Browse files Browse the repository at this point in the history
add global functions for a full static experience
paolobarbolini authored Jul 16, 2020

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
2 parents 757144f + b5d486a commit 8a1c022
Showing 2 changed files with 201 additions and 117 deletions.
309 changes: 194 additions & 115 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -8,16 +8,14 @@ Small crate to infer file and MIME type by checking the
```rust
let v = vec![0xFF, 0xD8, 0xFF, 0xAA];
let info = infer::Infer::new();
assert_eq!("image/jpeg", info.get(&v).unwrap().mime);
assert_eq!("jpg", info.get(&v).unwrap().ext);
assert_eq!("image/jpeg", infer::get(&v).unwrap().mime);
assert_eq!("jpg", infer::get(&v).unwrap().ext);
```
### Check path
```rust
let info = infer::Infer::new();
let res = info.get_from_path("testdata/sample.jpg");
let res = infer::get_from_path("testdata/sample.jpg");
assert!(res.is_ok());
let o = res.unwrap();
assert!(o.is_some());
@@ -39,8 +37,7 @@ assert!(infer::image::is_jpeg(&v));
```rust
let v = vec![0xFF, 0xD8, 0xFF, 0xAA];
let info = infer::Infer::new();
assert!(info.is_image(&v));
assert!(infer::is_image(&v));
```
### Adds a custom file type matcher
@@ -69,7 +66,7 @@ use std::fs::File;
use std::io::Read;
use std::path::Path;

use map::{MatcherType, MATCHER_MAP};
use map::{MatcherType, WrapMatcher, MATCHER_MAP};

/// All the supported matchers categorized and exposed as functions
pub use matchers::*;
@@ -87,18 +84,18 @@ pub struct Type {
pub ext: String,
}

/// Infer is the main struct of the module
/// Infer allows to use a custom set of `Matcher`s for infering a MIME type.
pub struct Infer {
mmap: Vec<(map::MatcherType, String, String, Matcher)>,
mmap: Vec<(map::MatcherType, String, String, WrapMatcher)>,
}

impl Infer {
/// Initialize a new instance of the infer struct.
pub fn new() -> Infer {
pub const fn new() -> Infer {
Infer { mmap: Vec::new() }
}

fn iter_matchers(&self) -> impl Iterator<Item = (&MatcherType, &str, &str, &Matcher)> {
fn iter_matchers(&self) -> impl Iterator<Item = (&MatcherType, &str, &str, &WrapMatcher)> {
let mmap = MATCHER_MAP
.iter()
.map(|(mt, mime, ext, matcher)| (mt, *mime, *ext, matcher));
@@ -110,17 +107,10 @@ impl Infer {

/// Returns the file type of the buffer.
///
/// # Examples
///
/// ```rust
/// let info = infer::Infer::new();
/// let v = vec![0xFF, 0xD8, 0xFF, 0xAA];
/// assert_eq!("image/jpeg", info.get(&v).unwrap().mime);
/// assert_eq!("jpg", info.get(&v).unwrap().ext);
/// ```
/// See [`get`](./fn.get.html).
pub fn get(&self, buf: &[u8]) -> Option<Type> {
for (_, mime, ext, matcher) in self.iter_matchers() {
if matcher(buf) {
if matcher.0(buf) {
return Some(Type {
mime: mime.to_string(),
ext: ext.to_string(),
@@ -133,22 +123,7 @@ impl Infer {

/// Returns the file type of the file given a path.
///
/// # Errors
///
/// Returns an error if we fail to read the path.
///
/// # Examples
///
/// ```rust
/// let info = infer::Infer::new();
/// let res = info.get_from_path("testdata/sample.jpg");
/// assert!(res.is_ok());
/// let o = res.unwrap();
/// assert!(o.is_some());
/// let typ = o.unwrap();
/// assert_eq!("image/jpeg", typ.mime);
/// assert_eq!("jpg", typ.ext);
/// ```
/// See [`get_from_path`](./fn.get_from_path.html).
pub fn get_from_path<P: AsRef<Path>>(&self, path: P) -> Result<Option<Type>, std::io::Error> {
let file = File::open(path)?;

@@ -164,19 +139,13 @@ impl Infer {

/// Determines whether a buffer is of given extension.
///
/// # Examples
///
/// ```rust
/// let info = infer::Infer::new();
/// let v = vec![0xFF, 0xD8, 0xFF, 0xAA];
/// assert!(info.is(&v, "jpg"));
/// ```
/// See [`is`](./fn.is.html).
pub fn is(&self, buf: &[u8], ext: &str) -> bool {
if let Some((_mt, _mi, _e, matcher)) = self
.iter_matchers()
.find(|(_mt, _mime, ex, _matcher)| *ex == ext)
{
if matcher(buf) {
if matcher.0(buf) {
return true;
}
}
@@ -186,19 +155,13 @@ impl Infer {

/// Determines whether a buffer is of given mime type.
///
/// # Examples
///
/// ```rust
/// let info = infer::Infer::new();
/// let v = vec![0xFF, 0xD8, 0xFF, 0xAA];
/// assert!(info.is_mime(&v, "image/jpeg"));
/// ```
/// See [`is_mime`](./fn.is_mime.html).
pub fn is_mime(&self, buf: &[u8], mime: &str) -> bool {
if let Some((_mt, _mi, _e, matcher)) = self
.iter_matchers()
.find(|(_mt, mi, _ext, _matcher)| *mi == mime)
{
if matcher(buf) {
if matcher.0(buf) {
return true;
}
}
@@ -208,12 +171,7 @@ impl Infer {

/// Returns whether an extension is supported.
///
/// # Examples
///
/// ```rust
/// let info = infer::Infer::new();
/// assert!(info.is_supported("jpg"));
/// ```
/// See [`is_supported`](./fn.is_supported.html).
pub fn is_supported(&self, ext: &str) -> bool {
for (_mt, _mime, type_ext, _matcher) in self.iter_matchers() {
if ext == type_ext {
@@ -226,12 +184,7 @@ impl Infer {

/// Returns whether a mime type is supported.
///
/// # Examples
///
/// ```rust
/// let info = infer::Infer::new();
/// assert!(info.is_mime_supported("image/jpeg"));
/// ```
/// See [`is_mime_supported`](./fn.is_mime_supported.html).
pub fn is_mime_supported(&self, mime: &str) -> bool {
for (_mt, type_mime, _ext, _matcher) in self.iter_matchers() {
if mime == type_mime {
@@ -244,25 +197,15 @@ impl Infer {

/// Determines whether a buffer is an application type.
///
/// # Examples
///
/// ```rust
/// use std::fs;
/// let info = infer::Infer::new();
/// assert!(info.is_app(&fs::read("testdata/sample.wasm").unwrap()));
/// ```
/// See [`is_app`](./fn.is_app.html).
pub fn is_app(&self, buf: &[u8]) -> bool {
self.is_type(buf, map::MatcherType::APP)
}

/// Determines whether a buffer is an archive type.
/// # Examples
///
/// ```rust
/// use std::fs;
/// let info = infer::Infer::new();
/// assert!(info.is_archive(&fs::read("testdata/sample.pdf").unwrap()));
/// ```
/// See [`is_archive`](./fn.is_archive.html).
pub fn is_archive(&self, buf: &[u8]) -> bool {
self.is_type(buf, map::MatcherType::ARCHIVE)
}
@@ -271,64 +214,35 @@ impl Infer {
///
/// # Examples
///
/// ```rust
/// // mp3
/// let info = infer::Infer::new();
/// let v = vec![0xff, 0xfb, 0x90, 0x44, 0x00];
/// assert!(info.is_audio(&v));
/// ```
/// See [`is_audio`](./fn.is_audio.html).
pub fn is_audio(&self, buf: &[u8]) -> bool {
self.is_type(buf, map::MatcherType::AUDIO)
}

/// Determines whether a buffer is a document type.
///
/// # Examples
///
/// ```rust
/// use std::fs;
/// let info = infer::Infer::new();
/// assert!(info.is_document(&fs::read("testdata/sample.docx").unwrap()));
/// ```
/// See [`is_document`](./fn.is_document.html).
pub fn is_document(&self, buf: &[u8]) -> bool {
self.is_type(buf, map::MatcherType::DOC)
}

/// Determines whether a buffer is a font type.
///
/// # Examples
///
/// ```rust
/// use std::fs;
/// let info = infer::Infer::new();
/// assert!(info.is_font(&fs::read("testdata/sample.ttf").unwrap()));
/// ```
/// See [`is_font`](./fn.is_font.html).
pub fn is_font(&self, buf: &[u8]) -> bool {
self.is_type(buf, map::MatcherType::FONT)
}

/// Determines whether a buffer is an image type.
///
/// # Examples
///
/// ```rust
/// let v = vec![0xFF, 0xD8, 0xFF, 0xAA];
/// let info = infer::Infer::new();
/// assert!(info.is_image(&v));
/// ```
/// See [`is_image`](./fn.is_image.html).
pub fn is_image(&self, buf: &[u8]) -> bool {
self.is_type(buf, map::MatcherType::IMAGE)
}

/// Determines whether a buffer is a video type.
///
/// # Examples
///
/// ```rust
/// use std::fs;
/// let info = infer::Infer::new();
/// assert!(info.is_video(&fs::read("testdata/sample.mov").unwrap()));
/// ```
/// See [`is_video`](./fn.is_video.html).
pub fn is_video(&self, buf: &[u8]) -> bool {
self.is_type(buf, map::MatcherType::VIDEO)
}
@@ -375,7 +289,7 @@ impl Infer {
map::MatcherType::CUSTOM,
mime.to_string(),
ext.to_string(),
m,
WrapMatcher(m),
));
}

@@ -384,7 +298,7 @@ impl Infer {
.iter_matchers()
.filter(|(mt, _mime, _e, _matcher)| **mt == typ)
{
if matcher(buf) {
if matcher.0(buf) {
return true;
}
}
@@ -399,6 +313,172 @@ impl Default for Infer {
}
}

static INFER: Infer = Infer::new();

/// Returns the file type of the buffer.
///
/// # Examples
///
/// ```rust
/// let v = vec![0xFF, 0xD8, 0xFF, 0xAA];
/// assert_eq!("image/jpeg", infer::get(&v).unwrap().mime);
/// assert_eq!("jpg", infer::get(&v).unwrap().ext);
/// ```
pub fn get(buf: &[u8]) -> Option<Type> {
INFER.get(buf)
}

/// Returns the file type of the file given a path.
///
/// # Errors
///
/// Returns an error if we fail to read the path.
///
/// # Examples
///
/// ```rust
/// let res = infer::get_from_path("testdata/sample.jpg");
/// assert!(res.is_ok());
/// let o = res.unwrap();
/// assert!(o.is_some());
/// let typ = o.unwrap();
/// assert_eq!("image/jpeg", typ.mime);
/// assert_eq!("jpg", typ.ext);
/// ```
pub fn get_from_path<P: AsRef<Path>>(path: P) -> Result<Option<Type>, std::io::Error> {
INFER.get_from_path(path)
}

/// Determines whether a buffer is of given extension.
///
/// # Examples
///
/// ```rust
/// let v = vec![0xFF, 0xD8, 0xFF, 0xAA];
/// assert!(infer::is(&v, "jpg"));
/// ```
pub fn is(buf: &[u8], ext: &str) -> bool {
INFER.is(buf, ext)
}

/// Determines whether a buffer is of given mime type.
///
/// # Examples
///
/// ```rust
/// let v = vec![0xFF, 0xD8, 0xFF, 0xAA];
/// assert!(infer::is_mime(&v, "image/jpeg"));
/// ```
pub fn is_mime(buf: &[u8], mime: &str) -> bool {
INFER.is_mime(buf, mime)
}

/// Returns whether an extension is supported.
///
/// # Examples
///
/// ```rust
/// assert!(infer::is_supported("jpg"));
/// ```
pub fn is_supported(ext: &str) -> bool {
INFER.is_supported(ext)
}

/// Returns whether a mime type is supported.
///
/// # Examples
///
/// ```rust
/// assert!(infer::is_mime_supported("image/jpeg"));
/// ```
pub fn is_mime_supported(mime: &str) -> bool {
INFER.is_mime_supported(mime)
}

/// Determines whether a buffer is an application type.
///
/// # Examples
///
/// ```rust
/// use std::fs;
/// assert!(infer::is_app(&fs::read("testdata/sample.wasm").unwrap()));
/// ```
pub fn is_app(buf: &[u8]) -> bool {
INFER.is_app(buf)
}

/// Determines whether a buffer is an archive type.
/// # Examples
///
/// ```rust
/// use std::fs;
/// assert!(infer::is_archive(&fs::read("testdata/sample.pdf").unwrap()));
/// ```
pub fn is_archive(buf: &[u8]) -> bool {
INFER.is_archive(buf)
}

/// Determines whether a buffer is an audio type.
///
/// # Examples
///
/// ```rust
/// // mp3
/// let v = vec![0xff, 0xfb, 0x90, 0x44, 0x00];
/// assert!(infer::is_audio(&v));
/// ```
pub fn is_audio(buf: &[u8]) -> bool {
INFER.is_audio(buf)
}

/// Determines whether a buffer is a document type.
///
/// # Examples
///
/// ```rust
/// use std::fs;
/// assert!(infer::is_document(&fs::read("testdata/sample.docx").unwrap()));
/// ```
pub fn is_document(buf: &[u8]) -> bool {
INFER.is_document(buf)
}

/// Determines whether a buffer is a font type.
///
/// # Examples
///
/// ```rust
/// use std::fs;
/// assert!(infer::is_font(&fs::read("testdata/sample.ttf").unwrap()));
/// ```
pub fn is_font(buf: &[u8]) -> bool {
INFER.is_font(buf)
}

/// Determines whether a buffer is an image type.
///
/// # Examples
///
/// ```rust
/// let v = vec![0xFF, 0xD8, 0xFF, 0xAA];
/// assert!(infer::is_image(&v));
/// ```
pub fn is_image(buf: &[u8]) -> bool {
INFER.is_image(buf)
}

/// Determines whether a buffer is a video type.
///
/// # Examples
///
/// ```rust
/// use std::fs;
/// assert!(infer::is_video(&fs::read("testdata/sample.mov").unwrap()));
/// ```
pub fn is_video(buf: &[u8]) -> bool {
INFER.is_video(buf)
}

#[cfg(test)]
mod tests {
use super::Infer;
@@ -413,8 +493,7 @@ mod tests {
#[test]
fn test_get_jpeg() {
let v = vec![0xFF, 0xD8, 0xFF, 0xAA];
let info = Infer::new();
match info.get(&v) {
match crate::get(&v) {
Some(info) => {
assert_eq!(info.ext, "jpg");
assert_eq!(info.mime, "image/jpeg");
9 changes: 7 additions & 2 deletions src/map.rs
Original file line number Diff line number Diff line change
@@ -13,10 +13,15 @@ pub enum MatcherType {
CUSTOM,
}

// This is needed until function pointers can be used in `const fn`.
// See trick and discussion at https://github.com/rust-lang/rust/issues/63997#issuecomment-616666309
#[repr(transparent)]
pub struct WrapMatcher(pub Matcher);

macro_rules! matcher_map {
($(($mtype:expr, $mime:literal, $ext:literal, $matcher:expr)),*) => {
pub const MATCHER_MAP: &[(MatcherType, &'static str, &'static str, Matcher)] = &[
$(($mtype, $mime, $ext, $matcher as Matcher),)*
pub const MATCHER_MAP: &[(MatcherType, &'static str, &'static str, WrapMatcher)] = &[
$(($mtype, $mime, $ext, WrapMatcher($matcher as Matcher)),)*
];
};
}

0 comments on commit 8a1c022

Please sign in to comment.