http-api-data 0.5 → 0.5.1
raw patch · 4 files changed
+89/−28 lines, 4 filesdep ~basedep ~base-compatdep ~text-show
Dependency ranges changed: base, base-compat, text-show
Files
- CHANGELOG.md +7/−0
- http-api-data.cabal +7/−6
- src/Web/Internal/HttpApiData.hs +71/−22
- test/Web/Internal/HttpApiDataSpec.hs +4/−0
CHANGELOG.md view
@@ -1,3 +1,10 @@+0.5.1+-----++* Add `toEncodedQueryParam` to `ToHttpApiData` type class. It has default+ implementation using `toQueryParam`, but may be overriden with more efficient+ one.+ 0.5 ---
http-api-data.cabal view
@@ -1,6 +1,6 @@ cabal-version: >= 1.10 name: http-api-data-version: 0.5+version: 0.5.1 synopsis: Converting to/from HTTP API data like URL pieces, headers and query parameters. category: Web@@ -31,8 +31,9 @@ GHC==8.8.4, GHC==8.10.7, GHC==9.0.2,- GHC==9.2.3,- GHC==9.4.1+ GHC==9.2.7,+ GHC==9.4.4,+ GHC==9.6.1 flag use-text-show description: Use text-show library for efficient ToHttpApiData implementations.@@ -44,7 +45,7 @@ include-dirs: include/ -- GHC bundled- build-depends: base >= 4.9 && < 4.17+ build-depends: base >= 4.9 && < 4.19 , bytestring >= 0.10.8.1 && < 0.12 , containers >= 0.5.7.1 && < 0.7 , text >= 1.2.3.0 && < 1.3 || >=2.0 && <2.1@@ -54,7 +55,7 @@ build-depends: attoparsec >= 0.13.2.2 && < 0.15 , attoparsec-iso8601 >= 1.1.0.0 && < 1.2- , base-compat >= 0.10.5 && < 0.13+ , base-compat >= 0.10.5 && < 0.14 , cookie >= 0.4.3 && < 0.5 , hashable >= 1.2.7.0 && < 1.5 , http-types >= 0.12.3 && < 0.13@@ -65,7 +66,7 @@ if flag(use-text-show) cpp-options: -DUSE_TEXT_SHOW- build-depends: text-show >= 3.8.2 && <3.10+ build-depends: text-show >= 3.8.2 && <3.11 exposed-modules: Web.HttpApiData
src/Web/Internal/HttpApiData.hs view
@@ -93,10 +93,10 @@ toUrlPiece = toQueryParam -- | Convert to a URL path piece, making sure to encode any special chars.- -- The default definition uses 'H.encodePathSegmentsRelative',+ -- The default definition uses @'H.urlEncodeBuilder' 'False'@ -- but this may be overriden with a more efficient version. toEncodedUrlPiece :: a -> BS.Builder- toEncodedUrlPiece = H.encodePathSegmentsRelative . (:[]) . toUrlPiece+ toEncodedUrlPiece = H.urlEncodeBuilder False . encodeUtf8 . toUrlPiece -- | Convert to HTTP header value. toHeader :: a -> ByteString@@ -106,6 +106,14 @@ toQueryParam :: a -> Text toQueryParam = toUrlPiece + -- | Convert to URL query param,+ -- The default definition uses @'H.urlEncodeBuilder' 'True'@+ -- but this may be overriden with a more efficient version.+ --+ -- @since 0.5.1+ toEncodedQueryParam :: a -> BS.Builder+ toEncodedQueryParam = H.urlEncodeBuilder True . encodeUtf8 . toQueryParam+ -- | Parse value from HTTP API data. -- -- __WARNING__: Do not derive this using @DeriveAnyClass@ as the generated@@ -422,12 +430,21 @@ unsafeToEncodedUrlPiece :: ToHttpApiData a => a -> BS.Builder unsafeToEncodedUrlPiece = BS.byteString . encodeUtf8 . toUrlPiece +-- | Convert to a URL-encoded query param using 'toQueryParam'.+-- /Note/: this function does not check if the result contains unescaped characters!+--+-- @since 0.5.1+unsafeToEncodedQueryParam :: ToHttpApiData a => a -> BS.Builder+unsafeToEncodedQueryParam = BS.byteString . encodeUtf8 . toQueryParam+ -- | -- >>> toUrlPiece () -- "_" instance ToHttpApiData () where- toUrlPiece () = "_"- toEncodedUrlPiece = unsafeToEncodedUrlPiece+ toUrlPiece _ = "_"+ toHeader _ = "_"+ toEncodedUrlPiece _ = "_"+ toEncodedQueryParam _ = "_" instance ToHttpApiData Char where toUrlPiece = T.singleton@@ -438,29 +455,30 @@ instance ToHttpApiData Version where toUrlPiece = T.pack . showVersion toEncodedUrlPiece = unsafeToEncodedUrlPiece+ toEncodedQueryParam = unsafeToEncodedQueryParam instance ToHttpApiData Void where toUrlPiece = absurd-instance ToHttpApiData Natural where toUrlPiece = showt; toEncodedUrlPiece = unsafeToEncodedUrlPiece+instance ToHttpApiData Natural where toUrlPiece = showt; toEncodedUrlPiece = unsafeToEncodedUrlPiece; toEncodedQueryParam = unsafeToEncodedQueryParam -instance ToHttpApiData Bool where toUrlPiece = showTextData; toEncodedUrlPiece = unsafeToEncodedUrlPiece-instance ToHttpApiData Ordering where toUrlPiece = showTextData; toEncodedUrlPiece = unsafeToEncodedUrlPiece+instance ToHttpApiData Bool where toUrlPiece = showTextData; toEncodedUrlPiece = unsafeToEncodedUrlPiece; toEncodedQueryParam = unsafeToEncodedQueryParam+instance ToHttpApiData Ordering where toUrlPiece = showTextData; toEncodedUrlPiece = unsafeToEncodedUrlPiece; toEncodedQueryParam = unsafeToEncodedQueryParam -instance ToHttpApiData Double where toUrlPiece = showt; toEncodedUrlPiece = unsafeToEncodedUrlPiece-instance ToHttpApiData Float where toUrlPiece = showt; toEncodedUrlPiece = unsafeToEncodedUrlPiece-instance ToHttpApiData Int where toUrlPiece = showt; toEncodedUrlPiece = unsafeToEncodedUrlPiece-instance ToHttpApiData Int8 where toUrlPiece = showt; toEncodedUrlPiece = unsafeToEncodedUrlPiece-instance ToHttpApiData Int16 where toUrlPiece = showt; toEncodedUrlPiece = unsafeToEncodedUrlPiece-instance ToHttpApiData Int32 where toUrlPiece = showt; toEncodedUrlPiece = unsafeToEncodedUrlPiece-instance ToHttpApiData Int64 where toUrlPiece = showt; toEncodedUrlPiece = unsafeToEncodedUrlPiece-instance ToHttpApiData Integer where toUrlPiece = showt; toEncodedUrlPiece = unsafeToEncodedUrlPiece-instance ToHttpApiData Word where toUrlPiece = showt; toEncodedUrlPiece = unsafeToEncodedUrlPiece-instance ToHttpApiData Word8 where toUrlPiece = showt; toEncodedUrlPiece = unsafeToEncodedUrlPiece-instance ToHttpApiData Word16 where toUrlPiece = showt; toEncodedUrlPiece = unsafeToEncodedUrlPiece-instance ToHttpApiData Word32 where toUrlPiece = showt; toEncodedUrlPiece = unsafeToEncodedUrlPiece-instance ToHttpApiData Word64 where toUrlPiece = showt; toEncodedUrlPiece = unsafeToEncodedUrlPiece+instance ToHttpApiData Double where toUrlPiece = showt; toEncodedUrlPiece = unsafeToEncodedUrlPiece; toEncodedQueryParam = unsafeToEncodedQueryParam+instance ToHttpApiData Float where toUrlPiece = showt; toEncodedUrlPiece = unsafeToEncodedUrlPiece; toEncodedQueryParam = unsafeToEncodedQueryParam+instance ToHttpApiData Int where toUrlPiece = showt; toEncodedUrlPiece = unsafeToEncodedUrlPiece; toEncodedQueryParam = unsafeToEncodedQueryParam+instance ToHttpApiData Int8 where toUrlPiece = showt; toEncodedUrlPiece = unsafeToEncodedUrlPiece; toEncodedQueryParam = unsafeToEncodedQueryParam+instance ToHttpApiData Int16 where toUrlPiece = showt; toEncodedUrlPiece = unsafeToEncodedUrlPiece; toEncodedQueryParam = unsafeToEncodedQueryParam+instance ToHttpApiData Int32 where toUrlPiece = showt; toEncodedUrlPiece = unsafeToEncodedUrlPiece; toEncodedQueryParam = unsafeToEncodedQueryParam+instance ToHttpApiData Int64 where toUrlPiece = showt; toEncodedUrlPiece = unsafeToEncodedUrlPiece; toEncodedQueryParam = unsafeToEncodedQueryParam+instance ToHttpApiData Integer where toUrlPiece = showt; toEncodedUrlPiece = unsafeToEncodedUrlPiece; toEncodedQueryParam = unsafeToEncodedQueryParam+instance ToHttpApiData Word where toUrlPiece = showt; toEncodedUrlPiece = unsafeToEncodedUrlPiece; toEncodedQueryParam = unsafeToEncodedQueryParam+instance ToHttpApiData Word8 where toUrlPiece = showt; toEncodedUrlPiece = unsafeToEncodedUrlPiece; toEncodedQueryParam = unsafeToEncodedQueryParam+instance ToHttpApiData Word16 where toUrlPiece = showt; toEncodedUrlPiece = unsafeToEncodedUrlPiece; toEncodedQueryParam = unsafeToEncodedQueryParam+instance ToHttpApiData Word32 where toUrlPiece = showt; toEncodedUrlPiece = unsafeToEncodedUrlPiece; toEncodedQueryParam = unsafeToEncodedQueryParam+instance ToHttpApiData Word64 where toUrlPiece = showt; toEncodedUrlPiece = unsafeToEncodedUrlPiece; toEncodedQueryParam = unsafeToEncodedQueryParam -- | Note: this instance is not polykinded-instance F.HasResolution a => ToHttpApiData (F.Fixed (a :: Type)) where toUrlPiece = showt; toEncodedUrlPiece = unsafeToEncodedUrlPiece+instance F.HasResolution a => ToHttpApiData (F.Fixed (a :: Type)) where toUrlPiece = showt; toEncodedUrlPiece = unsafeToEncodedUrlPiece; toEncodedQueryParam = unsafeToEncodedQueryParam -- | -- >>> toUrlPiece (fromGregorian 2015 10 03)@@ -468,6 +486,7 @@ instance ToHttpApiData Day where toUrlPiece = T.pack . show toEncodedUrlPiece = unsafeToEncodedUrlPiece+ toEncodedQueryParam = unsafeToEncodedQueryParam timeToUrlPiece :: FormatTime t => String -> t -> Text timeToUrlPiece fmt = T.pack . formatTime defaultTimeLocale (iso8601DateFormat (Just fmt))@@ -478,6 +497,7 @@ instance ToHttpApiData TimeOfDay where toUrlPiece = T.pack . formatTime defaultTimeLocale "%H:%M:%S%Q" toEncodedUrlPiece = unsafeToEncodedUrlPiece+ -- no toEncodedQueryParam as : is unsafe char. -- | -- >>> toUrlPiece $ LocalTime (fromGregorian 2015 10 03) (TimeOfDay 14 55 21.687)@@ -485,6 +505,7 @@ instance ToHttpApiData LocalTime where toUrlPiece = timeToUrlPiece "%H:%M:%S%Q" toEncodedUrlPiece = unsafeToEncodedUrlPiece+ -- no toEncodedQueryParam as : is unsafe char. -- | -- >>> toUrlPiece $ ZonedTime (LocalTime (fromGregorian 2015 10 03) (TimeOfDay 14 55 51.001)) utc@@ -492,6 +513,7 @@ instance ToHttpApiData ZonedTime where toUrlPiece = timeToUrlPiece "%H:%M:%S%Q%z" toEncodedUrlPiece = unsafeToEncodedUrlPiece+ -- no toEncodedQueryParam as : is unsafe char. -- | -- >>> toUrlPiece $ UTCTime (fromGregorian 2015 10 03) 864.5@@ -499,6 +521,7 @@ instance ToHttpApiData UTCTime where toUrlPiece = timeToUrlPiece "%H:%M:%S%QZ" toEncodedUrlPiece = unsafeToEncodedUrlPiece+ -- no toEncodedQueryParam as : is unsafe char. -- | -- >>> toUrlPiece Monday@@ -513,8 +536,9 @@ toUrlPiece Sunday = "sunday" toEncodedUrlPiece = unsafeToEncodedUrlPiece+ toEncodedQueryParam = unsafeToEncodedQueryParam --- | +-- | -- >>> toUrlPiece Q4 -- "q4" instance ToHttpApiData QuarterOfYear where@@ -523,6 +547,9 @@ toUrlPiece Q3 = "q3" toUrlPiece Q4 = "q4" + toEncodedUrlPiece = unsafeToEncodedUrlPiece+ toEncodedQueryParam = unsafeToEncodedQueryParam+ -- | -- >>> import Data.Time.Calendar.Quarter.Compat (Quarter (..)) -- >>> MkQuarter 8040@@ -540,6 +567,9 @@ f Q3 = "q3" f Q4 = "q4" + toEncodedUrlPiece = unsafeToEncodedUrlPiece+ toEncodedQueryParam = unsafeToEncodedQueryParam+ -- | -- >>> import Data.Time.Calendar.Month.Compat (Month (..)) -- >>> MkMonth 24482@@ -551,8 +581,13 @@ instance ToHttpApiData Month where toUrlPiece = T.pack . formatTime defaultTimeLocale "%Y-%m" + toEncodedUrlPiece = unsafeToEncodedUrlPiece+ toEncodedQueryParam = unsafeToEncodedQueryParam+ instance ToHttpApiData NominalDiffTime where toUrlPiece = toUrlPiece . nominalDiffTimeToSeconds++ toEncodedQueryParam = unsafeToEncodedQueryParam toEncodedUrlPiece = unsafeToEncodedUrlPiece instance ToHttpApiData String where toUrlPiece = T.pack@@ -562,46 +597,57 @@ instance ToHttpApiData All where toUrlPiece = coerce (toUrlPiece :: Bool -> Text) toEncodedUrlPiece = coerce (toEncodedUrlPiece :: Bool -> BS.Builder)+ toEncodedQueryParam = coerce (toEncodedQueryParam :: Bool -> BS.Builder) instance ToHttpApiData Any where toUrlPiece = coerce (toUrlPiece :: Bool -> Text) toEncodedUrlPiece = coerce (toEncodedUrlPiece :: Bool -> BS.Builder)+ toEncodedQueryParam = coerce (toEncodedQueryParam :: Bool -> BS.Builder) instance ToHttpApiData a => ToHttpApiData (Dual a) where toUrlPiece = coerce (toUrlPiece :: a -> Text) toEncodedUrlPiece = coerce (toEncodedUrlPiece :: a -> BS.Builder)+ toEncodedQueryParam = coerce (toEncodedQueryParam :: a -> BS.Builder) instance ToHttpApiData a => ToHttpApiData (Sum a) where toUrlPiece = coerce (toUrlPiece :: a -> Text) toEncodedUrlPiece = coerce (toEncodedUrlPiece :: a -> BS.Builder)+ toEncodedQueryParam = coerce (toEncodedQueryParam :: a -> BS.Builder) instance ToHttpApiData a => ToHttpApiData (Product a) where toUrlPiece = coerce (toUrlPiece :: a -> Text) toEncodedUrlPiece = coerce (toEncodedUrlPiece :: a -> BS.Builder)+ toEncodedQueryParam = coerce (toEncodedQueryParam :: a -> BS.Builder) instance ToHttpApiData a => ToHttpApiData (First a) where toUrlPiece = coerce (toUrlPiece :: Maybe a -> Text) toEncodedUrlPiece = coerce (toEncodedUrlPiece :: Maybe a -> BS.Builder)+ toEncodedQueryParam = coerce (toEncodedQueryParam :: Maybe a -> BS.Builder) instance ToHttpApiData a => ToHttpApiData (Last a) where toUrlPiece = coerce (toUrlPiece :: Maybe a -> Text) toEncodedUrlPiece = coerce (toEncodedUrlPiece :: Maybe a -> BS.Builder)+ toEncodedQueryParam = coerce (toEncodedQueryParam :: Maybe a -> BS.Builder) instance ToHttpApiData a => ToHttpApiData (Semi.Min a) where toUrlPiece = coerce (toUrlPiece :: a -> Text) toEncodedUrlPiece = coerce (toEncodedUrlPiece :: a -> BS.Builder)+ toEncodedQueryParam = coerce (toEncodedQueryParam :: a -> BS.Builder) instance ToHttpApiData a => ToHttpApiData (Semi.Max a) where toUrlPiece = coerce (toUrlPiece :: a -> Text) toEncodedUrlPiece = coerce (toEncodedUrlPiece :: a -> BS.Builder)+ toEncodedQueryParam = coerce (toEncodedQueryParam :: a -> BS.Builder) instance ToHttpApiData a => ToHttpApiData (Semi.First a) where toUrlPiece = coerce (toUrlPiece :: a -> Text) toEncodedUrlPiece = coerce (toEncodedUrlPiece :: a -> BS.Builder)+ toEncodedQueryParam = coerce (toEncodedQueryParam :: a -> BS.Builder) instance ToHttpApiData a => ToHttpApiData (Semi.Last a) where toUrlPiece = coerce (toUrlPiece :: a -> Text) toEncodedUrlPiece = coerce (toEncodedUrlPiece :: a -> BS.Builder)+ toEncodedQueryParam = coerce (toEncodedQueryParam :: a -> BS.Builder) -- | -- >>> toUrlPiece (Just "Hello")@@ -639,6 +685,7 @@ toHeader = coerce (toHeader :: a -> ByteString) toQueryParam = coerce (toQueryParam :: a -> Text) toEncodedUrlPiece = coerce (toEncodedUrlPiece :: a -> BS.Builder)+ toEncodedQueryParam = coerce (toEncodedQueryParam :: a -> BS.Builder) -- | @since 0.4.2 instance ToHttpApiData a => ToHttpApiData (Const a b) where@@ -646,6 +693,7 @@ toHeader = coerce (toHeader :: a -> ByteString) toQueryParam = coerce (toQueryParam :: a -> Text) toEncodedUrlPiece = coerce (toEncodedUrlPiece :: a -> BS.Builder)+ toEncodedQueryParam = coerce (toEncodedQueryParam :: a -> BS.Builder) -- | @since 0.4.2 instance ToHttpApiData a => ToHttpApiData (Identity a) where@@ -653,6 +701,7 @@ toHeader = coerce (toHeader :: a -> ByteString) toQueryParam = coerce (toQueryParam :: a -> Text) toEncodedUrlPiece = coerce (toEncodedUrlPiece :: a -> BS.Builder)+ toEncodedQueryParam = coerce (toEncodedQueryParam :: a -> BS.Builder) -- | -- >>> parseUrlPiece "_" :: Either Text ()
test/Web/Internal/HttpApiDataSpec.hs view
@@ -46,6 +46,9 @@ encodedUrlPieceProp :: ToHttpApiData a => a -> Property encodedUrlPieceProp x = toLazyByteString (toEncodedUrlPiece (toUrlPiece x)) === toLazyByteString (toEncodedUrlPiece x) +encodedQueryParamProp :: ToHttpApiData a => a -> Property+encodedQueryParamProp x = toLazyByteString (toEncodedQueryParam (toQueryParam x)) === toLazyByteString (toEncodedQueryParam x)+ -- | Check 'ToHttpApiData' and 'FromHttpApiData' compatibility checkUrlPiece :: forall a. (Eq a, ToHttpApiData a, FromHttpApiData a, Show a, Arbitrary a) => Proxy a -> String -> Spec checkUrlPiece _ = checkUrlPiece' (arbitrary :: Gen a)@@ -56,6 +59,7 @@ prop "toQueryParam <=> parseQueryParam" $ forAll gen (toQueryParam <=> parseQueryParam :: a -> Property) prop "toHeader <=> parseHeader" $ forAll gen (toHeader <=> parseHeader :: a -> Property) prop "toEncodedUrlPiece encodes correctly" $ forAll gen encodedUrlPieceProp+ prop "toEncodedQueryParam encodes correctly" $ forAll gen encodedQueryParamProp -- | Check case insensitivity for @parseUrlPiece@. checkUrlPieceI :: forall a. (Eq a, ToHttpApiData a, FromHttpApiData a, Arbitrary a) => Proxy a -> String -> Spec