--- #prune
-
--- |Manipulation of MIME Types.
+{-# LANGUAGE
+ DeriveDataTypeable
+ , OverloadedStrings
+ , RecordWildCards
+ , TemplateHaskell
+ , UnicodeSyntax
+ #-}
+-- |Parsing and printing MIME Media Types
+-- (<http://tools.ietf.org/html/rfc2046>).
module Network.HTTP.Lucu.MIMEType
( MIMEType(..)
- , (</>)
- , (<:>)
- , (<=>)
- , mimeTypeP
- , mimeTypeListP
- )
- where
-
-import Network.HTTP.Lucu.Parser
-import Network.HTTP.Lucu.Parser.Http
-import Network.HTTP.Lucu.Utils
-
--- |@'MIMEType' \"major\" \"minor\" [(\"name\", \"value\")]@
--- represents \"major\/minor; name=value\".
-data MIMEType = MIMEType {
- mtMajor :: String
- , mtMinor :: String
- , mtParams :: [ (String, String) ]
- } deriving (Eq)
-
-instance Show MIMEType where
- show (MIMEType maj min params)
- = maj ++ "/" ++ min ++
- if null params then
- ""
- else
- "; " ++ joinWith "; " (map showPair params)
- where
- showPair :: (String, String) -> String
- showPair (name, value)
- = name ++ "=" ++ if any (not . isToken) value then
- quoteStr value
- else
- value
+ , parseMIMEType
+ , printMIMEType
+ , mimeType
+ , mimeTypeList
+ )
+ where
+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
-infix 9 </>, <=>
-infixl 8 <:>
-
--- |@\"major\" \<\/\> \"minor\"@ constructs a MIME Type
--- \"major\/minor\".
-(</>) :: String -> String -> MIMEType
-maj </> min
- = MIMEType maj min []
-
--- |\<\:\> appends a @(name, value)@ pair to a MIME Type.
-(<:>) :: MIMEType -> (String, String) -> MIMEType
-mt@(MIMEType _ _ params) <:> pair
- = mt {
- mtParams = mtParams mt ++ [pair]
+-- |A media type, subtype, and parameters.
+data MIMEType
+ = MIMEType {
+ mtMedia ∷ !CIAscii
+ , mtSub ∷ !CIAscii
+ , mtParams ∷ !MIMEParams
}
+ deriving (Eq, Show, Read, Typeable)
--- |\<\=\> takes two strings and makes a tuple of them. So you can say
--- @\"text\" \<\/\> \"xml\" \<\:\> \"charset\" \<\=\> \"UTF-8\" \<\:\>
--- \"q\" \<\=\> \"0.9\"@ to represent \"text\/xml; charset=UTF-8;
--- q=0.9\".
-(<=>) :: String -> String -> (String, String)
-name <=> value = (name, value)
+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
-mimeTypeP :: Parser MIMEType
-mimeTypeP = allowEOF $
- do maj <- token
- char '/'
- min <- token
- params <- many paramP
- return $ MIMEType maj min params
- where
- paramP :: Parser (String, String)
- paramP = do many lws
- char ';'
- many lws
- name <- token
- char '='
- value <- token <|> quotedStr
- return (name, value)
+-- |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
-mimeTypeListP :: Parser [MIMEType]
-mimeTypeListP = allowEOF $ listOf mimeTypeP
+-- |'Parser' for a list of 'MIMEType's.
+mimeTypeList ∷ Parser [MIMEType]
+{-# INLINE mimeTypeList #-}
+mimeTypeList = listOf mimeType