zf

zenflows testing
git clone https://s.sonu.ch/~srfsh/zf.git
Log | Files | Refs | Submodules | README | LICENSE

mime.ex (7438B)


      1 defmodule MIME do
      2   @moduledoc """
      3   Maps MIME types to its file extensions and vice versa.
      4 
      5   MIME types can be extended in your application configuration
      6   as follows:
      7 
      8       config :mime, :types, %{
      9         "application/vnd.api+json" => ["json-api"]
     10       }
     11 
     12   After adding the configuration, MIME needs to be recompiled.
     13   If you are using mix, it can be done with:
     14 
     15       $ mix deps.clean mime --build
     16 
     17   """
     18 
     19   types = %{
     20     "application/atom+xml" => ["atom"],
     21     "application/epub+zip" => ["epub"],
     22     "application/gzip" => ["gz"],
     23     "application/java-archive" => ["jar"],
     24     "application/javascript" => ["js"],
     25     "application/json" => ["json"],
     26     "application/json-patch+json" => ["json-patch"],
     27     "application/ld+json" => ["jsonld"],
     28     "application/manifest+json" => ["webmanifest"],
     29     "application/msword" => ["doc"],
     30     "application/octet-stream" => ["bin"],
     31     "application/ogg" => ["ogx"],
     32     "application/pdf" => ["pdf"],
     33     "application/postscript" => ["ps", "eps", "ai"],
     34     "application/rss+xml" => ["rss"],
     35     "application/rtf" => ["rtf"],
     36     "application/vnd.amazon.ebook" => ["azw"],
     37     "application/vnd.api+json" => ["json-api"],
     38     "application/vnd.apple.installer+xml" => ["mpkg"],
     39     "application/vnd.etsi.asic-e+zip" => ["asice", "sce"],
     40     "application/vnd.etsi.asic-s+zip" => ["asics", "scs"],
     41     "application/vnd.mozilla.xul+xml" => ["xul"],
     42     "application/vnd.ms-excel" => ["xls"],
     43     "application/vnd.ms-fontobject" => ["eot"],
     44     "application/vnd.ms-powerpoint" => ["ppt"],
     45     "application/vnd.oasis.opendocument.presentation" => ["odp"],
     46     "application/vnd.oasis.opendocument.spreadsheet" => ["ods"],
     47     "application/vnd.oasis.opendocument.text" => ["odt"],
     48     "application/vnd.openxmlformats-officedocument.presentationml.presentation" => ["pptx"],
     49     "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" => ["xlsx"],
     50     "application/vnd.openxmlformats-officedocument.wordprocessingml.document" => ["docx"],
     51     "application/vnd.rar" => ["rar"],
     52     "application/vnd.visio" => ["vsd"],
     53     "application/wasm" => ["wasm"],
     54     "application/x-7z-compressed" => ["7z"],
     55     "application/x-abiword" => ["abw"],
     56     "application/x-bzip" => ["bz"],
     57     "application/x-bzip2" => ["bz2"],
     58     "application/x-cdf" => ["cda"],
     59     "application/x-csh" => ["csh"],
     60     "application/x-freearc" => ["arc"],
     61     "application/x-httpd-php" => ["php"],
     62     "application/x-msaccess" => ["mdb"],
     63     "application/x-sh" => ["sh"],
     64     "application/x-shockwave-flash" => ["swf"],
     65     "application/x-tar" => ["tar"],
     66     "application/xhtml+xml" => ["xhtml"],
     67     "application/xml" => ["xml"],
     68     "application/zip" => ["zip"],
     69     "audio/3gpp" => ["3gp"],
     70     "audio/3gpp2" => ["3g2"],
     71     "audio/aac" => ["aac"],
     72     "audio/midi" => ["mid", "midi"],
     73     "audio/mpeg" => ["mp3"],
     74     "audio/ogg" => ["oga"],
     75     "audio/opus" => ["opus"],
     76     "audio/wav" => ["wav"],
     77     "audio/webm" => ["weba"],
     78     "font/otf" => ["otf"],
     79     "font/ttf" => ["ttf"],
     80     "font/woff" => ["woff"],
     81     "font/woff2" => ["woff2"],
     82     "image/avif" => ["avif"],
     83     "image/bmp" => ["bmp"],
     84     "image/gif" => ["gif"],
     85     "image/heic" => ["heic"],
     86     "image/heif" => ["heif"],
     87     "image/jpeg" => ["jpg", "jpeg"],
     88     "image/jxl" => ["jxl"],
     89     "image/png" => ["png"],
     90     "image/svg+xml" => ["svg", "svgz"],
     91     "image/tiff" => ["tiff", "tif"],
     92     "image/vnd.adobe.photoshop" => ["psd"],
     93     "image/vnd.microsoft.icon" => ["ico"],
     94     "image/webp" => ["webp"],
     95     "text/calendar" => ["ics"],
     96     "text/css" => ["css"],
     97     "text/csv" => ["csv"],
     98     "text/html" => ["html", "htm"],
     99     "text/javascript" => ["js", "mjs"],
    100     "text/markdown" => ["md", "markdown"],
    101     "text/plain" => ["txt", "text"],
    102     "text/xml" => ["xml"],
    103     "video/3gpp" => ["3gp"],
    104     "video/3gpp2" => ["3g2"],
    105     "video/mp2t" => ["ts"],
    106     "video/mp4" => ["mp4"],
    107     "video/mpeg" => ["mpeg", "mpg"],
    108     "video/ogg" => ["ogv"],
    109     "video/quicktime" => ["mov"],
    110     "video/webm" => ["webm"],
    111     "video/x-ms-wmv" => ["wmv"],
    112     "video/x-msvideo" => ["avi"]
    113   }
    114 
    115   require Application
    116   custom_types = Application.compile_env(:mime, :types, %{})
    117 
    118   to_exts = fn map ->
    119     for {media, exts} <- map, ext <- exts, reduce: %{} do
    120       acc -> Map.update(acc, ext, [media], &[media | &1])
    121     end
    122   end
    123 
    124   exts =
    125     Map.merge(to_exts.(types), %{
    126       "3g2" => ["video/3gpp2"],
    127       "3gp" => ["video/3gpp"],
    128       "js" => ["text/javascript"],
    129       "xml" => ["text/xml"]
    130     })
    131 
    132   for {ext, [_, _ | _] = mimes} <- exts do
    133     raise "conflicting MIMEs for extension .#{ext}, please override: #{inspect(mimes)}"
    134   end
    135 
    136   all_exts = Map.merge(exts, to_exts.(custom_types))
    137   all_types = Map.merge(types, custom_types)
    138 
    139   @doc """
    140   Returns the custom types compiled into the MIME module.
    141   """
    142   def compiled_custom_types do
    143     unquote(Macro.escape(custom_types))
    144   end
    145 
    146   @doc """
    147   Returns the extensions associated with a given MIME type.
    148 
    149   ## Examples
    150 
    151       iex> MIME.extensions("text/html")
    152       ["html", "htm"]
    153 
    154       iex> MIME.extensions("application/json")
    155       ["json"]
    156 
    157       iex> MIME.extensions("application/vnd.custom+xml")
    158       ["xml"]
    159 
    160       iex> MIME.extensions("foo/bar")
    161       []
    162 
    163   """
    164   @spec extensions(String.t()) :: [String.t()]
    165   def extensions(type) do
    166     mime =
    167       type
    168       |> strip_params()
    169       |> downcase("")
    170 
    171     mime_to_ext(mime) || suffix(mime) || []
    172   end
    173 
    174   defp suffix(type) do
    175     case String.split(type, "+") do
    176       [_type_subtype_without_suffix, suffix] -> [suffix]
    177       _ -> nil
    178     end
    179   end
    180 
    181   @default_type "application/octet-stream"
    182 
    183   @doc """
    184   Returns the MIME type associated with a file extension.
    185 
    186   If no MIME type is known for `file_extension`,
    187   `#{inspect(@default_type)}` is returned.
    188 
    189   ## Examples
    190 
    191       iex> MIME.type("txt")
    192       "text/plain"
    193 
    194       iex> MIME.type("foobarbaz")
    195       #{inspect(@default_type)}
    196 
    197   """
    198   @spec type(String.t()) :: String.t()
    199   def type(file_extension) do
    200     ext_to_mime(file_extension) || @default_type
    201   end
    202 
    203   @doc """
    204   Returns whether an extension has a MIME type registered.
    205 
    206   ## Examples
    207 
    208       iex> MIME.has_type?("txt")
    209       true
    210 
    211       iex> MIME.has_type?("foobarbaz")
    212       false
    213 
    214   """
    215   @spec has_type?(String.t()) :: boolean
    216   def has_type?(file_extension) do
    217     is_binary(ext_to_mime(file_extension))
    218   end
    219 
    220   @doc """
    221   Guesses the MIME type based on the path's extension. See `type/1`.
    222 
    223   ## Examples
    224 
    225       iex> MIME.from_path("index.html")
    226       "text/html"
    227 
    228   """
    229   @spec from_path(Path.t()) :: String.t()
    230   def from_path(path) do
    231     case Path.extname(path) do
    232       "." <> ext -> type(downcase(ext, ""))
    233       _ -> @default_type
    234     end
    235   end
    236 
    237   defp strip_params(string) do
    238     string |> :binary.split(";") |> hd()
    239   end
    240 
    241   defp downcase(<<h, t::binary>>, acc) when h in ?A..?Z,
    242     do: downcase(t, <<acc::binary, h + 32>>)
    243 
    244   defp downcase(<<h, t::binary>>, acc), do: downcase(t, <<acc::binary, h>>)
    245   defp downcase(<<>>, acc), do: acc
    246 
    247   @spec ext_to_mime(String.t()) :: String.t() | nil
    248   defp ext_to_mime(type)
    249 
    250   for {ext, [type | _]} <- all_exts do
    251     defp ext_to_mime(unquote(ext)), do: unquote(type)
    252   end
    253 
    254   defp ext_to_mime(_ext), do: nil
    255 
    256   @spec mime_to_ext(String.t()) :: list(String.t()) | nil
    257   defp mime_to_ext(type)
    258 
    259   for {type, exts} <- all_types do
    260     defp mime_to_ext(unquote(type)), do: unquote(List.wrap(exts))
    261   end
    262 
    263   defp mime_to_ext(_type), do: nil
    264 end