+import Control.Applicative
+import Data.Ascii (Ascii, AsciiBuilder, CIAscii)
+import qualified Data.Ascii as A
+import Data.Attoparsec.Char8 as P
+import Data.Monoid.Unicode
+import Data.Typeable
+import Language.Haskell.TH.Syntax
+import Network.HTTP.Lucu.MIMEParams
+import Network.HTTP.Lucu.OrphanInstances ()
+import Network.HTTP.Lucu.Parser
+import Network.HTTP.Lucu.Parser.Http
+import Prelude.Unicode
+
+-- |A media type, subtype, and parameters.
+data MIMEType
+ = MIMEType {
+ mtMedia ∷ !CIAscii
+ , mtSub ∷ !CIAscii
+ , mtParams ∷ !MIMEParams
+ }
+ deriving (Eq, Show, Read, Typeable)
+
+instance Lift MIMEType where
+ lift (MIMEType {..})
+ = [| MIMEType {
+ mtMedia = $(lift mtMedia )
+ , mtSub = $(lift mtSub )
+ , mtParams = $(lift mtParams)
+ }
+ |]
+
+-- |Convert a 'MIMEType' to an 'AsciiBuilder'.
+printMIMEType ∷ MIMEType → AsciiBuilder
+{-# INLINEABLE printMIMEType #-}
+printMIMEType (MIMEType {..})
+ = A.toAsciiBuilder (A.fromCIAscii mtMedia) ⊕
+ A.toAsciiBuilder "/" ⊕
+ A.toAsciiBuilder (A.fromCIAscii mtSub) ⊕
+ printMIMEParams mtParams
+
+-- |Parse 'MIMEType' from an 'Ascii'. This function throws an
+-- exception for parse error. For literals consider using
+-- 'Network.HTTP.Lucu.MIMEType.TH.mimeType'.
+parseMIMEType ∷ Ascii → MIMEType
+{-# INLINEABLE parseMIMEType #-}
+parseMIMEType str
+ = case parseOnly (finishOff mimeType) $ A.toByteString str of
+ Right t → t
+ Left err → error ("Unparsable MIME Type: " ⧺ A.toString str ⧺ ": " ⧺ err)
+
+-- |'Parser' for an 'MIMEType'.
+mimeType ∷ Parser MIMEType
+{-# INLINEABLE mimeType #-}
+mimeType = do media ← A.toCIAscii <$> token
+ _ ← char '/'
+ sub ← A.toCIAscii <$> token
+ params ← mimeParams
+ return $ MIMEType media sub params
+
+-- |'Parser' for a list of 'MIMEType's.
+mimeTypeList ∷ Parser [MIMEType]
+{-# INLINE mimeTypeList #-}
+mimeTypeList = listOf mimeType