]> gitweb @ CieloNegro.org - Lucu.git/blobdiff - Network/HTTP/Lucu/MIMEType/Guess.hs
Reimplement MultipartForm
[Lucu.git] / Network / HTTP / Lucu / MIMEType / Guess.hs
index 2664d79b859e853565090f5581e103e7f9fce17f..d8bca8e785658efa5390862f8baa97c071a93f58 100644 (file)
@@ -1,8 +1,8 @@
 {-# LANGUAGE
     UnicodeSyntax
   #-}
--- |MIME Type guessing by a file extension. This is a poor man's way
--- of guessing MIME Types. It is simple and fast.
+-- |Guessing MIME Types by file extensions. It's not always accurate
+-- but simple and fast.
 --
 -- In general you don't have to use this module directly.
 module Network.HTTP.Lucu.MIMEType.Guess
@@ -14,6 +14,7 @@ module Network.HTTP.Lucu.MIMEType.Guess
     )
     where
 import Control.Applicative
+import Control.Monad
 import qualified Data.Ascii as A
 import Data.Attoparsec.Char8 as P
 import qualified Data.Attoparsec.Lazy as LP
@@ -21,6 +22,7 @@ import qualified Data.ByteString.Lazy.Char8 as B
 import qualified Data.Map as M
 import Data.Map (Map)
 import Data.Maybe
+import Data.Monoid.Unicode
 import Data.Text (Text)
 import qualified Data.Text as T
 import Data.Text.Encoding
@@ -32,26 +34,34 @@ import Network.HTTP.Lucu.MIMEType
 import Prelude.Unicode
 import System.FilePath
 
--- |'Map' from extension to 'MIMEType'.
+-- |A 'Map' from file extensions to 'MIMEType's.
 type ExtMap = Map Text MIMEType
 
--- |Guess the MIME Type of file.
+-- |Guess the MIME Type of file.
 guessTypeByFileName ∷ ExtMap → FilePath → Maybe MIMEType
 guessTypeByFileName extMap fpath
-    = let ext = T.pack $ takeExtension fpath
-      in
-        M.lookup ext extMap
+    = case takeExtension fpath of
+        []      → Nothing
+        (_:ext) → M.lookup (T.pack ext) extMap
 
 -- |Read an Apache mime.types and parse it.
 parseExtMapFile ∷ FilePath → IO ExtMap
 parseExtMapFile fpath
     = do file ← B.readFile fpath
          case LP.parse extMapP file of
-           LP.Done _ xs  → return $ compile xs
-           LP.Fail _ _ e → fail ("Failed to parse: " ⧺ fpath ⧺ ": " ⧺ e)
+           LP.Done _ xs
+               → case compile xs of
+                    Right m → return m
+                    Left  e → fail (concat [ "Duplicate extension \""
+                                           , show e
+                                           , "\" in: "
+                                           , fpath
+                                           ])
+           LP.Fail _ _ e
+               → fail ("Failed to parse: " ⧺ fpath ⧺ ": " ⧺ e)
 
 extMapP ∷ Parser [ (MIMEType, [Text]) ]
-extMapP = do xs ← P.many (comment <|> validLine <|> emptyLine)
+extMapP = do xs ← P.many (try comment <|> try validLine <|> emptyLine)
              endOfInput
              return $ catMaybes xs
     where
@@ -59,16 +69,14 @@ extMapP = do xs ← P.many (comment <|> validLine <|> emptyLine)
       isSpc c = c ≡ '\x20' ∨ c ≡ '\x09'
 
       comment ∷ Parser (Maybe (MIMEType, [Text]))
-      comment = try $
-                do skipWhile isSpc
-                   _ ← char '#'
+      comment = do skipWhile isSpc
+                   void $ char '#'
                    skipWhile (≢ '\x0A')
                    return Nothing
 
       validLine ∷ Parser (Maybe (MIMEType, [Text]))
-      validLine = try $
-                  do skipWhile isSpc
-                     mime ← mimeTypeP
+      validLine = do skipWhile isSpc
+                     mime ← mimeType
                      skipWhile isSpc
                      exts ← sepBy extP (skipWhile isSpc)
                      return $ Just (mime, exts)
@@ -77,24 +85,33 @@ extMapP = do xs ← P.many (comment <|> validLine <|> emptyLine)
       extP = decodeUtf8 <$> takeWhile1 (\c → (¬) (isSpc c ∨ c ≡ '\x0A'))
 
       emptyLine ∷ Parser (Maybe (MIMEType, [Text]))
-      emptyLine = try $
-                  do skipWhile isSpc
-                     _ ← char '\x0A'
+      emptyLine = do skipWhile isSpc
+                     void $ char '\x0A'
                      return Nothing
 
-compile ∷ [ (MIMEType, [Text]) ] → Map Text MIMEType
-compile = M.fromList ∘ concat ∘ map tr
+compile ∷ Ord k ⇒ [(v, [k])] → Either (k, v, v) (Map k v)
+compile = go (∅) ∘ concat ∘ map tr
     where
-      tr ∷ (MIMEType, [Text]) → [ (Text, MIMEType) ]
-      tr (mime, exts) = [ (ext, mime) | ext ← exts ]
+      tr ∷ (v, [k]) → [(k, v)]
+      tr (v, ks) = [(k, v) | k ← ks]
+
+      go ∷ Ord k ⇒ Map k v → [(k, v)] → Either (k, v, v) (Map k v)
+      go m []         = Right m
+      go m ((k, v):xs)
+          = case M.insertLookupWithKey' f k v m of
+              (Nothing, m') → go m' xs
+              (Just v0, _ ) → Left (k, v0, v)
+
+      f ∷ k → v → v → v
+      f _ _ = id
 
 -- |@'serializeExtMap' extMap moduleName variableName@ generates a
 -- Haskell source code which contains the following things:
 --
 -- * A definition of module named @moduleName@.
 --
--- * @variableName :: 'ExtMap'@ whose content is a serialization of
---   @extMap@.
+-- * @variableName :: 'ExtMap'@ whose content is the serialised
+-- @extMap@.
 --
 -- The module "Network.HTTP.Lucu.MIMEType.DefaultExtensionMap" is
 -- surely generated using this function.
@@ -116,6 +133,7 @@ serializeExtMap extMap moduleName variableName
           decls     = [ TypeSig (⊥) [name variableName]
                                     (TyCon (UnQual (name "ExtMap")))
                       , nameBind (⊥) (name variableName) extMapExp
+                      , InlineSig (⊥) False AlwaysActive (UnQual (name variableName))
                       ]
           comment   = concat [ "{- !!! WARNING !!!\n"
                              , "   This file is automatically generated.\n"
@@ -131,7 +149,7 @@ serializeExtMap extMap moduleName variableName
       record ∷ (Text, MIMEType) → Exp
       record (ext, mime)
           = tuple [ strE (T.unpack ext)
-                  , metaFunction "parseMIMEType" [strE $ mimeToString mime]
+                  , function "parseMIMEType" `app` strE (mimeToString mime)
                   ]
 
       mimeToString ∷ MIMEType → String