git.iko.soy/typeable/http-api-data
on master help sign in
Add Semigroup.Min/Max/First/Last and Tagged instances
6454ed0Oleg Grenrus
.travis.yml+6−7
@@ -32,12 +32,12 @@ before_cache:
32
32
33matrix:
33matrix:
34 include:
34 include:
35− - compiler: "ghc-8.6.1"
35+ - compiler: "ghc-8.6.2"
36 # env: TEST=--disable-tests BENCH=--disable-benchmarks
36 # env: TEST=--disable-tests BENCH=--disable-benchmarks
37− addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-8.6.1], sources: [hvr-ghc]}}
37+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-8.6.2], sources: [hvr-ghc]}}
38− - compiler: "ghc-8.4.3"
38+ - compiler: "ghc-8.4.4"
39 # env: TEST=--disable-tests BENCH=--disable-benchmarks
39 # env: TEST=--disable-tests BENCH=--disable-benchmarks
40− addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-8.4.3], sources: [hvr-ghc]}}
40+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-8.4.4], sources: [hvr-ghc]}}
41 - compiler: "ghc-8.2.2"
41 - compiler: "ghc-8.2.2"
42 # env: TEST=--disable-tests BENCH=--disable-benchmarks
42 # env: TEST=--disable-tests BENCH=--disable-benchmarks
43 addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-8.2.2], sources: [hvr-ghc]}}
43 addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-8.2.2], sources: [hvr-ghc]}}
@@ -93,8 +93,8 @@ install:
93# any command which exits with a non-zero exit code causes the build to fail.
93# any command which exits with a non-zero exit code causes the build to fail.
94script:
94script:
95 # test that source-distributions can be generated
95 # test that source-distributions can be generated
96− - (cd "." && cabal sdist)
96+ - cabal new-sdist all
97− - mv "."/dist/http-api-data-*.tar.gz ${DISTDIR}/
97+ - mv dist-newstyle/sdist/*.tar.gz ${DISTDIR}/
98 - cd ${DISTDIR} || false
98 - cd ${DISTDIR} || false
99 - find . -maxdepth 1 -name '*.tar.gz' -exec tar -xvf '{}' \;
99 - find . -maxdepth 1 -name '*.tar.gz' -exec tar -xvf '{}' \;
100 - "printf 'packages: http-api-data-*/*.cabal\\n' > cabal.project"
100 - "printf 'packages: http-api-data-*/*.cabal\\n' > cabal.project"
@@ -113,7 +113,6 @@ script:
113 - (cd http-api-data-* && cabal check)
113 - (cd http-api-data-* && cabal check)
114
114
115 # haddock
115 # haddock
116− - rm -rf ./dist-newstyle
117 - if $HADDOCK; then cabal new-haddock -w ${HC} ${TEST} ${BENCH} all; else echo "Skipping haddock generation";fi
116 - if $HADDOCK; then cabal new-haddock -w ${HC} ${TEST} ${BENCH} all; else echo "Skipping haddock generation";fi
118
117
119 # Build without installed constraints for packages in global-db
118 # Build without installed constraints for packages in global-db
CHANGELOG.md+7
@@ -1,3 +1,10 @@
1+0.4
2+---
3+
4+* `NominalDiffTime` instances preserve precision (roundtrip)
5+* Add `Semigroup.Min`, `Max`, `First`, `Last` instances
6+* Add `Tagged b a` instances
7+
10.3.10
80.3.10
2---
9---
3
10
http-api-data.cabal+6−3
@@ -1,6 +1,6 @@
1cabal-version: >= 1.10
1cabal-version: >= 1.10
2name: http-api-data
2name: http-api-data
3−version: 0.3.10
3+version: 0.4
4
4
5synopsis: Converting to/from HTTP API data like URL pieces, headers and query parameters.
5synopsis: Converting to/from HTTP API data like URL pieces, headers and query parameters.
6category: Web
6category: Web
@@ -22,13 +22,14 @@ extra-source-files:
22 test/*.hs
22 test/*.hs
23 CHANGELOG.md
23 CHANGELOG.md
24 README.md
24 README.md
25+
25tested-with:
26tested-with:
26 GHC==7.8.4,
27 GHC==7.8.4,
27 GHC==7.10.3,
28 GHC==7.10.3,
28 GHC==8.0.2,
29 GHC==8.0.2,
29 GHC==8.2.2,
30 GHC==8.2.2,
30− GHC==8.4.3,
31+ GHC==8.4.4,
31− GHC==8.6.1
32+ GHC==8.6.2
32
33
33custom-setup
34custom-setup
34 setup-depends:
35 setup-depends:
@@ -60,6 +61,7 @@ library
60 , cookie >= 0.4.3 && < 0.4.5
61 , cookie >= 0.4.3 && < 0.4.5
61 , hashable >= 1.2.7.0 && < 1.3
62 , hashable >= 1.2.7.0 && < 1.3
62 , http-types >= 0.12.2 && < 0.13
63 , http-types >= 0.12.2 && < 0.13
64+ , tagged >= 0.8.5 && < 0.9
63 , time-locale-compat >= 0.1.1.5 && < 0.2
65 , time-locale-compat >= 0.1.1.5 && < 0.2
64 , unordered-containers >= 0.2.9.0 && < 0.3
66 , unordered-containers >= 0.2.9.0 && < 0.3
65 , uuid-types >= 1.0.3 && <1.1
67 , uuid-types >= 1.0.3 && <1.1
@@ -98,6 +100,7 @@ test-suite spec
98 -- inherited depndencies
100 -- inherited depndencies
99 build-depends:
101 build-depends:
100 base
102 base
103+ , base-compat
101 , bytestring
104 , bytestring
102 , cookie
105 , cookie
103 , http-api-data
106 , http-api-data
src/Web/Internal/FormUrlEncoded.hs+35−21
@@ -17,7 +17,7 @@
17#include "overlapping-compat.h"
17#include "overlapping-compat.h"
18module Web.Internal.FormUrlEncoded where
18module Web.Internal.FormUrlEncoded where
19
19
20−import Prelude ()
20+import Prelude ()
21import Prelude.Compat
21import Prelude.Compat
22
22
23import Control.Arrow ((***))
23import Control.Arrow ((***))
@@ -25,38 +25,38 @@ import Control.Monad ((<=<))
25import Data.ByteString.Builder (shortByteString, toLazyByteString)
25import Data.ByteString.Builder (shortByteString, toLazyByteString)
26import qualified Data.ByteString.Lazy as BSL
26import qualified Data.ByteString.Lazy as BSL
27import qualified Data.ByteString.Lazy.Char8 as BSL8
27import qualified Data.ByteString.Lazy.Char8 as BSL8
28+import Data.Coerce (coerce)
28import qualified Data.Foldable as F
29import qualified Data.Foldable as F
29import Data.Hashable (Hashable)
30import Data.Hashable (Hashable)
30import Data.HashMap.Strict (HashMap)
31import Data.HashMap.Strict (HashMap)
31import qualified Data.HashMap.Strict as HashMap
32import qualified Data.HashMap.Strict as HashMap
32−import Data.Int
33+import Data.Int (Int16, Int32, Int64, Int8)
33import Data.IntMap (IntMap)
34import Data.IntMap (IntMap)
34import qualified Data.IntMap as IntMap
35import qualified Data.IntMap as IntMap
35import Data.List (intersperse, sortBy)
36import Data.List (intersperse, sortBy)
36import Data.Map (Map)
37import Data.Map (Map)
37import qualified Data.Map as Map
38import qualified Data.Map as Map
38−import Data.Monoid
39+import Data.Monoid (All (..), Any (..), Dual (..),
40+ Product (..), Sum (..))
39import Data.Ord (comparing)
41import Data.Ord (comparing)
42+import Data.Proxy (Proxy (..))
43+import Data.Semigroup (Semigroup (..))
40import qualified Data.Semigroup as Semi
44import qualified Data.Semigroup as Semi
41−
45+import Data.Tagged (Tagged (..))
42import Data.Text (Text)
46import Data.Text (Text)
43import qualified Data.Text as Text
47import qualified Data.Text as Text
44−import Data.Text.Encoding as Text
48+import qualified Data.Text.Encoding as Text
45import Data.Text.Encoding.Error (lenientDecode)
49import Data.Text.Encoding.Error (lenientDecode)
46import qualified Data.Text.Lazy as Lazy
50import qualified Data.Text.Lazy as Lazy
47−
48−import Data.Proxy
49−import Data.Time
51+import Data.Time (Day, LocalTime, NominalDiffTime,
52+ UTCTime, ZonedTime)
53+import Data.Void (Void)
50−import Data.Word
54+import Data.Word (Word16, Word32, Word64, Word8)
51−
52−import Data.Void
53−import Numeric.Natural
54−
55import GHC.Exts (Constraint, IsList (..))
55import GHC.Exts (Constraint, IsList (..))
56import GHC.Generics
56import GHC.Generics
57import GHC.TypeLits
57import GHC.TypeLits
58import Network.HTTP.Types.URI (urlDecode, urlEncodeBuilder)
58import Network.HTTP.Types.URI (urlDecode, urlEncodeBuilder)
59−
59+import Numeric.Natural (Natural)
60import Web.Internal.HttpApiData
60import Web.Internal.HttpApiData
61
61
62-- $setup
62-- $setup
@@ -119,9 +119,16 @@ instance ToFormKey Lazy.Text where toFormKey = toQueryParam
119instance ToFormKey All where toFormKey = toQueryParam
119instance ToFormKey All where toFormKey = toQueryParam
120instance ToFormKey Any where toFormKey = toQueryParam
120instance ToFormKey Any where toFormKey = toQueryParam
121
121
122−instance ToFormKey a => ToFormKey (Dual a) where toFormKey = toFormKey . getDual
122+instance ToFormKey a => ToFormKey (Dual a) where toFormKey = coerce (toFormKey :: a -> Text)
123−instance ToFormKey a => ToFormKey (Sum a) where toFormKey = toFormKey . getSum
123+instance ToFormKey a => ToFormKey (Sum a) where toFormKey = coerce (toFormKey :: a -> Text)
124−instance ToFormKey a => ToFormKey (Product a) where toFormKey = toFormKey . getProduct
124+instance ToFormKey a => ToFormKey (Product a) where toFormKey = coerce (toFormKey :: a -> Text)
125+
126+instance ToFormKey a => ToFormKey (Semi.Min a) where toFormKey = coerce (toFormKey :: a -> Text)
127+instance ToFormKey a => ToFormKey (Semi.Max a) where toFormKey = coerce (toFormKey :: a -> Text)
128+instance ToFormKey a => ToFormKey (Semi.First a) where toFormKey = coerce (toFormKey :: a -> Text)
129+instance ToFormKey a => ToFormKey (Semi.Last a) where toFormKey = coerce (toFormKey :: a -> Text)
130+
131+instance ToFormKey a => ToFormKey (Tagged b a) where toFormKey = coerce (toFormKey :: a -> Text)
125
132
126instance ToFormKey Void where toFormKey = toQueryParam
133instance ToFormKey Void where toFormKey = toQueryParam
127instance ToFormKey Natural where toFormKey = toQueryParam
134instance ToFormKey Natural where toFormKey = toQueryParam
@@ -164,9 +171,16 @@ instance FromFormKey Lazy.Text where parseFormKey = parseQueryParam
164instance FromFormKey All where parseFormKey = parseQueryParam
171instance FromFormKey All where parseFormKey = parseQueryParam
165instance FromFormKey Any where parseFormKey = parseQueryParam
172instance FromFormKey Any where parseFormKey = parseQueryParam
166
173
167−instance FromFormKey a => FromFormKey (Dual a) where parseFormKey = fmap Dual . parseFormKey
174+instance FromFormKey a => FromFormKey (Dual a) where parseFormKey = coerce (parseFormKey :: Text -> Either Text a)
168−instance FromFormKey a => FromFormKey (Sum a) where parseFormKey = fmap Sum . parseFormKey
175+instance FromFormKey a => FromFormKey (Sum a) where parseFormKey = coerce (parseFormKey :: Text -> Either Text a)
176+instance FromFormKey a => FromFormKey (Product a) where parseFormKey = coerce (parseFormKey :: Text -> Either Text a)
177+
178+instance FromFormKey a => FromFormKey (Semi.Min a) where parseFormKey = coerce (parseFormKey :: Text -> Either Text a)
179+instance FromFormKey a => FromFormKey (Semi.Max a) where parseFormKey = coerce (parseFormKey :: Text -> Either Text a)
169−instance FromFormKey a => FromFormKey (Product a) where parseFormKey = fmap Product . parseFormKey
180+instance FromFormKey a => FromFormKey (Semi.First a) where parseFormKey = coerce (parseFormKey :: Text -> Either Text a)
181+instance FromFormKey a => FromFormKey (Semi.Last a) where parseFormKey = coerce (parseFormKey :: Text -> Either Text a)
182+
183+instance FromFormKey a => FromFormKey (Tagged b a) where parseFormKey = coerce (parseFormKey :: Text -> Either Text a)
170
184
171instance FromFormKey Void where parseFormKey = parseQueryParam
185instance FromFormKey Void where parseFormKey = parseQueryParam
172instance FromFormKey Natural where parseFormKey = parseQueryParam
186instance FromFormKey Natural where parseFormKey = parseQueryParam
@@ -175,7 +189,7 @@ instance FromFormKey Natural where parseFormKey = parseQueryParam
175--
189--
176-- 'Form' can be URL-encoded with 'urlEncodeForm' and URL-decoded with 'urlDecodeForm'.
190-- 'Form' can be URL-encoded with 'urlEncodeForm' and URL-decoded with 'urlDecodeForm'.
177newtype Form = Form { unForm :: HashMap Text [Text] }
191newtype Form = Form { unForm :: HashMap Text [Text] }
178− deriving (Eq, Read, Generic, Semi.Semigroup, Monoid)
192+ deriving (Eq, Read, Generic, Semigroup, Monoid)
179
193
180instance Show Form where
194instance Show Form where
181 showsPrec d form = showParen (d > 10) $
195 showsPrec d form = showParen (d > 10) $
src/Web/Internal/HttpApiData.hs+92−49
@@ -13,21 +13,27 @@
13-- such as URL pieces, headers and query parameters.
13-- such as URL pieces, headers and query parameters.
14module Web.Internal.HttpApiData where
14module Web.Internal.HttpApiData where
15
15
16−import Prelude ()
16+import Prelude ()
17import Prelude.Compat
17import Prelude.Compat
18
18
19import Control.Arrow (left, (&&&))
19import Control.Arrow (left, (&&&))
20import Control.Monad ((<=<))
20import Control.Monad ((<=<))
21−
21+import qualified Data.Attoparsec.Text as Atto
22+import qualified Data.Attoparsec.Time as Atto
22import Data.ByteString (ByteString)
23import Data.ByteString (ByteString)
23import qualified Data.ByteString as BS
24import qualified Data.ByteString as BS
25+import qualified Data.ByteString.Builder as BS
24import qualified Data.ByteString.Lazy as LBS
26import qualified Data.ByteString.Lazy as LBS
25−import Data.Monoid
27+import Data.Coerce (coerce)
26−
28+import Data.Data (Data)
27import qualified Data.Fixed as F
29import qualified Data.Fixed as F
28−import Data.Int
30+import Data.Int (Int16, Int32, Int64, Int8)
29−import Data.Word
31+import Data.Monoid (All (..), Any (..), Dual (..),
30−
32+ First (..), Last (..),
33+ Product (..), Sum (..))
34+import Data.Semigroup (Semigroup (..))
35+import qualified Data.Semigroup as Semi
36+import Data.Tagged (Tagged (..))
31import Data.Text (Text)
37import Data.Text (Text)
32import qualified Data.Text as T
38import qualified Data.Text as T
33import Data.Text.Encoding (decodeUtf8', decodeUtf8With,
39import Data.Text.Encoding (decodeUtf8', decodeUtf8With,
@@ -36,43 +42,41 @@ import Data.Text.Encoding.Error (lenientDecode)
36import qualified Data.Text.Lazy as L
42import qualified Data.Text.Lazy as L
37import Data.Text.Read (Reader, decimal, rational,
43import Data.Text.Read (Reader, decimal, rational,
38 signed)
44 signed)
39−
40−import Data.Time
45+import Data.Time (Day, FormatTime, LocalTime,
46+ NominalDiffTime, TimeOfDay,
47+ UTCTime, ZonedTime, formatTime)
41−import Data.Time.Locale.Compat
48+import Data.Time.Locale.Compat (defaultTimeLocale,
49+ iso8601DateFormat)
50+import Data.Typeable (Typeable)
51+import qualified Data.UUID.Types as UUID
52+import Data.Version (Version, parseVersion,
53+ showVersion)
54+import Data.Void (Void, absurd)
55+import Data.Word (Word16, Word32, Word64, Word8)
56+import qualified Network.HTTP.Types as H
57+import Numeric.Natural (Natural)
58+import Text.ParserCombinators.ReadP (readP_to_S)
59+import Text.Read (readMaybe)
60+import Web.Cookie (SetCookie, parseSetCookie,
61+ renderSetCookie)
42
62
43#if MIN_VERSION_time(1,9,1)
63#if MIN_VERSION_time(1,9,1)
44−import Data.Time (nominalDiffTimeToSeconds, secondsToNominalDiffTime)
64+import Data.Time (nominalDiffTimeToSeconds,
65+ secondsToNominalDiffTime)
45#endif
66#endif
46
67
47−import Data.Version
48−
49−import Data.Void
50−import Numeric.Natural
51−
52−import Text.ParserCombinators.ReadP (readP_to_S)
53−import Text.Read (readMaybe)
54−
55#if USE_TEXT_SHOW
68#if USE_TEXT_SHOW
56import TextShow (TextShow, showt)
69import TextShow (TextShow, showt)
57#endif
70#endif
58
71
59−import qualified Data.UUID.Types as UUID
60
72
61−import qualified Data.ByteString.Builder as BS
62−import Data.Data (Data)
63−import Data.Typeable (Typeable)
64−import qualified Network.HTTP.Types as H
65−
66−import qualified Data.Attoparsec.Text as Atto
67−import qualified Data.Attoparsec.Time as Atto
68−
69−import Web.Cookie (SetCookie, parseSetCookie,
70− renderSetCookie)
71
73
72
74
73-- $setup
75-- $setup
74-- >>> data BasicAuthToken = BasicAuthToken Text deriving (Show)
76-- >>> data BasicAuthToken = BasicAuthToken Text deriving (Show)
75-- >>> instance FromHttpApiData BasicAuthToken where parseHeader h = BasicAuthToken <$> parseHeaderWithPrefix "Basic " h; parseQueryParam p = BasicAuthToken <$> parseQueryParam p
77-- >>> instance FromHttpApiData BasicAuthToken where parseHeader h = BasicAuthToken <$> parseHeaderWithPrefix "Basic " h; parseQueryParam p = BasicAuthToken <$> parseQueryParam p
78+-- >>> import Data.Time
79+-- >>> import Data.Version
76
80
77-- | Convert value to HTTP API data.
81-- | Convert value to HTTP API data.
78--
82--
@@ -519,28 +523,49 @@ instance ToHttpApiData String where toUrlPiece = T.pack
519instance ToHttpApiData Text where toUrlPiece = id
523instance ToHttpApiData Text where toUrlPiece = id
520instance ToHttpApiData L.Text where toUrlPiece = L.toStrict
524instance ToHttpApiData L.Text where toUrlPiece = L.toStrict
521
525
522−instance ToHttpApiData All where toUrlPiece = toUrlPiece . getAll; toEncodedUrlPiece = toEncodedUrlPiece . getAll
526+instance ToHttpApiData All where
527+ toUrlPiece = coerce (toUrlPiece :: Bool -> Text)
528+ toEncodedUrlPiece = coerce (toEncodedUrlPiece :: Bool -> BS.Builder)
529+
523−instance ToHttpApiData Any where toUrlPiece = toUrlPiece . getAny; toEncodedUrlPiece = toEncodedUrlPiece . getAny
530+instance ToHttpApiData Any where
531+ toUrlPiece = coerce (toUrlPiece :: Bool -> Text)
532+ toEncodedUrlPiece = coerce (toEncodedUrlPiece :: Bool -> BS.Builder)
524
533
525instance ToHttpApiData a => ToHttpApiData (Dual a) where
534instance ToHttpApiData a => ToHttpApiData (Dual a) where
526− toUrlPiece = toUrlPiece . getDual
535+ toUrlPiece = coerce (toUrlPiece :: a -> Text)
527− toEncodedUrlPiece = toEncodedUrlPiece . getDual
536+ toEncodedUrlPiece = coerce (toEncodedUrlPiece :: a -> BS.Builder)
528
537
529instance ToHttpApiData a => ToHttpApiData (Sum a) where
538instance ToHttpApiData a => ToHttpApiData (Sum a) where
530− toUrlPiece = toUrlPiece . getSum
539+ toUrlPiece = coerce (toUrlPiece :: a -> Text)
531− toEncodedUrlPiece = toEncodedUrlPiece . getSum
540+ toEncodedUrlPiece = coerce (toEncodedUrlPiece :: a -> BS.Builder)
532
541
533instance ToHttpApiData a => ToHttpApiData (Product a) where
542instance ToHttpApiData a => ToHttpApiData (Product a) where
534− toUrlPiece = toUrlPiece . getProduct
543+ toUrlPiece = coerce (toUrlPiece :: a -> Text)
535− toEncodedUrlPiece = toEncodedUrlPiece . getProduct
544+ toEncodedUrlPiece = coerce (toEncodedUrlPiece :: a -> BS.Builder)
536
545
537instance ToHttpApiData a => ToHttpApiData (First a) where
546instance ToHttpApiData a => ToHttpApiData (First a) where
538− toUrlPiece = toUrlPiece . getFirst
547+ toUrlPiece = coerce (toUrlPiece :: Maybe a -> Text)
539− toEncodedUrlPiece = toEncodedUrlPiece . getFirst
548+ toEncodedUrlPiece = coerce (toEncodedUrlPiece :: Maybe a -> BS.Builder)
540
549
541instance ToHttpApiData a => ToHttpApiData (Last a) where
550instance ToHttpApiData a => ToHttpApiData (Last a) where
542− toUrlPiece = toUrlPiece . getLast
551+ toUrlPiece = coerce (toUrlPiece :: Maybe a -> Text)
552+ toEncodedUrlPiece = coerce (toEncodedUrlPiece :: Maybe a -> BS.Builder)
553+
554+instance ToHttpApiData a => ToHttpApiData (Semi.Min a) where
555+ toUrlPiece = coerce (toUrlPiece :: a -> Text)
556+ toEncodedUrlPiece = coerce (toEncodedUrlPiece :: a -> BS.Builder)
557+
558+instance ToHttpApiData a => ToHttpApiData (Semi.Max a) where
559+ toUrlPiece = coerce (toUrlPiece :: a -> Text)
560+ toEncodedUrlPiece = coerce (toEncodedUrlPiece :: a -> BS.Builder)
561+
562+instance ToHttpApiData a => ToHttpApiData (Semi.First a) where
563+ toUrlPiece = coerce (toUrlPiece :: a -> Text)
543− toEncodedUrlPiece = toEncodedUrlPiece . getLast
564+ toEncodedUrlPiece = coerce (toEncodedUrlPiece :: a -> BS.Builder)
565+
566+instance ToHttpApiData a => ToHttpApiData (Semi.Last a) where
567+ toUrlPiece = coerce (toUrlPiece :: a -> Text)
568+ toEncodedUrlPiece = coerce (toEncodedUrlPiece :: a -> BS.Builder)
544
569
545-- |
570-- |
546-- >>> toUrlPiece (Just "Hello")
571-- >>> toUrlPiece (Just "Hello")
@@ -572,6 +597,12 @@ instance ToHttpApiData SetCookie where
572 toHeader = LBS.toStrict . BS.toLazyByteString . renderSetCookie
597 toHeader = LBS.toStrict . BS.toLazyByteString . renderSetCookie
573 -- toEncodedUrlPiece = renderSetCookie -- doesn't do things.
598 -- toEncodedUrlPiece = renderSetCookie -- doesn't do things.
574
599
600+instance ToHttpApiData a => ToHttpApiData (Tagged b a) where
601+ toUrlPiece = coerce (toUrlPiece :: a -> Text)
602+ toHeader = coerce (toHeader :: a -> ByteString)
603+ toQueryParam = coerce (toQueryParam :: a -> Text)
604+ toEncodedUrlPiece = coerce (toEncodedUrlPiece :: a -> BS.Builder)
605+
575-- |
606-- |
576-- >>> parseUrlPiece "_" :: Either Text ()
607-- >>> parseUrlPiece "_" :: Either Text ()
577-- Right ()
608-- Right ()
@@ -657,14 +688,19 @@ instance FromHttpApiData UTCTime where parseUrlPiece = runAtto Atto.utcTime
657
688
658instance FromHttpApiData NominalDiffTime where parseUrlPiece = fmap secsToNominalDiffTime . parseUrlPiece
689instance FromHttpApiData NominalDiffTime where parseUrlPiece = fmap secsToNominalDiffTime . parseUrlPiece
659
690
660−instance FromHttpApiData All where parseUrlPiece = fmap All . parseUrlPiece
691+instance FromHttpApiData All where parseUrlPiece = coerce (parseUrlPiece :: Text -> Either Text Bool)
661−instance FromHttpApiData Any where parseUrlPiece = fmap Any . parseUrlPiece
692+instance FromHttpApiData Any where parseUrlPiece = coerce (parseUrlPiece :: Text -> Either Text Bool)
693+
694+instance FromHttpApiData a => FromHttpApiData (Dual a) where parseUrlPiece = coerce (parseUrlPiece :: Text -> Either Text a)
695+instance FromHttpApiData a => FromHttpApiData (Sum a) where parseUrlPiece = coerce (parseUrlPiece :: Text -> Either Text a)
696+instance FromHttpApiData a => FromHttpApiData (Product a) where parseUrlPiece = coerce (parseUrlPiece :: Text -> Either Text a)
697+instance FromHttpApiData a => FromHttpApiData (First a) where parseUrlPiece = coerce (parseUrlPiece :: Text -> Either Text (Maybe a))
698+instance FromHttpApiData a => FromHttpApiData (Last a) where parseUrlPiece = coerce (parseUrlPiece :: Text -> Either Text (Maybe a))
662
699
663−instance FromHttpApiData a => FromHttpApiData (Dual a) where parseUrlPiece = fmap Dual . parseUrlPiece
700+instance FromHttpApiData a => FromHttpApiData (Semi.Min a) where parseUrlPiece = coerce (parseUrlPiece :: Text -> Either Text a)
664−instance FromHttpApiData a => FromHttpApiData (Sum a) where parseUrlPiece = fmap Sum . parseUrlPiece
665−instance FromHttpApiData a => FromHttpApiData (Product a) where parseUrlPiece = fmap Product . parseUrlPiece
701+instance FromHttpApiData a => FromHttpApiData (Semi.Max a) where parseUrlPiece = coerce (parseUrlPiece :: Text -> Either Text a)
666−instance FromHttpApiData a => FromHttpApiData (First a) where parseUrlPiece = fmap First . parseUrlPiece
702+instance FromHttpApiData a => FromHttpApiData (Semi.First a) where parseUrlPiece = coerce (parseUrlPiece :: Text -> Either Text a)
667−instance FromHttpApiData a => FromHttpApiData (Last a) where parseUrlPiece = fmap Last . parseUrlPiece
703+instance FromHttpApiData a => FromHttpApiData (Semi.Last a) where parseUrlPiece = coerce (parseUrlPiece :: Text -> Either Text a)
668
704
669-- |
705-- |
670-- >>> parseUrlPiece "Just 123" :: Either Text (Maybe Int)
706-- >>> parseUrlPiece "Just 123" :: Either Text (Maybe Int)
@@ -715,6 +751,11 @@ instance FromHttpApiData SetCookie where
715 parseUrlPiece = parseHeader . encodeUtf8
751 parseUrlPiece = parseHeader . encodeUtf8
716 parseHeader = Right . parseSetCookie
752 parseHeader = Right . parseSetCookie
717
753
754+instance FromHttpApiData a => FromHttpApiData (Tagged b a) where
755+ parseUrlPiece = coerce (parseUrlPiece :: Text -> Either Text a)
756+ parseHeader = coerce (parseHeader :: ByteString -> Either Text a)
757+ parseQueryParam = coerce (parseQueryParam :: Text -> Either Text a)
758+
718-------------------------------------------------------------------------------
759-------------------------------------------------------------------------------
719-- Attoparsec helpers
760-- Attoparsec helpers
720-------------------------------------------------------------------------------
761-------------------------------------------------------------------------------
@@ -723,3 +764,5 @@ runAtto :: Atto.Parser a -> Text -> Either Text a
723runAtto p t = case Atto.parseOnly (p <* Atto.endOfInput) t of
764runAtto p t = case Atto.parseOnly (p <* Atto.endOfInput) t of
724 Left err -> Left (T.pack err)
765 Left err -> Left (T.pack err)
725 Right x -> Right x
766 Right x -> Right x
767+
768+
test/Web/Internal/HttpApiDataSpec.hs+30−26
@@ -1,32 +1,36 @@
1{-# LANGUAGE ScopedTypeVariables #-}
1{-# LANGUAGE ScopedTypeVariables #-}
2module Web.Internal.HttpApiDataSpec (spec) where
2module Web.Internal.HttpApiDataSpec (spec) where
3
3
4−import Control.Applicative
4+
5+import Prelude ()
6+import Prelude.Compat
7+
8+import qualified Data.ByteString as BS
9+import Data.ByteString.Builder (toLazyByteString)
10+import Data.Char
5−import qualified Data.Fixed as F
11+import qualified Data.Fixed as F
6−import Data.Int
12+import Data.Int
7−import Data.Char
8−import Data.Word
9−import Data.Time
10−import qualified Data.Text as T
13+import qualified Data.Text as T
11−import qualified Data.Text.Encoding as TE
14+import qualified Data.Text.Encoding as TE
12−import qualified Data.Text.Lazy as L
15+import qualified Data.Text.Lazy as L
13−import qualified Data.ByteString as BS
16+import Data.Time
14−import Data.ByteString.Builder (toLazyByteString)
17+import qualified Data.UUID.Types as UUID
15−import Data.Version
18+import Data.Version
16−import qualified Data.UUID.Types as UUID
19+import Data.Word
17−import Web.Cookie (SetCookie, defaultSetCookie, setCookieName, setCookieValue)
20+import Web.Cookie (SetCookie, defaultSetCookie,
18−
21+ setCookieName, setCookieValue)
22+
19−import Data.Proxy
23+import Data.Proxy
20−
24+
21−import Numeric.Natural
25+import Numeric.Natural
22−
26+
23−import Test.Hspec
27+import Test.Hspec
24−import Test.Hspec.QuickCheck(prop)
28+import Test.Hspec.QuickCheck (prop)
25−import Test.QuickCheck
29+import Test.QuickCheck
26−
30+
27−import Web.Internal.HttpApiData
31+import Web.Internal.HttpApiData
28−
32+
29−import Web.Internal.TestInstances
33+import Web.Internal.TestInstances
30
34
31(<=>) :: forall a b. (Show a, Show b, Eq a) => (a -> b) -> (b -> Either T.Text a) -> a -> Property
35(<=>) :: forall a b. (Show a, Show b, Eq a) => (a -> b) -> (b -> Either T.Text a) -> a -> Property
32(f <=> g) x = counterexample
36(f <=> g) x = counterexample