    -- | Defines the 'MediaType' accept header with an 'Accept' instance for use
    -- in content-type negotiation.
    module Network.HTTP.Media.MediaType
      ( -- * Type and creation
        MediaType,
        Parameters,
        (//),
        (/:),

        -- * Querying
        mainType,
        subType,
        parameters,
        (/?),
        (/.),
      )
    where

    import Data.ByteString (ByteString)
    import qualified Data.ByteString.Char8 as BS
    import Data.CaseInsensitive (CI)
    import qualified Data.CaseInsensitive as CI
    import Data.Map (empty, insert)
    import qualified Data.Map as Map
    import Network.HTTP.Media.MediaType.Internal (MediaType (MediaType))
    import Network.HTTP.Media.MediaType.Internal hiding (MediaType (..))
    import qualified Network.HTTP.Media.MediaType.Internal as Internal
    import Network.HTTP.Media.Utils

    -- | Retrieves the main type of a 'MediaType'.
    mainType :: MediaType -> CI ByteString
    mainType :: MediaType -> CI ByteString
    mainType = MediaType -> CI ByteString
    Internal.mainType

    -- | Retrieves the sub type of a 'MediaType'.
    subType :: MediaType -> CI ByteString
    subType :: MediaType -> CI ByteString
    subType = MediaType -> CI ByteString
    Internal.subType

    -- | Retrieves the parameters of a 'MediaType'.
    parameters :: MediaType -> Parameters
    parameters :: MediaType -> Parameters
    parameters = MediaType -> Parameters
    Internal.parameters

    -- | Builds a 'MediaType' without parameters. Can produce an error if
    -- either type is invalid.
    (//) :: ByteString -> ByteString -> MediaType
    ByteString
    a // :: ByteString -> ByteString -> MediaType
    // ByteString
    b
      | ByteString
    a ByteString -> ByteString -> Bool
    forall a. Eq a => a -> a -> Bool
    == ByteString
    "*" Bool -> Bool -> Bool
    && ByteString
    b ByteString -> ByteString -> Bool
    forall a. Eq a => a -> a -> Bool
    == ByteString
    "*" = CI ByteString -> CI ByteString -> Parameters -> MediaType
    MediaType (ByteString -> CI ByteString
    forall s. FoldCase s => s -> CI s
    CI.mk ByteString
    a) (ByteString -> CI ByteString
    forall s. FoldCase s => s -> CI s
    CI.mk ByteString
    b) Parameters
    forall k a. Map k a
    empty
      | ByteString
    b ByteString -> ByteString -> Bool
    forall a. Eq a => a -> a -> Bool
    == ByteString
    "*" = CI ByteString -> CI ByteString -> Parameters -> MediaType
    MediaType (ByteString -> CI ByteString
    ensureR ByteString
    a) (ByteString -> CI ByteString
    forall s. FoldCase s => s -> CI s
    CI.mk ByteString
    b) Parameters
    forall k a. Map k a
    empty
      | Bool
    otherwise = CI ByteString -> CI ByteString -> Parameters -> MediaType
    MediaType (ByteString -> CI ByteString
    ensureR ByteString
    a) (ByteString -> CI ByteString
    ensureR ByteString
    b) Parameters
    forall k a. Map k a
    empty

    -- | Adds a parameter to a 'MediaType'. Can produce an error if either
    -- string is invalid.
    (/:) :: MediaType -> (ByteString, ByteString) -> MediaType
    (MediaType CI ByteString
    a CI ByteString
    b Parameters
    p) /: :: MediaType -> (ByteString, ByteString) -> MediaType
    /: (ByteString
    k, ByteString
    v) = CI ByteString -> CI ByteString -> Parameters -> MediaType
    MediaType CI ByteString
    a CI ByteString
    b (Parameters -> MediaType) -> Parameters -> MediaType
    forall a b. (a -> b) -> a -> b
    $ CI ByteString -> CI ByteString -> Parameters -> Parameters
    forall k a. Ord k => k -> a -> Map k a -> Map k a
    insert (ByteString -> CI ByteString
    ensureR ByteString
    k) (ByteString -> CI ByteString
    ensureV ByteString
    v) Parameters
    p

    -- | Evaluates if a 'MediaType' has a parameter of the given name.
    (/?) :: MediaType -> ByteString -> Bool
    (MediaType CI ByteString
    _ CI ByteString
    _ Parameters
    p) /? :: MediaType -> ByteString -> Bool
    /? ByteString
    k = CI ByteString -> Parameters -> Bool
    forall k a. Ord k => k -> Map k a -> Bool
    Map.member (ByteString -> CI ByteString
    forall s. FoldCase s => s -> CI s
    CI.mk ByteString
    k) Parameters
    p

    -- | Retrieves a parameter from a 'MediaType'.
    (/.) :: MediaType -> ByteString -> Maybe (CI ByteString)
    (MediaType CI ByteString
    _ CI ByteString
    _ Parameters
    p) /. :: MediaType -> ByteString -> Maybe (CI ByteString)
    /. ByteString
    k = CI ByteString -> Parameters -> Maybe (CI ByteString)
    forall k a. Ord k => k -> Map k a -> Maybe a
    Map.lookup (ByteString -> CI ByteString
    forall s. FoldCase s => s -> CI s
    CI.mk ByteString
    k) Parameters
    p

    -- | Ensures that the 'ByteString' matches the ABNF for `reg-name` in RFC
    -- 4288.
    ensureR :: ByteString -> CI ByteString
    ensureR :: ByteString -> CI ByteString
    ensureR ByteString
    bs =
      ByteString -> CI ByteString
    forall s. FoldCase s => s -> CI s
    CI.mk (ByteString -> CI ByteString) -> ByteString -> CI ByteString
    forall a b. (a -> b) -> a -> b
    $
        if Int
    l Int -> Int -> Bool
    forall a. Eq a => a -> a -> Bool
    == Int
    0 Bool -> Bool -> Bool
    || Int
    l Int -> Int -> Bool
    forall a. Ord a => a -> a -> Bool
    > Int
    127
          then [Char] -> ByteString
    forall a. HasCallStack => [Char] -> a
    error ([Char] -> ByteString) -> [Char] -> ByteString
    forall a b. (a -> b) -> a -> b
    $ [Char]
    "Invalid length for " [Char] -> [Char] -> [Char]
    forall a. [a] -> [a] -> [a]
    ++ ByteString -> [Char]
    forall a. Show a => a -> [Char]
    show ByteString
    bs
          else (Char -> Bool) -> ByteString -> ByteString
    ensure Char -> Bool
    isMediaChar ByteString
    bs
      where
        l :: Int
    l = ByteString -> Int
    BS.length ByteString
    bs

    -- | Ensures that the 'ByteString' does not contain invalid characters for
    -- a parameter value. RFC 4288 does not specify what characters are valid, so
    -- here we just disallow parameter and media type breakers, ',' and ';'.
    ensureV :: ByteString -> CI ByteString
    ensureV :: ByteString -> CI ByteString
    ensureV = ByteString -> CI ByteString
    forall s. FoldCase s => s -> CI s
    CI.mk (ByteString -> CI ByteString)
    -> (ByteString -> ByteString) -> ByteString -> CI ByteString
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . (Char -> Bool) -> ByteString -> ByteString
    ensure (Char -> [Char] -> Bool
    forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
    `notElem` [Char
    ',', Char
    ';'])

    -- | Ensures the predicate matches for every character in the given string.
    ensure :: (Char -> Bool) -> ByteString -> ByteString
    ensure :: (Char -> Bool) -> ByteString -> ByteString
    ensure Char -> Bool
    f ByteString
    bs =
      ByteString -> (Char -> ByteString) -> Maybe Char -> ByteString
    forall b a. b -> (a -> b) -> Maybe a -> b
    maybe
        ([Char] -> ByteString
    forall a. HasCallStack => [Char] -> a
    error ([Char] -> ByteString) -> [Char] -> ByteString
    forall a b. (a -> b) -> a -> b
    $ [Char]
    "Invalid character in " [Char] -> [Char] -> [Char]
    forall a. [a] -> [a] -> [a]
    ++ ByteString -> [Char]
    forall a. Show a => a -> [Char]
    show ByteString
    bs)
        (ByteString -> Char -> ByteString
    forall a b. a -> b -> a
    const ByteString
    bs)
        ((Char -> Bool) -> ByteString -> Maybe Char
    BS.find Char -> Bool
    f ByteString
    bs)