-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
645 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
Manifest.toml | ||
.DS_Store |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
[mimedb] | ||
git-tree-sha1 = "6ac9178816f707e7ed89993630f34ab12ac0176e" | ||
|
||
[[mimedb.download]] | ||
sha256 = "b8e70bb4d52acd5d0d1ed848c0e6e3c903a533aa500acffbe003f011b18f9e3b" | ||
url = "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
name = "MIMEs" | ||
uuid = "6c6e2e6c-3030-632d-7369-2d6c69616d65" | ||
license = "MIT" | ||
authors = ["Fons van der Plas <[email protected]>"] | ||
version = "0.1.0" | ||
|
||
[deps] | ||
Artifacts = "56f22d72-fd6d-98f1-02f0-08ddc0907c33" | ||
JSON = "682c06a0-de6a-54ab-a142-c8b1cf79cde6" | ||
|
||
[compat] | ||
JSON = "0.21" | ||
julia = "1.3" | ||
|
||
[extras] | ||
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" | ||
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" | ||
|
||
[targets] | ||
test = ["Test", "Random"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,22 @@ | ||
# MIMEs | ||
Get a MIME type from a filename! | ||
# MIMEs.jl | ||
A small package to transform between file extensions and MIME types. | ||
|
||
# Examples | ||
```julia | ||
julia> using MIMEs | ||
|
||
julia> m = mime_from_extension(".json") | ||
MIME type application/json | ||
|
||
julia> extension_from_mime(m) | ||
".json" | ||
|
||
julia> contenttype_from_mime(m) | ||
"application/json; charset=utf-8" | ||
``` | ||
|
||
# Implementation | ||
|
||
This package uses the [jshttp/mime-db](https://github.com/jshttp/mime-db) database, made available using Artifacts. The function implementations, including resolution for conflicting extensions, is based on [jshttp/mime-types](https://github.com/jshttp/mime-types). | ||
|
||
The database parsing and processing happens during precompilation, lookups are very fast. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,207 @@ | ||
module MIMEs | ||
|
||
export mime_from_extension, mime_from_path, extension_from_mime, charset_from_mime, compressible_from_mime, contenttype_from_mime | ||
|
||
import JSON | ||
using Artifacts | ||
|
||
|
||
const _mimedb_root = artifact"mimedb" | ||
|
||
const _mimedb_json = joinpath(_mimedb_root, "package", "db.json") | ||
const _mimedb = JSON.parse(read(_mimedb_json, String)) | ||
|
||
# https://github.com/jshttp/mime-db/issues/194 | ||
_mimedb["text/javascript"], _mimedb["application/javascript"] = _mimedb["application/javascript"], _mimedb["text/javascript"] | ||
|
||
|
||
const _source_preference = ("nginx", "apache", nothing, "iana") | ||
|
||
const _ext2mime = Dict{String,String}() | ||
const _mime2ext = Dict{String,Vector}() | ||
|
||
for (mime_str, val) in _mimedb | ||
mime = mime_str | ||
|
||
exts = get(val, "extensions", nothing) | ||
if exts === nothing | ||
continue | ||
end | ||
_mime2ext[mime] = exts | ||
|
||
src = get(val, "source", nothing) | ||
for ex in exts | ||
if haskey(_ext2mime, ex) | ||
other_src = get(_mimedb[identity(_ext2mime[ex])], "source", nothing) | ||
|
||
from = findfirst(isequal(other_src), _source_preference) | ||
to = findfirst(isequal(src), _source_preference) | ||
|
||
if ( | ||
!(_ext2mime[ex] isa MIME"application/octet-stream") && | ||
( | ||
from > to || | ||
(from == to && startswith(identity(_ext2mime[ex]), "application/") | ||
) | ||
) | ||
) | ||
# skip the remapping | ||
continue | ||
end | ||
end | ||
|
||
_ext2mime[ex] = mime | ||
end | ||
|
||
end | ||
|
||
""" | ||
```julia | ||
mime_from_extension(query::String[, default=nothing])::MIME | ||
``` | ||
# Examples: | ||
```julia | ||
mime_from_extension(".json") == MIME"application/json"() | ||
mime_from_extension("html") == MIME"text/html"() | ||
mime_from_extension("asdfff") == nothing | ||
mime_from_extension("asdfff", MIME"text/plain"()) == MIME"text/plain"() | ||
``` | ||
""" | ||
function mime_from_extension(query::String, default=nothing) | ||
m = get(_ext2mime, lowercase(lstrip(query, '.')), nothing) | ||
m === nothing ? default : MIME(m) | ||
end | ||
|
||
|
||
""" | ||
```julia | ||
mime_from_path(path::String[, default::T=nothing])::Union{MIME,T} | ||
``` | ||
Return the MIME type of the file at `path`, based on the file extension. | ||
# Examples: | ||
```julia | ||
mime_from_path("hello.json") == MIME"application/json"() | ||
mime_from_path("/home/fons/wow.html") == MIME"text/html"() | ||
mime_from_path("/home/fons/wow") == nothing | ||
mime_from_path("/home/fons/wow", MIME"text/plain"()) == MIME"text/plain"() | ||
``` | ||
""" | ||
function mime_from_path(path::String, default=nothing) | ||
mime_from_extension(splitext(path)[2], default) | ||
end | ||
|
||
|
||
""" | ||
```julia | ||
extension_from_mime(mime::MIME[, default::T=""])::Union{String,T} | ||
``` | ||
Return the most common file extension used for files of the given MIME type. | ||
# Examples: | ||
```julia | ||
extension_from_mime(MIME"application/json"()) == ".json" | ||
extension_from_mime(MIME"text/html"()) == ".html" | ||
extension_from_mime(MIME"text/blablablaa"()) == "" | ||
extension_from_mime(MIME"text/blablablaa"(), ".bin") == ".bin" | ||
``` | ||
""" | ||
function extension_from_mime(mime::MIME, default="") | ||
exs = get(_mime2ext, string(mime), nothing) | ||
if exs === nothing || isempty(exs) | ||
default | ||
else | ||
"." * first(exs) | ||
end | ||
end | ||
|
||
|
||
|
||
|
||
""" | ||
```julia | ||
compressible_from_mime(mime::MIME)::Bool | ||
``` | ||
Whether a file of the given MIME type can/should be gzipped. | ||
# Examples: | ||
```julia | ||
compressible_from_mime(MIME"text/html"()) == true | ||
compressible_from_mime(MIME"image/png"()) == false | ||
compressible_from_mime(MIME"text/blablablaa"()) == false | ||
``` | ||
""" | ||
function compressible_from_mime(mime::MIME) | ||
c = get(_mimedb, string(mime), nothing) | ||
if c === nothing | ||
false | ||
else | ||
get(c, "compressible", false) | ||
end | ||
end | ||
|
||
|
||
|
||
""" | ||
```julia | ||
charset_from_mime(mime::MIME)::String | ||
``` | ||
The default charset associated with this type, if any. If not known, text MIMEs default to "UTF-8". Possible values are: $(Set([get(x, "charset", nothing) for x in (_mimedb |> values)]) |> collect |> string). | ||
# Examples: | ||
```julia | ||
charset_from_mime(MIME"application/json"()) == "UTF-8" | ||
charset_from_mime(MIME"application/x-bogus"()) == nothing | ||
charset_from_mime(MIME"text/blablablaa"()) == "UTF-8" # because it is a `text/` mime | ||
charset_from_mime(MIME"text/blablablaa"(), "LATIN-1") == "LATIN-1" | ||
``` | ||
""" | ||
function charset_from_mime(mime::MIME) | ||
fallback() = istextmime(mime) ? "UTF-8" : nothing | ||
c = get(_mimedb, string(mime), nothing) | ||
if c === nothing | ||
fallback() | ||
else | ||
get(fallback, c, "charset")::Union{String,Nothing} | ||
end | ||
end | ||
|
||
|
||
function charset_from_mime(mime::MIME, default) | ||
c = get(_mimedb, string(mime), nothing) | ||
if c === nothing | ||
default | ||
else | ||
get(c, "charset", default) | ||
end | ||
end | ||
|
||
|
||
|
||
""" | ||
```julia | ||
contenttype_from_mime(mime::MIME)::String | ||
``` | ||
Turn a MIME into a Content-Type header value, which might include the `charset` parameter. | ||
# Examples: | ||
```julia | ||
contenttype_from_mime(MIME"application/json"()) == "application/json; charset=utf-8" | ||
contenttype_from_mime(MIME"application/x-bogus"()) == "application/x-bogus" | ||
``` | ||
# See also: | ||
[`charset_from_mime`](@ref) | ||
""" | ||
contenttype_from_mime(mime::MIME) = let c = charset_from_mime(mime) | ||
c === nothing ? string(mime) : "$(string(mime)); charset=$(lowercase(c))" | ||
end | ||
|
||
|
||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
using Test | ||
|
||
using MIMEs | ||
|
||
@test mime_from_path("a/foo.txt") === MIME"text/plain"() | ||
@test mime_from_path("a/foo.json") === MIME"application/json"() | ||
@test mime_from_extension(".json") === MIME"application/json"() | ||
@test mime_from_extension("json") === MIME"application/json"() | ||
@test mime_from_extension("JSON") === MIME"application/json"() | ||
@test extension_from_mime(MIME"application/json"()) == ".json" | ||
@test extension_from_mime(MIME"application/x-asfdafd"()) == "" | ||
@test extension_from_mime(MIME"application/x-asfdafd"(), ".a") == ".a" | ||
|
||
|
||
@test charset_from_mime(MIME"application/x-asfdafd"()) == nothing | ||
@test charset_from_mime(MIME"text/x-asfdafd"()) == "UTF-8" | ||
@test charset_from_mime(MIME"text/plain"()) == "UTF-8" | ||
@test charset_from_mime(MIME"text/html"()) == "UTF-8" | ||
@test contenttype_from_mime(MIME"application/x-asfdafd"()) == "application/x-asfdafd" | ||
@test contenttype_from_mime(MIME"text/html"()) == "text/html; charset=utf-8" | ||
|
||
@test mime_from_extension("js") == MIME"text/javascript"() | ||
|
||
@test compressible_from_mime(MIME"text/html"()) | ||
@test compressible_from_mime(MIME"text/css"()) | ||
@test !compressible_from_mime(MIME"text/x-sadfa"()) | ||
|
||
@test contenttype_from_mime(MIME"application/json"()) == "application/json; charset=utf-8" | ||
@test contenttype_from_mime(MIME"application/x-bogus"()) == "application/x-bogus" | ||
|
||
# from https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types | ||
const mdn = Dict( | ||
".bin" => "application/octet-stream", | ||
".bmp" => "image/bmp", | ||
".css" => "text/css", | ||
".csv" => "text/csv", | ||
".eot" => "application/vnd.ms-fontobject", | ||
".gz" => "application/gzip", | ||
".gif" => "image/gif", | ||
".htm" => "text/html", | ||
".html" => "text/html", | ||
".ico" => "image/vnd.microsoft.icon", | ||
".jpeg" => "image/jpeg", | ||
".jpg" => "image/jpeg", | ||
".js" => "text/javascript", | ||
".json" => "application/json", | ||
".jsonld" => "application/ld+json", | ||
".mjs" => "text/javascript", | ||
".mp3" => "audio/mpeg", | ||
".mp4" => "video/mp4", | ||
".mpeg" => "video/mpeg", | ||
".ogx" => "application/ogg", ".otf" => "font/otf", | ||
".png" => "image/png", | ||
".pdf" => "application/pdf", | ||
".rtf" => "application/rtf", | ||
".sh" => "application/x-sh", | ||
".svg" => "image/svg+xml", | ||
".tar" => "application/x-tar", | ||
".tif" => "image/tiff", | ||
".tiff" => "image/tiff", | ||
".ttf" => "font/ttf", | ||
".txt" => "text/plain", | ||
".weba" => "audio/webm", | ||
".webm" => "video/webm", | ||
".webp" => "image/webp", | ||
".woff" => "font/woff", | ||
".woff2" => "font/woff2", | ||
".xhtml" => "application/xhtml+xml", | ||
".xml" => "application/xml", | ||
".xul" => "application/vnd.mozilla.xul+xml", | ||
".zip" => "application/zip", | ||
".wasm" => "application/wasm", | ||
) | ||
|
||
for (ex, ms) in mdn | ||
@test mime_from_extension(ex) === MIME(ms) | ||
end | ||
|
||
|
||
# Mismatched with MDN, but that's fine: | ||
# ".aac" => "audio/aac", | ||
# ".wav" => "audio/wav", | ||
# ".oga" => "audio/ogg", ".ogv" => "video/ogg", | ||
# ".opus" => "audio/opus", |
Oops, something went wrong.