diff --git a/servant.cabal b/servant.cabal index f52ca766..a967e10a 100644 --- a/servant.cabal +++ b/servant.cabal @@ -94,3 +94,4 @@ test-suite spec , servant , string-conversions , text + , url diff --git a/src/Servant/API/ContentTypes.hs b/src/Servant/API/ContentTypes.hs index 2c71503d..f2ae6b15 100644 --- a/src/Servant/API/ContentTypes.hs +++ b/src/Servant/API/ContentTypes.hs @@ -261,6 +261,7 @@ encodeFormUrlEncoded xs = let escape :: TextS.Text -> ByteString escape = cs . escapeURIString isUnreserved . cs encodePair :: (TextS.Text, TextS.Text) -> ByteString + encodePair (k, "") = escape k encodePair (k, v) = escape k <> "=" <> escape v in B.intercalate "&" $ map encodePair xs @@ -275,6 +276,7 @@ decodeFormUrlEncoded q = do [k,v] -> return ( unescape k , unescape v ) + [k] -> return ( unescape k, "" ) _ -> Left $ "not a valid pair: " <> cs p unescape :: TextS.Text -> TextS.Text unescape = cs . unEscapeString . cs . TextS.intercalate "%20" . TextS.splitOn "+" diff --git a/test/Servant/API/ContentTypesSpec.hs b/test/Servant/API/ContentTypesSpec.hs index 6e09464d..49b879dc 100644 --- a/test/Servant/API/ContentTypesSpec.hs +++ b/test/Servant/API/ContentTypesSpec.hs @@ -6,11 +6,12 @@ module Servant.API.ContentTypesSpec where import Control.Applicative +import Control.Arrow import Data.Aeson import Data.Function (on) import Data.Proxy -import Data.ByteString.Char8 +import Data.ByteString.Char8 (ByteString, append, pack) import qualified Data.ByteString.Lazy as BSL import Data.List (maximumBy) import Data.Maybe (fromJust, isJust, isNothing) @@ -19,6 +20,7 @@ import Data.String.Conversions (cs) import qualified Data.Text as TextS import qualified Data.Text.Lazy as TextL import GHC.Generics +import Network.URL (importParams, exportParams) import Test.Hspec import Test.QuickCheck import Test.QuickCheck.Instances () @@ -44,6 +46,14 @@ spec = describe "Servant.API.ContentTypes" $ do let p = Proxy :: Proxy FormUrlEncoded property $ \x -> fromByteString p (toByteString p x) == Right (x::[(TextS.Text,TextS.Text)]) + it "has fromByteString reverse exportParams (Network.URL)" $ do + let p = Proxy :: Proxy FormUrlEncoded + property $ \x -> (fromByteString p . cs . exportParams . map (cs *** cs) $ x) == Right (x::[(TextS.Text,TextS.Text)]) + + it "has importParams (Network.URL) reverse toByteString" $ do + let p = Proxy :: Proxy FormUrlEncoded + property $ \x -> (fmap (map (cs *** cs)) . importParams . cs . toByteString p $ x) == Just (x::[(TextS.Text,TextS.Text)]) + describe "The PlainText Content-Type type" $ do it "has fromByteString reverse toByteString (lazy Text)" $ do