http-media 0.3.0 → 0.4.0
raw patch · 12 files changed
+119/−97 lines, 12 files
Files
- http-media.cabal +9/−7
- src/Network/HTTP/Media.hs +8/−3
- src/Network/HTTP/Media/Accept.hs +1/−11
- src/Network/HTTP/Media/Language.hs +11/−2
- src/Network/HTTP/Media/Language/Internal.hs +7/−18
- src/Network/HTTP/Media/MediaType.hs +0/−1
- src/Network/HTTP/Media/MediaType/Internal.hs +14/−20
- src/Network/HTTP/Media/Quality.hs +21/−7
- src/Network/HTTP/Media/RenderHeader.hs +29/−0
- src/Network/HTTP/Media/Utils.hs +4/−2
- test/Network/HTTP/Media/Language/Tests.hs +2/−2
- test/Network/HTTP/Media/Tests.hs +13/−24
http-media.cabal view
@@ -1,5 +1,5 @@ name: http-media-version: 0.3.0+version: 0.4.0 license: MIT license-file: LICENSE author: Timothy Jones@@ -51,11 +51,12 @@ exposed-modules: Network.HTTP.Media Network.HTTP.Media.Accept- Network.HTTP.Media.MediaType Network.HTTP.Media.Language+ Network.HTTP.Media.MediaType+ Network.HTTP.Media.RenderHeader other-modules:- Network.HTTP.Media.MediaType.Internal Network.HTTP.Media.Language.Internal+ Network.HTTP.Media.MediaType.Internal Network.HTTP.Media.Quality Network.HTTP.Media.Utils build-depends:@@ -78,15 +79,16 @@ Network.HTTP.Media.Accept Network.HTTP.Media.Accept.Tests Network.HTTP.Media.Gen- Network.HTTP.Media.MediaType- Network.HTTP.Media.MediaType.Gen- Network.HTTP.Media.MediaType.Internal- Network.HTTP.Media.MediaType.Tests Network.HTTP.Media.Language Network.HTTP.Media.Language.Gen Network.HTTP.Media.Language.Internal Network.HTTP.Media.Language.Tests+ Network.HTTP.Media.MediaType+ Network.HTTP.Media.MediaType.Gen+ Network.HTTP.Media.MediaType.Internal+ Network.HTTP.Media.MediaType.Tests Network.HTTP.Media.Quality+ Network.HTTP.Media.RenderHeader Network.HTTP.Media.Tests Network.HTTP.Media.Utils
src/Network/HTTP/Media.hs view
@@ -14,6 +14,7 @@ -- * Languages , Language+ , toParts -- * Accept matching , matchAccept@@ -36,6 +37,9 @@ -- * Accept , Accept (..)++ -- * Rendering+ , RenderHeader (..) ) where ------------------------------------------------------------------------------@@ -48,9 +52,10 @@ import Data.ByteString.UTF8 (toString) -------------------------------------------------------------------------------import Network.HTTP.Media.Accept as Accept-import Network.HTTP.Media.Language as Language-import Network.HTTP.Media.MediaType as MediaType+import Network.HTTP.Media.Accept as Accept+import Network.HTTP.Media.RenderHeader+import Network.HTTP.Media.Language as Language+import Network.HTTP.Media.MediaType as MediaType import Network.HTTP.Media.Quality import Network.HTTP.Media.Utils
src/Network/HTTP/Media/Accept.hs view
@@ -7,8 +7,7 @@ ) where -------------------------------------------------------------------------------import Data.ByteString-import Data.ByteString.UTF8 (toString)+import Data.ByteString (ByteString) ------------------------------------------------------------------------------@@ -23,14 +22,6 @@ -- handled. parseAccept :: ByteString -> Maybe a - -- | Specifies how to show an Accept-* header. Defaults to the standard- -- show method.- --- -- Mostly useful just for avoiding quotes when rendering 'ByteString's- -- with accompanying quality.- showAccept :: a -> String- showAccept = show- -- | Evaluates whether either the left argument matches the right one. -- -- This relation must be a total order, where more specific terms on the@@ -54,7 +45,6 @@ instance Accept ByteString where parseAccept = Just- showAccept = toString matches = (==) moreSpecificThan _ _ = False
src/Network/HTTP/Media/Language.hs view
@@ -3,10 +3,19 @@ -- language negotiation. module Network.HTTP.Media.Language ( Language- , toList- , toByteString+ , toParts ) where ------------------------------------------------------------------------------+import Data.ByteString (ByteString)++------------------------------------------------------------------------------ import Network.HTTP.Media.Language.Internal+++------------------------------------------------------------------------------+-- | Converts 'Language' to a list of its language parts. The wildcard+-- produces an empty list.+toParts :: Language -> [ByteString]+toParts (Language l) = l
src/Network/HTTP/Media/Language/Internal.hs view
@@ -3,8 +3,6 @@ -- language negotiation. module Network.HTTP.Media.Language.Internal ( Language (..)- , toList- , toByteString ) where ------------------------------------------------------------------------------@@ -20,8 +18,9 @@ import Data.String (IsString (..)) -------------------------------------------------------------------------------import Network.HTTP.Media.Accept (Accept (..))-import Network.HTTP.Media.Utils (hyphen, isAlpha)+import Network.HTTP.Media.Accept (Accept (..))+import Network.HTTP.Media.RenderHeader (RenderHeader (..))+import Network.HTTP.Media.Utils (hyphen, isAlpha) ------------------------------------------------------------------------------@@ -37,7 +36,7 @@ -- Note that internally, Language [] equates to *. instance Show Language where- show = BS.toString . toByteString+ show = BS.toString . renderHeader instance IsString Language where fromString "*" = Language []@@ -64,17 +63,7 @@ moreSpecificThan (Language a) (Language b) = b `isPrefixOf` a && length a > length b ----------------------------------------------------------------------------------- | Converts 'Language' to a list of its language parts. The wildcard--- produces an empty list.-toList :: Language -> [ByteString]-toList (Language l) = l------------------------------------------------------------------------------------ | Converts 'Language' to 'ByteString'.-toByteString :: Language -> ByteString-toByteString (Language []) = "*"-toByteString (Language l) = BS.intercalate "-" l+instance RenderHeader Language where+ renderHeader (Language []) = "*"+ renderHeader (Language l) = BS.intercalate "-" l
src/Network/HTTP/Media/MediaType.hs view
@@ -8,7 +8,6 @@ , Parameters , (//) , (/:)- , toByteString -- * Querying , mainType
src/Network/HTTP/Media/MediaType/Internal.hs view
@@ -3,7 +3,6 @@ module Network.HTTP.Media.MediaType.Internal ( MediaType (..) , Parameters- , toByteString ) where ------------------------------------------------------------------------------@@ -12,15 +11,16 @@ import qualified Data.Map as Map -------------------------------------------------------------------------------import Control.Monad (guard)-import Data.ByteString (ByteString)-import Data.String (IsString (..))-import Data.Map (Map)-import Data.Maybe (fromMaybe)-import Data.Monoid ((<>))+import Control.Monad (guard)+import Data.ByteString (ByteString)+import Data.String (IsString (..))+import Data.Map (Map)+import Data.Maybe (fromMaybe)+import Data.Monoid ((<>)) -------------------------------------------------------------------------------import Network.HTTP.Media.Accept (Accept (..))+import Network.HTTP.Media.Accept (Accept (..))+import Network.HTTP.Media.RenderHeader (RenderHeader (..)) import Network.HTTP.Media.Utils @@ -33,10 +33,7 @@ } deriving (Eq) instance Show MediaType where- show (MediaType a b p) =- Map.foldrWithKey f (BS.toString a ++ '/' : BS.toString b) p- where- f k v = (++ ';' : BS.toString k ++ '=' : BS.toString v)+ show = BS.toString . renderHeader instance IsString MediaType where fromString str = flip fromMaybe (parseAccept $ BS.fromString str) $@@ -70,16 +67,13 @@ subB = subType b == "*" params = not (Map.null $ parameters a) && Map.null (parameters b) +instance RenderHeader MediaType where+ renderHeader (MediaType a b p) = Map.foldrWithKey f (a <> "/" <> b) p+ where+ f k v = (<> ";" <> k <> "=" <> v) + ------------------------------------------------------------------------------ -- | 'MediaType' parameters. type Parameters = Map ByteString ByteString------------------------------------------------------------------------------------ | Converts 'MediaType' to 'ByteString'.-toByteString :: MediaType -> ByteString-toByteString (MediaType a b p) = Map.foldrWithKey f (a <> "/" <> b) p- where- f k v = (<> ";" <> k <> "=" <> v)
src/Network/HTTP/Media/Quality.hs view
@@ -8,23 +8,34 @@ ) where -------------------------------------------------------------------------------import Data.Maybe (listToMaybe)-import Data.Word (Word16)+import qualified Data.ByteString as BS+import qualified Data.ByteString.UTF8 as BS -------------------------------------------------------------------------------import Network.HTTP.Media.Accept+import Data.ByteString (ByteString)+import Data.List (dropWhileEnd)+import Data.Maybe (listToMaybe)+import Data.Monoid ((<>))+import Data.Word (Word16) ------------------------------------------------------------------------------+import Network.HTTP.Media.RenderHeader (RenderHeader (..))+import Network.HTTP.Media.Utils (zero)++------------------------------------------------------------------------------ -- | Attaches a quality value to data. data Quality a = Quality { qualityData :: a , qualityValue :: Word16 } deriving (Eq) -instance Accept a => Show (Quality a) where- show (Quality a q) = showAccept a ++ ";q=" ++ showQ q+instance RenderHeader a => Show (Quality a) where+ show = BS.toString . renderHeader +instance RenderHeader h => RenderHeader (Quality h) where+ renderHeader (Quality a q) = renderHeader a <> ";q=" <> showQ q + ------------------------------------------------------------------------------ -- | Attaches the quality value '1'. maxQuality :: a -> Quality a@@ -39,10 +50,13 @@ ------------------------------------------------------------------------------ -- | Converts the integral value into its standard quality representation.-showQ :: Word16 -> String+showQ :: Word16 -> ByteString showQ 1000 = "1" showQ 0 = "0"-showQ q = '0' : '.' : let s = show q in replicate (3 - length s) '0' ++ s+showQ q = "0." <> BS.replicate (3 - length s) zero <> b+ where+ s = show q+ b = BS.fromString (dropWhileEnd (== '0') s) ------------------------------------------------------------------------------
+ src/Network/HTTP/Media/RenderHeader.hs view
@@ -0,0 +1,29 @@+------------------------------------------------------------------------------+-- | Defines the 'RenderHeader' type class, with the 'renderHeader' method.+-- 'renderHeader' can be used to render basic header values (acting as+-- identity on 'ByteString's), but it will also work on lists of quality+-- values, which provides the necessary interface for rendering the full+-- possibilities of Accept headers.+module Network.HTTP.Media.RenderHeader+ ( RenderHeader (..)+ ) where++------------------------------------------------------------------------------+import Data.ByteString (ByteString, intercalate)+++------------------------------------------------------------------------------+-- | A class for header values, so they may be rendered to their 'ByteString'+-- representation. Lists of header values and quality-marked header values+-- will render appropriately.+class RenderHeader h where++ -- | Render a header value to a UTF-8 'ByteString'.+ renderHeader :: h -> ByteString++instance RenderHeader ByteString where+ renderHeader = id++instance RenderHeader h => RenderHeader [h] where+ renderHeader = intercalate "," . map renderHeader+
src/Network/HTTP/Media/Utils.hs view
@@ -14,6 +14,7 @@ , space , equal , hyphen+ , zero ) where ------------------------------------------------------------------------------@@ -62,6 +63,7 @@ ------------------------------------------------------------------------------ -- | 'ByteString' compatible characters.-slash, semi, comma, space, equal, hyphen :: Word8-[slash, semi, comma, space, equal, hyphen] = [47, 59, 44, 32, 61, 45]+slash, semi, comma, space, equal, hyphen, zero :: Word8+[slash, semi, comma, space, equal, hyphen, zero] =+ [47, 59, 44, 32, 61, 45, 48]
test/Network/HTTP/Media/Language/Tests.hs view
@@ -12,7 +12,7 @@ ------------------------------------------------------------------------------ import Network.HTTP.Media.Accept-import Network.HTTP.Media.Language+import Network.HTTP.Media.RenderHeader import Network.HTTP.Media.Language.Gen @@ -111,5 +111,5 @@ testParseAccept :: Test testParseAccept = testProperty "parseAccept" $ do lang <- genLanguage- return $ parseAccept (toByteString lang) == Just lang+ return $ parseAccept (renderHeader lang) == Just lang
test/Network/HTTP/Media/Tests.hs view
@@ -4,8 +4,6 @@ ------------------------------------------------------------------------------ import Control.Monad (replicateM) import Data.ByteString (ByteString)-import Data.ByteString.UTF8 (fromString)-import Data.List (intercalate) import Data.Map (empty) import Data.Maybe (isNothing, listToMaybe) import Data.Word (Word16)@@ -38,24 +36,23 @@ [ testProperty "Without quality" $ do media <- medias return $- parseQuality (group media) == Just (map maxQuality media)+ parseQuality (renderHeader media) == Just (map maxQuality media) , testProperty "With quality" $ do media <- medias >>= mapM (flip fmap (choose (0, 1000)) . Quality)- return $ parseQuality (group media) == Just media+ return $ parseQuality (renderHeader media) == Just media ] where medias = listOf1 genMediaType- group media = fromString $ intercalate "," (map show media) ------------------------------------------------------------------------------ testMatchAccept :: Test-testMatchAccept = testMatch "Accept" matchAccept qToBS+testMatchAccept = testMatch "Accept" matchAccept renderHeader ------------------------------------------------------------------------------ testMapAccept :: Test-testMapAccept = testMap "Accept" mapAccept qToBS+testMapAccept = testMap "Accept" mapAccept renderHeader ------------------------------------------------------------------------------@@ -63,7 +60,7 @@ testMatchContent = testGroup "matchContent" [ testProperty "Most specific" $ do media <- genConcreteMediaType- let client = toBS+ let client = renderHeader [ MediaType "*" "*" empty , media { subType = "*" } , media { parameters = empty }@@ -73,17 +70,19 @@ , testProperty "Nothing" $ do (server, client) <- genServerAndClient let client' = filter (not . flip any server . matches) client- return . isNothing $ matchAccept server (toBS client')+ return . isNothing $ matchAccept server (renderHeader client') , testProperty "Left biased" $ do server <- genServer- return $ matchAccept server (toBS server) == Just (head server)+ return $+ matchAccept server (renderHeader server) == Just (head server) , testProperty "Against */*" $ do server <- genServer let stars = "*/*" :: ByteString- return $ matchAccept server (toBS [stars]) == Just (head server)+ return $+ matchAccept server (renderHeader [stars]) == Just (head server) , testProperty "Against type/*" $ do server <- genServer- let client = toBS [subStarOf $ head server]+ let client = renderHeader [subStarOf $ head server] return $ matchAccept server client == Just (head server) ] @@ -94,12 +93,12 @@ [ testProperty "Matches" $ do server <- genServer let zipped = zip server server- return $ mapAccept zipped (toBS server) == listToMaybe server+ return $ mapAccept zipped (renderHeader server) == listToMaybe server , testProperty "Nothing" $ do server <- genServer client <- listOf1 $ genDiffMediaTypesWith genConcreteMediaType server let zipped = zip server $ repeat ()- return . isNothing $ mapAccept zipped (toBS client)+ return . isNothing $ mapAccept zipped (renderHeader client) ] @@ -195,14 +194,4 @@ server <- genServer client <- listOf1 $ genDiffMediaTypesWith genConcreteMediaType server return (server, client)----------------------------------------------------------------------------------toBS :: Accept a => [a] -> ByteString-toBS = fromString . intercalate "," . map showAccept----------------------------------------------------------------------------------qToBS :: Accept a => [Quality a] -> ByteString-qToBS = fromString . intercalate "," . map show