packages feed

hackage-server-0.5.0: Distribution/Server/Util/ContentType.hs

module Distribution.Server.Util.ContentType (
    parseContentAccept
  ) where

import Happstack.Server.Types (ContentType(..))

import qualified Text.ParserCombinators.ReadP as Parse
import Control.Monad
import Data.List (find, sortBy)
import Data.Char (isAlphaNum, isDigit)
import Data.Ord (comparing)

-- data VaryFormat = Json | Xml | Html | Plain | Other

-- do some special processing here to fix Webkit's effing issues (and IE's, less so)
-- hackageVaryingAccept :: String -> [VaryFormat]

-- this just returns a list of content-types sorted by quality preference
parseContentAccept :: String -> [ContentType]
parseContentAccept = process . maybe [] fst . find (null . snd) . Parse.readP_to_S parser
  where
    process :: [(a, Int)] -> [a]
    process = map fst . sortBy (flip (comparing snd)) . filter ((/=0) . snd)
    parser :: Parse.ReadP [(ContentType, Int)]
    parser = flip Parse.sepBy1 (Parse.char ',') $ do
        Parse.skipSpaces
        -- a more 'accurate' type than (String, String)
        -- might be Maybe (String, Maybe String)
        typ <- parseMediaType
        void $ Parse.char '/'
        subTyp <- parseMediaType
        quality <- Parse.option 1000 $ do
            Parse.skipSpaces >> Parse.string ";q=" >> Parse.skipSpaces
            parseQuality
        -- TODO: parse other parameters
        return (ContentType {ctType = typ, ctSubtype = subTyp, ctParameters = []}, quality)
    parseMediaType = (Parse.char '*' >> return []) Parse.<++ Parse.munch1 (\c -> case c of '-' -> True; '.' -> True; '+' -> True; _ -> isAlphaNum c)
    -- other characters technically allowed but never found in the wild: !#$%&^_`|~
    parseQuality :: Parse.ReadP Int -- returns a quality in fixed point (0.75 -> 750)
    parseQuality = (Parse.char '1' >> Parse.optional (Parse.char '.' >> Parse.many (Parse.char '0')) >> return 1000) Parse.<++
                   (Parse.char '0' >> zeroOption (Parse.char '.' >> zeroOption munch3Digits))
    zeroOption :: Parse.ReadP Int -> Parse.ReadP Int
    zeroOption p = p Parse.<++ return 0
    munch3Digits :: Parse.ReadP Int
    munch3Digits = fmap (\s ->  read $ take 3 (s++"00") :: Int) (Parse.munch1 isDigit)

--application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5