-- |MIME Type guesser which guesses by a file extension. This is a -- poor man's way of guessing MIME Types. It is simple and fast. -- -- In general you don't have to use this module directly. module Network.HTTP.Lucu.MIMEType.Guess ( ExtMap , guessTypeByFileName , parseExtMapFile , serializeExtMap ) where import qualified Data.ByteString.Lazy.Char8 as B import Data.ByteString.Lazy.Char8 (ByteString) import qualified Data.Map as M import Data.Map (Map) import Data.Maybe import Language.Haskell.Pretty import Language.Haskell.Syntax import Network.HTTP.Lucu.MIMEType import Network.HTTP.Lucu.Parser import Network.HTTP.Lucu.Parser.Http import Network.HTTP.Lucu.Utils import System.IO -- |Map from extension to MIME Type. type ExtMap = Map String MIMEType -- |Guess the MIME Type of file. guessTypeByFileName :: ExtMap -> FilePath -> Maybe MIMEType guessTypeByFileName extMap fpath = extMap `seq` fpath `seq` let ext = last $ splitBy (== '.') fpath in M.lookup ext extMap >>= return -- |Read an Apache mime.types and parse it. parseExtMapFile :: FilePath -> IO ExtMap parseExtMapFile fpath = fpath `seq` do file <- B.readFile fpath case parse (allowEOF extMapP) file of (Success xs, _) -> return $ compile xs (_, input') -> let near = B.unpack $ B.take 100 input' in fail ("Failed to parse: " ++ fpath ++ " (near: " ++ near ++ ")") extMapP :: Parser [ (MIMEType, [String]) ] extMapP = do xs <- many (comment <|> validLine <|> emptyLine) eof return $ catMaybes xs where spc = oneOf " \t" comment = do many spc char '#' many $ satisfy (/= '\n') return Nothing validLine = do many spc mime <- mimeTypeP many spc exts <- sepBy token (many spc) return $ Just (mime, exts) emptyLine = oneOf " \t\n" >> return Nothing compile :: [ (MIMEType, [String]) ] -> Map String MIMEType compile = M.fromList . foldr (++) [] . map tr where tr :: (MIMEType, [String]) -> [ (String, MIMEType) ] tr (mime, exts) = [ (ext, mime) | ext <- exts ] -- |@'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@. -- -- The module "Network.HTTP.Lucu.MIMEType.DefaultExtensionMap" is -- surely generated using this function. serializeExtMap :: ExtMap -> String -> String -> String serializeExtMap extMap moduleName variableName = let hsModule = HsModule undefined modName (Just exports) imports decls modName = Module moduleName exports = [HsEVar (UnQual (HsIdent variableName))] imports = [ HsImportDecl undefined (Module "Network.HTTP.Lucu.MIMEType") False Nothing Nothing , HsImportDecl undefined (Module "Network.HTTP.Lucu.MIMEType.Guess") False Nothing Nothing , HsImportDecl undefined (Module "Data.Map") True (Just (Module "M")) Nothing , HsImportDecl undefined (Module "Data.Map") False Nothing (Just (False, [HsIAbs (HsIdent "Map")])) ] decls = [ HsTypeSig undefined [HsIdent variableName] (HsQualType [] (HsTyCon (UnQual (HsIdent "ExtMap")))) , HsFunBind [HsMatch undefined (HsIdent variableName) [] (HsUnGuardedRhs extMapExp) []] ] extMapExp = HsApp (HsVar (Qual (Module "M") (HsIdent "fromList"))) (HsList records) comment = "{- !!! WARNING !!!\n" ++ " This file is automatically generated.\n" ++ " DO NOT EDIT BY HAND OR YOU WILL REGRET -}\n\n" in comment ++ prettyPrint hsModule ++ "\n" where records :: [HsExp] records = map record $ M.assocs extMap record :: (String, MIMEType) -> HsExp record (ext, mime) = HsTuple [HsLit (HsString ext), mimeToExp mime] mimeToExp :: MIMEType -> HsExp mimeToExp (MIMEType maj min params) = foldl appendParam (HsInfixApp (HsLit (HsString maj)) (HsQVarOp (UnQual (HsSymbol ""))) (HsLit (HsString min))) params appendParam :: HsExp -> (String, String) -> HsExp appendParam x param = HsInfixApp x (HsQVarOp (UnQual (HsSymbol "<:>"))) $ paramToExp param paramToExp :: (String, String) -> HsExp paramToExp (name, value) = HsInfixApp (HsLit (HsString name)) (HsQVarOp (UnQual (HsSymbol "<=>"))) (HsLit (HsString value))