# module MIME

##
Overview

This module implements a global MIME registry.

NOTE To use `MIME`, you must explicitly import it with `require "mime"`

    require "mime"

    MIME.from_extension(".html")         # => "text/html"
    MIME.from_filename("path/file.html") # => "text/html"

The registry will be populated with some default values (see `DEFAULT_TYPES`) as well as the operating system's MIME database.

Default initialization can be skipped by calling `MIME.init(false)` before the first query to the MIME database.

##
OS-provided MIME database

On a POSIX system, the following files are tried to be read in sequential order, stopping at the first existing file. These values override those from `DEFAULT_TYPES`.

    /etc/mime.types
    /etc/httpd/mime.types                    # Mac OS X
    /etc/httpd/conf/mime.types               # Apache
    /etc/apache/mime.types                   # Apache 1
    /etc/apache2/mime.types                  # Apache 2
    /usr/local/etc/httpd/conf/mime.types
    /usr/local/lib/netscape/mime.types
    /usr/local/etc/httpd/conf/mime.types     # Apache 1.2
    /usr/local/etc/mime.types                # FreeBSD
    /usr/share/misc/mime.types               # OpenBSD

##
Registering custom MIME types

Applications can register their own MIME types:

    require "mime"

    MIME.from_extension?(".cr")     # => nil
    MIME.extensions("text/crystal") # => Set(String).new

    MIME.register(".cr", "text/crystal")
    MIME.from_extension?(".cr")     # => "text/crystal"
    MIME.extensions("text/crystal") # => Set(String){".cr"}

##
Loading a custom MIME database

To load a custom MIME database, `.load_mime_database` can be called with an `IO` to read the database from.

    require "mime"

    # Load user-defined MIME types
    File.open("~/.mime.types") do |io|
      MIME.load_mime_database(io)
    end

Loaded values override previously defined mappings.

The data format must follow the format of `mime.types`: Each line declares a MIME type followed by a whitespace-separated list of extensions mapped to this type. Everything following a `#` is considered a comment until the end of line. Empty lines are ignored.

    text/html html htm

    # comment

##
Defined in:

mime.cr
mime/media_type.cr

##
Constant Summary

DEFAULT_TYPES = `{".css" => "text/css; charset=utf-8", ".gif" => "image/gif", ".htm" => "text/html; charset=utf-8", ".html" => "text/html; charset=utf-8", ".jpg" => "image/jpeg", ".jpeg" => "image/jpeg", ".js" => "text/javascript; charset=utf-8", ".json" => "application/json", ".pdf" => "application/pdf", ".png" => "image/png", ".svg" => "image/svg+xml", ".txt" => "text/plain; charset=utf-8", ".xml" => "text/xml; charset=utf-8", ".wasm" => "application/wasm", ".webp" => "image/webp"}`

  A limited set of default MIME types.

##
Class Method Summary

- .extensions(type : String) : Set(String)

  Returns all extensions registered for type.

- .from_extension(extension : String, default) : String

  Looks up the MIME type associated with extension.

- .from_extension(extension : String) : String

  Looks up the MIME type associated with extension.

- .from_extension(extension : String, &)

  Looks up the MIME type associated with extension.

- .from_extension?(extension : String) : String | Nil

  Looks up the MIME type associated with extension.

- .from_filename(filename : String | Path, default) : String

  Looks up the MIME type associated with the extension in filename.

- .from_filename(filename : String | Path) : String

  Looks up the MIME type associated with the extension in filename.

- .from_filename(filename : String | Path, &)

  Looks up the MIME type associated with the extension in filename.

- .from_filename?(filename : String | Path) : String | Nil

  Looks up the MIME type associated with the extension in filename.

- .init(load_defaults : Bool = true) : Nil

  Initializes the MIME database.

- .init(filename : String) : Nil

  Initializes the MIME database loading contents from a file.

- .load_mime_database(io : IO) : Nil

  Reads MIME type mappings from io and registers the extension-to-type relation (see `.register`).

- .register(extension : String, type : String) : Nil

  Register type for extension.

##
Class Method Detail

def self.extensions(type : String) : Set(String) #

Returns all extensions registered for type.

def self.from_extension(extension : String, default) : String #

Looks up the MIME type associated with extension.

A case-sensitive search is tried first, if this yields no result, it is matched case-insensitive. Returns default if extension is not registered.

def self.from_extension(extension : String) : String #

Looks up the MIME type associated with extension.

A case-sensitive search is tried first, if this yields no result, it is matched case-insensitive. Raises `KeyError` if extension is not registered.

def self.from_extension(extension : String, &) #

Looks up the MIME type associated with extension.

A case-sensitive search is tried first, if this yields no result, it is matched case-insensitive. Runs the given block if extension is not registered.

def self.from_extension?(extension : String) : String | Nil #

Looks up the MIME type associated with extension.

A case-sensitive search is tried first, if this yields no result, it is matched case-insensitive. Returns `nil` if extension is not registered.

def self.from_filename(filename : String | Path, default) : String #

Looks up the MIME type associated with the extension in filename.

A case-sensitive search is tried first, if this yields no result, it is matched case-insensitive. Returns default if extension is not registered.

def self.from_filename(filename : String | Path) : String #

Looks up the MIME type associated with the extension in filename.

A case-sensitive search is tried first, if this yields no result, it is matched case-insensitive. Raises `KeyError` if extension is not registered.

def self.from_filename(filename : String | Path, &) #

Looks up the MIME type associated with the extension in filename.

A case-sensitive search is tried first, if this yields no result, it is matched case-insensitive. Runs the given block if extension is not registered.

def self.from_filename?(filename : String | Path) : String | Nil #

Looks up the MIME type associated with the extension in filename.

A case-sensitive search is tried first, if this yields no result, it is matched case-insensitive. Returns `nil` if extension is not registered.

def self.init(load_defaults : Bool = true) : Nil #

Initializes the MIME database.

The default behaviour is to load the internal defaults as well as the OS-provided MIME database. This can be disabled with load_defaults set to `false`.

This method usually doesn't need to be called explicitly when the default behaviour is expected. It will be called implicitly with `load_defaults: true` when a query method is called and the MIME database has not been initialized before.

Calling this method repeatedly is allowed.

def self.init(filename : String) : Nil #

Initializes the MIME database loading contents from a file.

This will neither load the internal defaults nor the OS-provided MIME database, only the database at filename (using `.load_mime_database`).

Calling this method repeatedly is allowed.

def self.load_mime_database(io : IO) : Nil #

Reads MIME type mappings from io and registers the extension-to-type relation (see `.register`).

The format follows that of `mime.types`: Each line is list of MIME type and zero or more extensions, separated by whitespace.

def self.register(extension : String, type : String) : Nil #

Register type for extension.

extension must start with a dot (`.`) and must not contain any null bytes.