on d34cede help sign in
.gitignore−4
since splitsnapshots
unifiedsplit
@@ -5,7 +5,6 @@ dist-newstyle
5cabal.sandbox.config
5cabal.sandbox.config
6.stack-work/
6.stack-work/
7.stack-work-bench/
7.stack-work-bench/
8−*.yaml.lock
9
8
10*.o
9*.o
11*.hi
10*.hi
@@ -14,9 +13,6 @@ cabal.sandbox.config
14.\#*
13.\#*
15\#*
14\#*
16
15
17−# Emacs backup files
18−*~
19−
20benchmarks/AesonEncode
16benchmarks/AesonEncode
21
17
22tests/qc
18tests/qc
.hlint.yamldeleted−11
@@ -1,11 +0,0 @@
1−# Ignore some builtin hints
2−- ignore:
3− name: "Reduce duplication"
4− within:
5− - Data.Aeson.Types.ToJSON
6−- ignore:
7− name: "Eta reduce"
8−- ignore:
9− name: "Use newtype instead of data"
10− within:
11− - Data.Aeson.Types.Internal
.travis.yml+21−42
@@ -12,58 +12,37 @@ cache:
12 - $HOME/.stack
12 - $HOME/.stack
13 - $HOME/.cabsnap
13 - $HOME/.cabsnap
14 - $HOME/.cabal/packages
14 - $HOME/.cabal/packages
15− - $HOME/.cabal/store
16
15
17before_cache:
16before_cache:
18 - rm -fv $HOME/.cabal/packages/hackage.haskell.org/build-reports.log
17 - rm -fv $HOME/.cabal/packages/hackage.haskell.org/build-reports.log
19− - rm -fv $HOME/.cabal/packages/hackage.haskell.org/00-index.*
18+ - rm -fv $HOME/.cabal/packages/hackage.haskell.org/00-index.tar
20− - rm -fv $HOME/.cabal/packages/hackage.haskell.org/*.json
21− - rm -fv $HOME/.cabal/packages/hackage.haskell.org/01-index.cache
22− - rm -fv $HOME/.cabal/packages/hackage.haskell.org/01-index.tar
23− - rm -fv $HOME/.cabal/packages/hackage.haskell.org/01-index.tar.idx
24
19
25matrix:
20matrix:
26 include:
21 include:
27−
28− - env: BUILD=hlint CABALVER=2.2 GHCVER=8.4.4
22+ - env: BUILD=hlint CABALVER=1.24 GHCVER=8.0.2
29− compiler: ": #GHC 8.4.4"
30− addons: {apt: {packages: [cabal-install-2.2,ghc-8.4.4], sources: [hvr-ghc]}}
31−
32− - env: BUILD=stack CABALVER=2.4 GHCVER=8.6.5 STACK_YAML=stack-bench.yaml
33− compiler: ": #GHC 8.6.5"
34− addons: {apt: {packages: [cabal-install-2.4,ghc-8.6.5], sources: [hvr-ghc]}}
35−
36− - env: BUILD=stack CABALVER=2.2 GHCVER=8.6.5 STACK_YAML=stack-ffi-unescape.yaml
37− compiler: ": #GHC 8.6.5"
38− addons: {apt: {packages: [cabal-install-2.2,ghc-8.6.5], sources: [hvr-ghc]}}
39−
40− - env: BUILD=cabal2 CABALVER=3.0 GHCVER=8.8.1
41− compiler: ": #GHC 8.8.1"
42− addons: {apt: {packages: [cabal-install-3.0,ghc-8.8.1], sources: [hvr-ghc]}}
43−
44− - env: BUILD=cabal CABALVER=2.4 GHCVER=8.6.5
45− compiler: ": #GHC 8.6.5"
46− addons: {apt: {packages: [cabal-install-2.4,ghc-8.6.5], sources: [hvr-ghc]}}
47−
48− - env: BUILD=cabal CABALVER=2.2 GHCVER=8.4.4
49− compiler: ": #GHC 8.4.4"
50− addons: {apt: {packages: [cabal-install-2.2,ghc-8.4.4], sources: [hvr-ghc]}}
51−
52− - env: BUILD=cabal CABALVER=2.0 GHCVER=8.2.2
53− compiler: ": #GHC 8.2.2"
54− addons: {apt: {packages: [cabal-install-2.0,ghc-8.2.2], sources: [hvr-ghc]}}
55−
56− - env: BUILD=cabal CABALVER=2.0 GHCVER=8.0.2
57 compiler: ": #GHC 8.0.2"
23 compiler: ": #GHC 8.0.2"
58− addons: {apt: {packages: [cabal-install-2.0,ghc-8.0.2], sources: [hvr-ghc]}}
24+ addons: {apt: {packages: [cabal-install-1.24,ghc-8.0.2], sources: [hvr-ghc]}}
59−
25+ - env: BUILD=stack CABALVER=1.24 GHCVER=8.0.2 STACK_YAML=stack-bench.yaml
26+ compiler: ": #GHC 8.0.2"
27+ addons: {apt: {packages: [cabal-install-1.24,ghc-8.0.2], sources: [hvr-ghc]}}
28+ - env: BUILD=stack CABALVER=1.24 GHCVER=8.0.2 STACK_YAML=stack-pure-unescape.yaml
29+ compiler: ": #GHC 8.0.2"
30+ addons: {apt: {packages: [cabal-install-1.24,ghc-8.0.2], sources: [hvr-ghc]}}
31+ - env: BUILD=cabal CABALVER=1.24 GHCVER=8.0.2
32+ compiler: ": #GHC 8.0.2"
33+ addons: {apt: {packages: [cabal-install-1.24,ghc-8.0.2], sources: [hvr-ghc]}}
34+ - env: BUILD=cabal CABALVER=1.18 GHCVER=7.4.2
35+ compiler: ": #GHC 7.4.2"
36+ addons: {apt: {packages: [cabal-install-1.18,ghc-7.4.2], sources: [hvr-ghc]}}
60− - env: BUILD=cabal CABALVER=1.22 GHCVER=7.10.3
37+ - env: BUILD=cabal CABALVER=1.18 GHCVER=7.6.3
61− compiler: ": #GHC 7.10.3"
38+ compiler: ": #GHC 7.6.3"
62− addons: {apt: {packages: [cabal-install-1.22,ghc-7.10.3], sources: [hvr-ghc]}}
39+ addons: {apt: {packages: [cabal-install-1.18,ghc-7.6.3], sources: [hvr-ghc]}}
63−
64 - env: BUILD=cabal CABALVER=1.18 GHCVER=7.8.4
40 - env: BUILD=cabal CABALVER=1.18 GHCVER=7.8.4
65 compiler: ": #GHC 7.8.4"
41 compiler: ": #GHC 7.8.4"
66 addons: {apt: {packages: [cabal-install-1.18,ghc-7.8.4], sources: [hvr-ghc]}}
42 addons: {apt: {packages: [cabal-install-1.18,ghc-7.8.4], sources: [hvr-ghc]}}
43+ - env: BUILD=cabal CABALVER=1.22 GHCVER=7.10.3
44+ compiler: ": #GHC 7.10.3"
45+ addons: {apt: {packages: [cabal-install-1.22,ghc-7.10.3], sources: [hvr-ghc]}}
67
46
68before_install:
47before_install:
69 - unset CC
48 - unset CC
CONTRIBUTING.md+1−39
@@ -8,7 +8,7 @@ Changes that affect users of aeson should be mentioned in `changelog.md`. It sho
8 * When it should be used
8 * When it should be used
9 * What it supersedes
9 * What it supersedes
10
10
11−Add this entry under an `## Upcoming` header above existing releases.
11+Add this entry under an `# Upcoming` header above existing releases.
12
12
13The exact format of entries is not important as we'll go through everything before a release is made.
13The exact format of entries is not important as we'll go through everything before a release is made.
14
14
@@ -58,41 +58,3 @@ import Foo as F
58import Qux (Q(..), q)
58import Qux (Q(..), q)
59import qualified A
59import qualified A
60```
60```
61−
62−## Development Workflow (If you plan on using Stack)
63−
64−Here we outline a general workflow new contributors could adopt for a nice incremental development experience.
65−
66−The root folder consists of two stack-*.yaml config files.
67−
68−1. stack-nightly.yaml - Config file defines all the settings to build and test any changes.
69−2. stack-bench.yaml - Used to run benchmark tests.
70−3. stack-ffi-unescape.yaml - This is for replacing certain parts with a C-implementation.
71−
72−Therefore, a ghci development experience would be:
73−
74−1. `stack ghci --stack-yaml stack-nightly.yaml --test`
75−2. :r to recompile
76−3. `main` to run all tests, or `:main --pattern Foo` to run specific tests matched by pattern.
77−
78−Some discussion on this topic can also be found here [here](https://github.com/bos/aeson/issues/733)
79−
80−Of course before submitting a PR, the following steps are recommended:
81−
82−1. 'stack test --stack-yaml stack-nightly.yaml` - Run the entire suite of tests
83−2. `make hlint` - Run hlint on the source folders.
84−3. 'stack test --stack-yaml stack-bench.yaml` - Run the benchmark tests if you believe your changes could affect the benchmarks.
85−
86−## Development Workflow (If you plan on using Cabal)
87−
88−A ghci development experience would be:
89−
90−1. `cabal new-repl test:tests`
91−2. `:r` to recompile
92−3. `Main.main` to run all tests, or `:m Main; :main --pattern Foo` to run specific tests matched by pattern.
93−
94−Of course before submitting a PR, the following steps are recommended:
95−
96−1. `cabal new-test` - Run the entire suite of tests
97−2. `make hlint` - Run hlint on the source folders.
98−3. Uncomment the `benchmarks` line in `cabal.project` - Run the benchmark tests if you believe your changes could affect the benchmarks.
Data/Aeson.hs+5−77
@@ -1,4 +1,3 @@
1−{-# LANGUAGE NoImplicitPrelude #-}
2-- |
1-- |
3-- Module: Data.Aeson
2-- Module: Data.Aeson
4-- Copyright: (c) 2011-2016 Bryan O'Sullivan
3-- Copyright: (c) 2011-2016 Bryan O'Sullivan
@@ -39,16 +38,11 @@ module Data.Aeson
39 , eitherDecode
38 , eitherDecode
40 , eitherDecode'
39 , eitherDecode'
41 , encode
40 , encode
42− , encodeFile
43 -- ** Variants for strict bytestrings
41 -- ** Variants for strict bytestrings
44 , decodeStrict
42 , decodeStrict
45− , decodeFileStrict
46 , decodeStrict'
43 , decodeStrict'
47− , decodeFileStrict'
48 , eitherDecodeStrict
44 , eitherDecodeStrict
49− , eitherDecodeFileStrict
50 , eitherDecodeStrict'
45 , eitherDecodeStrict'
51− , eitherDecodeFileStrict'
52 -- * Core JSON types
46 -- * Core JSON types
53 , Value(..)
47 , Value(..)
54 , Encoding
48 , Encoding
@@ -63,18 +57,11 @@ module Data.Aeson
63 , fromJSON
57 , fromJSON
64 , ToJSON(..)
58 , ToJSON(..)
65 , KeyValue(..)
59 , KeyValue(..)
66− , (<?>)
67− , JSONPath
68 -- ** Keys for maps
60 -- ** Keys for maps
69 , ToJSONKey(..)
61 , ToJSONKey(..)
70 , ToJSONKeyFunction(..)
62 , ToJSONKeyFunction(..)
71 , FromJSONKey(..)
63 , FromJSONKey(..)
72 , FromJSONKeyFunction(..)
64 , FromJSONKeyFunction(..)
73− -- *** Generic keys
74− , GToJSONKey()
75− , genericToJSONKey
76− , GFromJSONKey()
77− , genericFromJSONKey
78 -- ** Liftings to unary and binary type constructors
65 -- ** Liftings to unary and binary type constructors
79 , FromJSON1(..)
66 , FromJSON1(..)
80 , parseJSON1
67 , parseJSON1
@@ -100,34 +87,15 @@ module Data.Aeson
100 , genericLiftToEncoding
87 , genericLiftToEncoding
101 , genericParseJSON
88 , genericParseJSON
102 , genericLiftParseJSON
89 , genericLiftParseJSON
103− -- ** Generic and TH encoding configuration
104− , Options
105 , defaultOptions
90 , defaultOptions
106− -- *** Options fields
107− -- $optionsFields
108− , fieldLabelModifier
109− , constructorTagModifier
110− , allNullaryToStringTag
111− , omitNothingFields
112− , sumEncoding
113− , unwrapUnaryRecords
114− , tagSingleConstructors
115− -- *** Options utilities
116− , SumEncoding(..)
117− , camelTo2
118− , defaultTaggedObject
119− -- ** Options for object keys
120− , JSONKeyOptions
121− , keyModifier
122− , defaultJSONKeyOptions
123
91
124 -- * Inspecting @'Value's@
92 -- * Inspecting @'Value's@
125 , withObject
93 , withObject
126 , withText
94 , withText
127 , withArray
95 , withArray
96+ , withNumber
128 , withScientific
97 , withScientific
129 , withBool
98 , withBool
130− , withEmbeddedJSON
131 -- * Constructors and accessors
99 -- * Constructors and accessors
132 , Series
100 , Series
133 , pairs
101 , pairs
@@ -140,16 +108,16 @@ module Data.Aeson
140 -- * Parsing
108 -- * Parsing
141 , json
109 , json
142 , json'
110 , json'
143− , parseIndexedJSON
144 ) where
111 ) where
145
112
113+import Prelude ()
146import Prelude.Compat
114import Prelude.Compat
147
115
148−import Data.Aeson.Types.FromJSON (ifromJSON, parseIndexedJSON)
116+import Data.Aeson.Types.FromJSON (ifromJSON)
149import Data.Aeson.Encoding (encodingToLazyByteString)
117import Data.Aeson.Encoding (encodingToLazyByteString)
150import Data.Aeson.Parser.Internal (decodeWith, decodeStrictWith, eitherDecodeWith, eitherDecodeStrictWith, jsonEOF, json, jsonEOF', json')
118import Data.Aeson.Parser.Internal (decodeWith, decodeStrictWith, eitherDecodeWith, eitherDecodeStrictWith, jsonEOF, json, jsonEOF', json')
151import Data.Aeson.Types
119import Data.Aeson.Types
152−import Data.Aeson.Types.Internal (JSONPath, formatError, (<?>))
120+import Data.Aeson.Types.Internal (JSONPath, formatError)
153import qualified Data.ByteString as B
121import qualified Data.ByteString as B
154import qualified Data.ByteString.Lazy as L
122import qualified Data.ByteString.Lazy as L
155
123
@@ -159,10 +127,6 @@ import qualified Data.ByteString.Lazy as L
159encode :: (ToJSON a) => a -> L.ByteString
127encode :: (ToJSON a) => a -> L.ByteString
160encode = encodingToLazyByteString . toEncoding
128encode = encodingToLazyByteString . toEncoding
161
129
162−-- | Efficiently serialize a JSON value as a lazy 'L.ByteString' and write it to a file.
163−encodeFile :: (ToJSON a) => FilePath -> a -> IO ()
164−encodeFile fp = L.writeFile fp . encode
165−
166-- | Efficiently deserialize a JSON value from a lazy 'L.ByteString'.
130-- | Efficiently deserialize a JSON value from a lazy 'L.ByteString'.
167-- If this fails due to incomplete or invalid input, 'Nothing' is
131-- If this fails due to incomplete or invalid input, 'Nothing' is
168-- returned.
132-- returned.
@@ -189,18 +153,6 @@ decodeStrict :: (FromJSON a) => B.ByteString -> Maybe a
189decodeStrict = decodeStrictWith jsonEOF fromJSON
153decodeStrict = decodeStrictWith jsonEOF fromJSON
190{-# INLINE decodeStrict #-}
154{-# INLINE decodeStrict #-}
191
155
192−-- | Efficiently deserialize a JSON value from a file.
193−-- If this fails due to incomplete or invalid input, 'Nothing' is
194−-- returned.
195−--
196−-- The input file's content must consist solely of a JSON document,
197−-- with no trailing data except for whitespace.
198−--
199−-- This function parses immediately, but defers conversion. See
200−-- 'json' for details.
201−decodeFileStrict :: (FromJSON a) => FilePath -> IO (Maybe a)
202−decodeFileStrict = fmap decodeStrict . B.readFile
203−
204-- | Efficiently deserialize a JSON value from a lazy 'L.ByteString'.
156-- | Efficiently deserialize a JSON value from a lazy 'L.ByteString'.
205-- If this fails due to incomplete or invalid input, 'Nothing' is
157-- If this fails due to incomplete or invalid input, 'Nothing' is
206-- returned.
158-- returned.
@@ -227,18 +179,6 @@ decodeStrict' :: (FromJSON a) => B.ByteString -> Maybe a
227decodeStrict' = decodeStrictWith jsonEOF' fromJSON
179decodeStrict' = decodeStrictWith jsonEOF' fromJSON
228{-# INLINE decodeStrict' #-}
180{-# INLINE decodeStrict' #-}
229
181
230−-- | Efficiently deserialize a JSON value from a file.
231−-- If this fails due to incomplete or invalid input, 'Nothing' is
232−-- returned.
233−--
234−-- The input file's content must consist solely of a JSON document,
235−-- with no trailing data except for whitespace.
236−--
237−-- This function parses and performs conversion immediately. See
238−-- 'json'' for details.
239−decodeFileStrict' :: (FromJSON a) => FilePath -> IO (Maybe a)
240−decodeFileStrict' = fmap decodeStrict' . B.readFile
241−
242eitherFormatError :: Either (JSONPath, String) a -> Either String a
182eitherFormatError :: Either (JSONPath, String) a -> Either String a
243eitherFormatError = either (Left . uncurry formatError) Right
183eitherFormatError = either (Left . uncurry formatError) Right
244{-# INLINE eitherFormatError #-}
184{-# INLINE eitherFormatError #-}
@@ -254,12 +194,6 @@ eitherDecodeStrict =
254 eitherFormatError . eitherDecodeStrictWith jsonEOF ifromJSON
194 eitherFormatError . eitherDecodeStrictWith jsonEOF ifromJSON
255{-# INLINE eitherDecodeStrict #-}
195{-# INLINE eitherDecodeStrict #-}
256
196
257−-- | Like 'decodeFileStrict' but returns an error message when decoding fails.
258−eitherDecodeFileStrict :: (FromJSON a) => FilePath -> IO (Either String a)
259−eitherDecodeFileStrict =
260− fmap (eitherFormatError . eitherDecodeStrictWith jsonEOF ifromJSON) . B.readFile
261−{-# INLINE eitherDecodeFileStrict #-}
262−
263-- | Like 'decode'' but returns an error message when decoding fails.
197-- | Like 'decode'' but returns an error message when decoding fails.
264eitherDecode' :: (FromJSON a) => L.ByteString -> Either String a
198eitherDecode' :: (FromJSON a) => L.ByteString -> Either String a
265eitherDecode' = eitherFormatError . eitherDecodeWith jsonEOF' ifromJSON
199eitherDecode' = eitherFormatError . eitherDecodeWith jsonEOF' ifromJSON
@@ -271,12 +205,6 @@ eitherDecodeStrict' =
271 eitherFormatError . eitherDecodeStrictWith jsonEOF' ifromJSON
205 eitherFormatError . eitherDecodeStrictWith jsonEOF' ifromJSON
272{-# INLINE eitherDecodeStrict' #-}
206{-# INLINE eitherDecodeStrict' #-}
273
207
274−-- | Like 'decodeFileStrict'' but returns an error message when decoding fails.
275−eitherDecodeFileStrict' :: (FromJSON a) => FilePath -> IO (Either String a)
276−eitherDecodeFileStrict' =
277− fmap (eitherFormatError . eitherDecodeStrictWith jsonEOF' ifromJSON) . B.readFile
278−{-# INLINE eitherDecodeFileStrict' #-}
279−
280-- $use
208-- $use
281--
209--
282-- This section contains basic information on the different ways to
210-- This section contains basic information on the different ways to
@@ -438,7 +366,7 @@ eitherDecodeFileStrict' =
438-- The 'Object' type contains JSON objects:
366-- The 'Object' type contains JSON objects:
439--
367--
440-- > λ> decode "{\"name\":\"Dave\",\"age\":2}" :: Maybe Object
368-- > λ> decode "{\"name\":\"Dave\",\"age\":2}" :: Maybe Object
441−-- > Just (fromList [("name",String "Dave"),("age",Number 2)])
369+-- > Just (fromList) [("name",String "Dave"),("age",Number 2)]
442--
370--
443-- You can extract values from it with a parser using 'parse',
371-- You can extract values from it with a parser using 'parse',
444-- 'parseEither' or, in this example, 'parseMaybe':
372-- 'parseEither' or, in this example, 'parseMaybe':
Data/Aeson/Encode.hs+1−2
@@ -1,4 +1,3 @@
1−{-# LANGUAGE NoImplicitPrelude #-}
2-- |
1-- |
3-- Module: Data.Aeson.Encode
2-- Module: Data.Aeson.Encode
4-- Copyright: (c) 2012-2016 Bryan O'Sullivan
3-- Copyright: (c) 2012-2016 Bryan O'Sullivan
@@ -9,13 +8,13 @@
9-- Portability: portable
8-- Portability: portable
10--
9--
11-- This module is left to supply limited backwards-compatibility.
10-- This module is left to supply limited backwards-compatibility.
12−
13module Data.Aeson.Encode {-# DEPRECATED "Use Data.Aeson or Data.Aeson.Text instead" #-}
11module Data.Aeson.Encode {-# DEPRECATED "Use Data.Aeson or Data.Aeson.Text instead" #-}
14 (
12 (
15 encode
13 encode
16 , encodeToTextBuilder
14 , encodeToTextBuilder
17 ) where
15 ) where
18
16
17+import Prelude ()
19
18
20import Data.ByteString.Lazy (ByteString)
19import Data.ByteString.Lazy (ByteString)
21import Data.Text.Lazy.Builder (Builder)
20import Data.Text.Lazy.Builder (Builder)
Data/Aeson/Encoding.hs+1−2
@@ -1,10 +1,8 @@
1−{-# LANGUAGE NoImplicitPrelude #-}
2-- |
1-- |
3--
2--
4-- Functions in this module return well-formed 'Encoding''.
3-- Functions in this module return well-formed 'Encoding''.
5-- Polymorphic variants, which return @'Encoding' a@, return a textual JSON
4-- Polymorphic variants, which return @'Encoding' a@, return a textual JSON
6-- value, so it can be used as both @'Encoding'' 'Text'@ and @'Encoding' = 'Encoding'' 'Value'@.
5-- value, so it can be used as both @'Encoding'' 'Text'@ and @'Encoding' = 'Encoding'' 'Value'@.
7−
8module Data.Aeson.Encoding
6module Data.Aeson.Encoding
9 (
7 (
10 -- * Encoding
8 -- * Encoding
@@ -51,5 +49,6 @@ module Data.Aeson.Encoding
51 , value
49 , value
52 ) where
50 ) where
53
51
52+import Prelude ()
54
53
55import Data.Aeson.Encoding.Internal
54import Data.Aeson.Encoding.Internal
Data/Aeson/Encoding/Builder.hs+79−6
@@ -1,6 +1,6 @@
1{-# LANGUAGE BangPatterns #-}
1{-# LANGUAGE BangPatterns #-}
2−{-# LANGUAGE NoImplicitPrelude #-}
2+{-# LANGUAGE CPP #-}
3−{-# LANGUAGE TupleSections #-}
3+
4-- |
4-- |
5-- Module: Data.Aeson.Encoding.Builder
5-- Module: Data.Aeson.Encoding.Builder
6-- Copyright: (c) 2011 MailRank, Inc.
6-- Copyright: (c) 2011 MailRank, Inc.
@@ -36,6 +36,7 @@ module Data.Aeson.Encoding.Builder
36 , ascii5
36 , ascii5
37 ) where
37 ) where
38
38
39+import Prelude ()
39import Prelude.Compat
40import Prelude.Compat
40
41
41import Data.Aeson.Internal.Time
42import Data.Aeson.Internal.Time
@@ -44,9 +45,8 @@ import Data.ByteString.Builder as B
44import Data.ByteString.Builder.Prim as BP
45import Data.ByteString.Builder.Prim as BP
45import Data.ByteString.Builder.Scientific (scientificBuilder)
46import Data.ByteString.Builder.Scientific (scientificBuilder)
46import Data.Char (chr, ord)
47import Data.Char (chr, ord)
47−import Data.Semigroup ((<>))
48+import Data.Monoid ((<>))
48import Data.Scientific (Scientific, base10Exponent, coefficient)
49import Data.Scientific (Scientific, base10Exponent, coefficient)
49−import Data.Text.Encoding (encodeUtf8BuilderEscaped)
50import Data.Time (UTCTime(..))
50import Data.Time (UTCTime(..))
51import Data.Time.Calendar (Day(..), toGregorian)
51import Data.Time.Calendar (Day(..), toGregorian)
52import Data.Time.LocalTime
52import Data.Time.LocalTime
@@ -55,6 +55,20 @@ import qualified Data.HashMap.Strict as HMS
55import qualified Data.Text as T
55import qualified Data.Text as T
56import qualified Data.Vector as V
56import qualified Data.Vector as V
57
57
58+#if MIN_VERSION_bytestring(0,10,4)
59+import Data.Text.Encoding (encodeUtf8BuilderEscaped)
60+#else
61+import Data.Bits ((.&.))
62+import Data.Text.Internal (Text(..))
63+import Data.Text.Internal.Unsafe.Shift (shiftR)
64+import Foreign.Ptr (minusPtr, plusPtr)
65+import Foreign.Storable (poke)
66+import qualified Data.ByteString.Builder.Internal as B
67+import qualified Data.ByteString.Builder.Prim.Internal as BP
68+import qualified Data.Text.Array as A
69+import qualified Data.Text.Internal.Encoding.Utf16 as U16
70+#endif
71+
58-- | Encode a JSON value to a "Data.ByteString" 'B.Builder'.
72-- | Encode a JSON value to a "Data.ByteString" 'B.Builder'.
59--
73--
60-- Use this function if you are encoding over the wire, or need to
74-- Use this function if you are encoding over the wire, or need to
@@ -133,7 +147,7 @@ c2w c = fromIntegral (ord c)
133-- | Encode a JSON number.
147-- | Encode a JSON number.
134scientific :: Scientific -> Builder
148scientific :: Scientific -> Builder
135scientific s
149scientific s
136− | e < 0 || e > 1024 = scientificBuilder s
150+ | e < 0 = scientificBuilder s
137 | otherwise = B.integerDec (coefficient s * 10 ^ e)
151 | otherwise = B.integerDec (coefficient s * 10 ^ e)
138 where
152 where
139 e = base10Exponent s
153 e = base10Exponent s
@@ -201,7 +215,7 @@ timeOfDay64 (TOD h m s)
201 !(T mh ml) = twoDigits m
215 !(T mh ml) = twoDigits m
202 !(T sh sl) = twoDigits (fromIntegral real)
216 !(T sh sl) = twoDigits (fromIntegral real)
203 (real,frac) = s `quotRem` pico
217 (real,frac) = s `quotRem` pico
204− showFrac = ('.',) >$< (BP.liftFixedToBounded BP.char7 >*< trunc12)
218+ showFrac = (\x -> ('.', x)) >$< (BP.liftFixedToBounded BP.char7 >*< trunc12)
205 trunc12 = (`quotRem` micro) >$<
219 trunc12 = (`quotRem` micro) >$<
206 BP.condB (\(_,y) -> y == 0) (fst >$< trunc6) (digits6 >*< trunc6)
220 BP.condB (\(_,y) -> y == 0) (fst >$< trunc6) (digits6 >*< trunc6)
207 digits6 = ((`quotRem` milli) . fromIntegral) >$< (digits3 >*< digits3)
221 digits6 = ((`quotRem` milli) . fromIntegral) >$< (digits3 >*< digits3)
@@ -254,3 +268,62 @@ twoDigits a = T (digit hi) (digit lo)
254
268
255digit :: Int -> Char
269digit :: Int -> Char
256digit x = chr (x + 48)
270digit x = chr (x + 48)
271+
272+#if !(MIN_VERSION_bytestring(0,10,4))
273+-- | Encode text using UTF-8 encoding and escape the ASCII characters using
274+-- a 'BP.BoundedPrim'.
275+--
276+-- Use this function is to implement efficient encoders for text-based formats
277+-- like JSON or HTML.
278+{-# INLINE encodeUtf8BuilderEscaped #-}
279+-- TODO: Extend documentation with references to source code in @blaze-html@
280+-- or @aeson@ that uses this function.
281+encodeUtf8BuilderEscaped :: BP.BoundedPrim Word8 -> Text -> B.Builder
282+encodeUtf8BuilderEscaped be =
283+ -- manual eta-expansion to ensure inlining works as expected
284+ \txt -> B.builder (mkBuildstep txt)
285+ where
286+ bound = max 4 $ BP.sizeBound be
287+
288+ mkBuildstep (Text arr off len) !k =
289+ outerLoop off
290+ where
291+ iend = off + len
292+
293+ outerLoop !i0 br@(B.BufferRange op0 ope)
294+ | i0 >= iend = k br
295+ | outRemaining > 0 = goPartial (i0 + min outRemaining inpRemaining)
296+ -- TODO: Use a loop with an integrated bound's check if outRemaining
297+ -- is smaller than 8, as this will save on divisions.
298+ | otherwise = return $ B.bufferFull bound op0 (outerLoop i0)
299+ where
300+ outRemaining = (ope `minusPtr` op0) `div` bound
301+ inpRemaining = iend - i0
302+
303+ goPartial !iendTmp = go i0 op0
304+ where
305+ go !i !op
306+ | i < iendTmp = case A.unsafeIndex arr i of
307+ w | w <= 0x7F ->
308+ BP.runB be (fromIntegral w) op >>= go (i + 1)
309+ | w <= 0x7FF -> do
310+ poke8 0 $ (w `shiftR` 6) + 0xC0
311+ poke8 1 $ (w .&. 0x3f) + 0x80
312+ go (i + 1) (op `plusPtr` 2)
313+ | 0xD800 <= w && w <= 0xDBFF -> do
314+ let c = ord $ U16.chr2 w (A.unsafeIndex arr (i+1))
315+ poke8 0 $ (c `shiftR` 18) + 0xF0
316+ poke8 1 $ ((c `shiftR` 12) .&. 0x3F) + 0x80
317+ poke8 2 $ ((c `shiftR` 6) .&. 0x3F) + 0x80
318+ poke8 3 $ (c .&. 0x3F) + 0x80
319+ go (i + 2) (op `plusPtr` 4)
320+ | otherwise -> do
321+ poke8 0 $ (w `shiftR` 12) + 0xE0
322+ poke8 1 $ ((w `shiftR` 6) .&. 0x3F) + 0x80
323+ poke8 2 $ (w .&. 0x3F) + 0x80
324+ go (i + 1) (op `plusPtr` 3)
325+ | otherwise =
326+ outerLoop i (B.BufferRange op ope)
327+ where
328+ poke8 j v = poke (op `plusPtr` j) (fromIntegral v :: Word8)
329+#endif
Data/Aeson/Encoding/Internal.hs+1−2
@@ -1,9 +1,7 @@
1{-# LANGUAGE DeriveDataTypeable #-}
1{-# LANGUAGE DeriveDataTypeable #-}
2{-# LANGUAGE EmptyDataDecls #-}
2{-# LANGUAGE EmptyDataDecls #-}
3−{-# LANGUAGE NoImplicitPrelude #-}
4{-# LANGUAGE OverloadedStrings #-}
3{-# LANGUAGE OverloadedStrings #-}
5{-# LANGUAGE RankNTypes #-}
4{-# LANGUAGE RankNTypes #-}
6−
7module Data.Aeson.Encoding.Internal
5module Data.Aeson.Encoding.Internal
8 (
6 (
9 -- * Encoding
7 -- * Encoding
@@ -57,6 +55,7 @@ module Data.Aeson.Encoding.Internal
57 , comma, colon, openBracket, closeBracket, openCurly, closeCurly
55 , comma, colon, openBracket, closeBracket, openCurly, closeCurly
58 ) where
56 ) where
59
57
58+import Prelude ()
60import Prelude.Compat
59import Prelude.Compat
61
60
62import Data.Aeson.Types.Internal (Value)
61import Data.Aeson.Types.Internal (Value)
Data/Aeson/Internal.hs+1−1
@@ -1,4 +1,3 @@
1−{-# LANGUAGE NoImplicitPrelude #-}
2-- |
1-- |
3-- Module: Data.Aeson.Internal
2-- Module: Data.Aeson.Internal
4-- Copyright: (c) 2015-2016 Bryan O'Sullivan
3-- Copyright: (c) 2015-2016 Bryan O'Sullivan
@@ -23,6 +22,7 @@ module Data.Aeson.Internal
23 , iparse
22 , iparse
24 ) where
23 ) where
25
24
25+import Prelude ()
26
26
27import Data.Aeson.Types.Internal
27import Data.Aeson.Types.Internal
28import Data.Aeson.Types.FromJSON (ifromJSON)
28import Data.Aeson.Types.FromJSON (ifromJSON)
Data/Aeson/Internal/Functions.hs+1−1
@@ -1,4 +1,3 @@
1−{-# LANGUAGE NoImplicitPrelude #-}
2-- |
1-- |
3-- Module: Data.Aeson.Functions
2-- Module: Data.Aeson.Functions
4-- Copyright: (c) 2011-2016 Bryan O'Sullivan
3-- Copyright: (c) 2011-2016 Bryan O'Sullivan
@@ -15,6 +14,7 @@ module Data.Aeson.Internal.Functions
15 , mapKey
14 , mapKey
16 ) where
15 ) where
17
16
17+import Prelude ()
18import Prelude.Compat
18import Prelude.Compat
19
19
20import Data.Hashable (Hashable)
20import Data.Hashable (Hashable)
Data/Aeson/Parser.hs+2−12
@@ -1,4 +1,3 @@
1−{-# LANGUAGE NoImplicitPrelude #-}
2-- |
1-- |
3-- Module: Data.Aeson.Parser
2-- Module: Data.Aeson.Parser
4-- Copyright: (c) 2012-2016 Bryan O'Sullivan
3-- Copyright: (c) 2012-2016 Bryan O'Sullivan
@@ -35,20 +34,10 @@ module Data.Aeson.Parser
35 , value
34 , value
36 , jstring
35 , jstring
37 , scientific
36 , scientific
38− -- ** Handling objects with duplicate keys
39− , jsonWith
40− , jsonLast
41− , jsonAccum
42− , jsonNoDup
43 -- * Strict parsers
37 -- * Strict parsers
44 -- $strict
38 -- $strict
45 , json'
39 , json'
46 , value'
40 , value'
47− -- ** Handling objects with duplicate keys
48− , jsonWith'
49− , jsonLast'
50− , jsonAccum'
51− , jsonNoDup'
52 -- * Decoding without FromJSON instances
41 -- * Decoding without FromJSON instances
53 , decodeWith
42 , decodeWith
54 , decodeStrictWith
43 , decodeStrictWith
@@ -56,8 +45,9 @@ module Data.Aeson.Parser
56 , eitherDecodeStrictWith
45 , eitherDecodeStrictWith
57 ) where
46 ) where
58
47
48+import Prelude ()
59
49
60−import Data.Aeson.Parser.Internal
50+import Data.Aeson.Parser.Internal (decodeStrictWith, decodeWith, eitherDecodeStrictWith, eitherDecodeWith, json, json', jstring, scientific, value, value')
61
51
62-- $lazy
52-- $lazy
63--
53--
Data/Aeson/Parser/Internal.hs+51−219
@@ -1,14 +1,10 @@
1{-# LANGUAGE BangPatterns #-}
1{-# LANGUAGE BangPatterns #-}
2{-# LANGUAGE CPP #-}
2{-# LANGUAGE CPP #-}
3−{-# LANGUAGE NoImplicitPrelude #-}
4{-# LANGUAGE OverloadedStrings #-}
3{-# LANGUAGE OverloadedStrings #-}
5#if MIN_VERSION_ghc_prim(0,3,1)
4#if MIN_VERSION_ghc_prim(0,3,1)
6{-# LANGUAGE MagicHash #-}
5{-# LANGUAGE MagicHash #-}
7#endif
6#endif
8−#if __GLASGOW_HASKELL__ <= 710 && __GLASGOW_HASKELL__ >= 706
7+
9−-- Work around a compiler bug
10−{-# OPTIONS_GHC -fsimpl-tick-factor=200 #-}
11−#endif
12-- |
8-- |
13-- Module: Data.Aeson.Parser.Internal
9-- Module: Data.Aeson.Parser.Internal
14-- Copyright: (c) 2011-2016 Bryan O'Sullivan
10-- Copyright: (c) 2011-2016 Bryan O'Sullivan
@@ -25,43 +21,30 @@ module Data.Aeson.Parser.Internal
25 (
21 (
26 -- * Lazy parsers
22 -- * Lazy parsers
27 json, jsonEOF
23 json, jsonEOF
28− , jsonWith
29− , jsonLast
30− , jsonAccum
31− , jsonNoDup
32 , value
24 , value
33 , jstring
25 , jstring
34 , jstring_
26 , jstring_
35 , scientific
27 , scientific
36 -- * Strict parsers
28 -- * Strict parsers
37 , json', jsonEOF'
29 , json', jsonEOF'
38− , jsonWith'
39− , jsonLast'
40− , jsonAccum'
41− , jsonNoDup'
42 , value'
30 , value'
43 -- * Helpers
31 -- * Helpers
44 , decodeWith
32 , decodeWith
45 , decodeStrictWith
33 , decodeStrictWith
46 , eitherDecodeWith
34 , eitherDecodeWith
47 , eitherDecodeStrictWith
35 , eitherDecodeStrictWith
48− -- ** Handling objects with duplicate keys
49− , fromListAccum
50− , parseListNoDup
51 ) where
36 ) where
52
37
38+import Prelude ()
53import Prelude.Compat
39import Prelude.Compat
54
40
55import Control.Applicative ((<|>))
41import Control.Applicative ((<|>))
56import Control.Monad (void, when)
42import Control.Monad (void, when)
57−import Data.Aeson.Types.Internal (IResult(..), JSONPath, Object, Result(..), Value(..))
43+import Data.Aeson.Types.Internal (IResult(..), JSONPath, Result(..), Value(..))
58import Data.Attoparsec.ByteString.Char8 (Parser, char, decimal, endOfInput, isDigit_w8, signed, string)
44import Data.Attoparsec.ByteString.Char8 (Parser, char, decimal, endOfInput, isDigit_w8, signed, string)
59−import Data.Function (fix)
60−import Data.Functor.Compat (($>))
61import Data.Scientific (Scientific)
45import Data.Scientific (Scientific)
62import Data.Text (Text)
46import Data.Text (Text)
63−import Data.Vector (Vector)
64−import qualified Data.Vector as Vector (empty, fromList, fromListN, reverse)
47+import Data.Vector as Vector (Vector, empty, fromListN, reverse)
65import qualified Data.Attoparsec.ByteString as A
48import qualified Data.Attoparsec.ByteString as A
66import qualified Data.Attoparsec.Lazy as L
49import qualified Data.Attoparsec.Lazy as L
67import qualified Data.ByteString as B
50import qualified Data.ByteString as B
@@ -93,7 +76,7 @@ import qualified Data.Text.Encoding as TE
93#define C_n 110
76#define C_n 110
94#define C_t 116
77#define C_t 116
95
78
96−-- | Parse any JSON value.
79+-- | Parse a top-level JSON value.
97--
80--
98-- The conversion of a parsed value to a Haskell value is deferred
81-- The conversion of a parsed value to a Haskell value is deferred
99-- until the Haskell value is needed. This may improve performance if
82-- until the Haskell value is needed. This may improve performance if
@@ -103,15 +86,10 @@ import qualified Data.Text.Encoding as TE
103-- This function is an alias for 'value'. In aeson 0.8 and earlier, it
86-- This function is an alias for 'value'. In aeson 0.8 and earlier, it
104-- parsed only object or array types, in conformance with the
87-- parsed only object or array types, in conformance with the
105-- now-obsolete RFC 4627.
88-- now-obsolete RFC 4627.
106−--
107−-- ==== Warning
108−--
109−-- If an object contains duplicate keys, only the first one will be kept.
110−-- For a more flexible alternative, see 'jsonWith'.
111json :: Parser Value
89json :: Parser Value
112json = value
90json = value
113
91
114−-- | Parse any JSON value.
92+-- | Parse a top-level JSON value.
115--
93--
116-- This is a strict version of 'json' which avoids building up thunks
94-- This is a strict version of 'json' which avoids building up thunks
117-- during parsing; it performs all conversions immediately. Prefer
95-- during parsing; it performs all conversions immediately. Prefer
@@ -120,35 +98,23 @@ json = value
120-- This function is an alias for 'value''. In aeson 0.8 and earlier, it
98-- This function is an alias for 'value''. In aeson 0.8 and earlier, it
121-- parsed only object or array types, in conformance with the
99-- parsed only object or array types, in conformance with the
122-- now-obsolete RFC 4627.
100-- now-obsolete RFC 4627.
123−--
124−-- ==== Warning
125−--
126−-- If an object contains duplicate keys, only the first one will be kept.
127−-- For a more flexible alternative, see 'jsonWith''.
128json' :: Parser Value
101json' :: Parser Value
129json' = value'
102json' = value'
130
103
131−-- Open recursion: object_, object_', array_, array_' are parameterized by the
132−-- toplevel Value parser to be called recursively, to keep the parameter
133−-- mkObject outside of the recursive loop for proper inlining.
134−
135−object_ :: ([(Text, Value)] -> Either String Object) -> Parser Value -> Parser Value
104+object_ :: Parser Value
136−object_ mkObject val = {-# SCC "object_" #-} Object <$> objectValues mkObject jstring val
105+object_ = {-# SCC "object_" #-} Object <$> objectValues jstring value
137−{-# INLINE object_ #-}
138
106
139−object_' :: ([(Text, Value)] -> Either String Object) -> Parser Value -> Parser Value
107+object_' :: Parser Value
140−object_' mkObject val' = {-# SCC "object_'" #-} do
108+object_' = {-# SCC "object_'" #-} do
141− !vals <- objectValues mkObject jstring' val'
109+ !vals <- objectValues jstring' value'
142 return (Object vals)
110 return (Object vals)
143 where
111 where
144 jstring' = do
112 jstring' = do
145 !s <- jstring
113 !s <- jstring
146 return s
114 return s
147−{-# INLINE object_' #-}
148
115
149−objectValues :: ([(Text, Value)] -> Either String Object)
150− -> Parser Text -> Parser Value -> Parser (H.HashMap Text Value)
116+objectValues :: Parser Text -> Parser Value -> Parser (H.HashMap Text Value)
151−objectValues mkObject str val = do
117+objectValues str val = do
152 skipSpace
118 skipSpace
153 w <- A.peekWord8'
119 w <- A.peekWord8'
154 if w == CLOSE_CURLY
120 if w == CLOSE_CURLY
@@ -158,26 +124,22 @@ objectValues mkObject str val = do
158 -- Why use acc pattern here, you may ask? because 'H.fromList' use 'unsafeInsert'
124 -- Why use acc pattern here, you may ask? because 'H.fromList' use 'unsafeInsert'
159 -- and it's much faster because it's doing in place update to the 'HashMap'!
125 -- and it's much faster because it's doing in place update to the 'HashMap'!
160 loop acc = do
126 loop acc = do
161− k <- (str A.<?> "object key") <* skipSpace <* (char ':' A.<?> "':'")
127+ k <- str <* skipSpace <* char ':'
162− v <- (val A.<?> "object value") <* skipSpace
128+ v <- val <* skipSpace
163− ch <- A.satisfy (\w -> w == COMMA || w == CLOSE_CURLY) A.<?> "',' or '}'"
129+ ch <- A.satisfy $ \w -> w == COMMA || w == CLOSE_CURLY
164 let acc' = (k, v) : acc
130 let acc' = (k, v) : acc
165 if ch == COMMA
131 if ch == COMMA
166 then skipSpace >> loop acc'
132 then skipSpace >> loop acc'
167− else case mkObject acc' of
133+ else return (H.fromList acc')
168− Left err -> fail err
169− Right obj -> pure obj
170{-# INLINE objectValues #-}
134{-# INLINE objectValues #-}
171
135
172−array_ :: Parser Value -> Parser Value
136+array_ :: Parser Value
173−array_ val = {-# SCC "array_" #-} Array <$> arrayValues val
137+array_ = {-# SCC "array_" #-} Array <$> arrayValues value
174−{-# INLINE array_ #-}
175
138
176−array_' :: Parser Value -> Parser Value
139+array_' :: Parser Value
177−array_' val = {-# SCC "array_'" #-} do
140+array_' = {-# SCC "array_'" #-} do
178− !vals <- arrayValues val
141+ !vals <- arrayValues value'
179 return (Array vals)
142 return (Array vals)
180−{-# INLINE array_' #-}
181
143
182arrayValues :: Parser Value -> Parser (Vector Value)
144arrayValues :: Parser Value -> Parser (Vector Value)
183arrayValues val = do
145arrayValues val = do
@@ -188,133 +150,57 @@ arrayValues val = do
188 else loop [] 1
150 else loop [] 1
189 where
151 where
190 loop acc !len = do
152 loop acc !len = do
191− v <- (val A.<?> "json list value") <* skipSpace
153+ v <- val <* skipSpace
192− ch <- A.satisfy (\w -> w == COMMA || w == CLOSE_SQUARE) A.<?> "',' or ']'"
154+ ch <- A.satisfy $ \w -> w == COMMA || w == CLOSE_SQUARE
193 if ch == COMMA
155 if ch == COMMA
194 then skipSpace >> loop (v:acc) (len+1)
156 then skipSpace >> loop (v:acc) (len+1)
195 else return (Vector.reverse (Vector.fromListN len (v:acc)))
157 else return (Vector.reverse (Vector.fromListN len (v:acc)))
196{-# INLINE arrayValues #-}
158{-# INLINE arrayValues #-}
197
159
198−-- | Parse any JSON value. Synonym of 'json'.
160+-- | Parse any JSON value. You should usually 'json' in preference to
199−value :: Parser Value
161+-- this function, as this function relaxes the object-or-array
200−value = jsonWith (pure . H.fromList)
162+-- requirement of RFC 4627.
201−
202−-- | Parse any JSON value.
203−--
204−-- This parser is parameterized by a function to construct an 'Object'
205−-- from a raw list of key-value pairs, where duplicates are preserved.
206−-- The pairs appear in __reverse order__ from the source.
207--
163--
208−-- ==== __Examples__
164+-- In particular, be careful in using this function if you think your
209−--
165+-- code might interoperate with Javascript. A na&#xef;ve Javascript
210−-- 'json' keeps only the first occurence of each key, using 'HashMap.Lazy.fromList'.
166+-- library that parses JSON data using @eval@ is vulnerable to attack
211−--
167+-- unless the encoded data represents an object or an array. JSON
212−-- @
168+-- implementations in other languages conform to that same restriction
213−-- 'json' = 'jsonWith' ('Right' '.' 'H.fromList')
169+-- to preserve interoperability and security.
214−-- @
170+value :: Parser Value
215−--
171+value = do
216−-- 'jsonLast' keeps the last occurence of each key, using
217−-- @'HashMap.Lazy.fromListWith' ('const' 'id')@.
218−--
219−-- @
220−-- 'jsonLast' = 'jsonWith' ('Right' '.' 'HashMap.Lazy.fromListWith' ('const' 'id'))
221−-- @
222−--
223−-- 'jsonAccum' keeps wraps all values in arrays to keep duplicates, using
224−-- 'fromListAccum'.
225−--
226−-- @
227−-- 'jsonAccum' = 'jsonWith' ('Right' . 'fromListAccum')
228−-- @
229−--
230−-- 'jsonNoDup' fails if any object contains duplicate keys, using 'parseListNoDup'.
231−--
232−-- @
233−-- 'jsonNoDup' = 'jsonWith' 'parseListNoDup'
234−-- @
235−jsonWith :: ([(Text, Value)] -> Either String Object) -> Parser Value
236−jsonWith mkObject = fix $ \value_ -> do
237 skipSpace
172 skipSpace
238 w <- A.peekWord8'
173 w <- A.peekWord8'
239 case w of
174 case w of
240 DOUBLE_QUOTE -> A.anyWord8 *> (String <$> jstring_)
175 DOUBLE_QUOTE -> A.anyWord8 *> (String <$> jstring_)
241− OPEN_CURLY -> A.anyWord8 *> object_ mkObject value_
176+ OPEN_CURLY -> A.anyWord8 *> object_
242− OPEN_SQUARE -> A.anyWord8 *> array_ value_
177+ OPEN_SQUARE -> A.anyWord8 *> array_
243− C_f -> string "false" $> Bool False
178+ C_f -> string "false" *> pure (Bool False)
244− C_t -> string "true" $> Bool True
179+ C_t -> string "true" *> pure (Bool True)
245− C_n -> string "null" $> Null
180+ C_n -> string "null" *> pure Null
246 _ | w >= 48 && w <= 57 || w == 45
181 _ | w >= 48 && w <= 57 || w == 45
247 -> Number <$> scientific
182 -> Number <$> scientific
248 | otherwise -> fail "not a valid json value"
183 | otherwise -> fail "not a valid json value"
249−{-# INLINE jsonWith #-}
250−
251−-- | Variant of 'json' which keeps only the last occurence of every key.
252−jsonLast :: Parser Value
253−jsonLast = jsonWith (Right . H.fromListWith (const id))
254
184
255−-- | Variant of 'json' wrapping all object mappings in 'Array' to preserve
256−-- key-value pairs with the same keys.
257−jsonAccum :: Parser Value
258−jsonAccum = jsonWith (Right . fromListAccum)
259−
260−-- | Variant of 'json' which fails if any object contains duplicate keys.
261−jsonNoDup :: Parser Value
262−jsonNoDup = jsonWith parseListNoDup
263−
264−-- | @'fromListAccum' kvs@ is an object mapping keys to arrays containing all
265−-- associated values from the original list @kvs@.
266−--
267−-- >>> fromListAccum [("apple", Bool True), ("apple", Bool False), ("orange", Bool False)]
268−-- fromList [("apple", [Bool False, Bool True]), ("orange", [Bool False])]
269−fromListAccum :: [(Text, Value)] -> Object
270−fromListAccum =
271− fmap (Array . Vector.fromList . ($ [])) . H.fromListWith (.) . (fmap . fmap) (:)
272−
273−-- | @'fromListNoDup' kvs@ fails if @kvs@ contains duplicate keys.
274−parseListNoDup :: [(Text, Value)] -> Either String Object
275−parseListNoDup =
276− H.traverseWithKey unwrap . H.fromListWith (\_ _ -> Nothing) . (fmap . fmap) Just
277− where
278− unwrap k Nothing = Left $ "found duplicate key: " ++ show k
279− unwrap _ (Just v) = Right v
280−
281−-- | Strict version of 'value'. Synonym of 'json''.
185+-- | Strict version of 'value'. See also 'json''.
282value' :: Parser Value
186value' :: Parser Value
283−value' = jsonWith' (pure . H.fromList)
187+value' = do
284−
285−-- | Strict version of 'jsonWith'.
286−jsonWith' :: ([(Text, Value)] -> Either String Object) -> Parser Value
287−jsonWith' mkObject = fix $ \value_ -> do
288 skipSpace
188 skipSpace
289 w <- A.peekWord8'
189 w <- A.peekWord8'
290 case w of
190 case w of
291 DOUBLE_QUOTE -> do
191 DOUBLE_QUOTE -> do
292 !s <- A.anyWord8 *> jstring_
192 !s <- A.anyWord8 *> jstring_
293 return (String s)
193 return (String s)
294− OPEN_CURLY -> A.anyWord8 *> object_' mkObject value_
194+ OPEN_CURLY -> A.anyWord8 *> object_'
295− OPEN_SQUARE -> A.anyWord8 *> array_' value_
195+ OPEN_SQUARE -> A.anyWord8 *> array_'
296− C_f -> string "false" $> Bool False
196+ C_f -> string "false" *> pure (Bool False)
297− C_t -> string "true" $> Bool True
197+ C_t -> string "true" *> pure (Bool True)
298− C_n -> string "null" $> Null
198+ C_n -> string "null" *> pure Null
299 _ | w >= 48 && w <= 57 || w == 45
199 _ | w >= 48 && w <= 57 || w == 45
300 -> do
200 -> do
301 !n <- scientific
201 !n <- scientific
302 return (Number n)
202 return (Number n)
303 | otherwise -> fail "not a valid json value"
203 | otherwise -> fail "not a valid json value"
304−{-# INLINE jsonWith' #-}
305−
306−-- | Variant of 'json'' which keeps only the last occurence of every key.
307−jsonLast' :: Parser Value
308−jsonLast' = jsonWith' (pure . H.fromListWith (const id))
309−
310−-- | Variant of 'json'' wrapping all object mappings in 'Array' to preserve
311−-- key-value pairs with the same keys.
312−jsonAccum' :: Parser Value
313−jsonAccum' = jsonWith' (pure . fromListAccum)
314−
315−-- | Variant of 'json'' which fails if any object contains duplicate keys.
316−jsonNoDup' :: Parser Value
317−jsonNoDup' = jsonWith' parseListNoDup
318
204
319-- | Parse a quoted JSON string.
205-- | Parse a quoted JSON string.
320jstring :: Parser Text
206jstring :: Parser Text
@@ -390,12 +276,7 @@ eitherDecodeWith p to s =
390 L.Done _ v -> case to v of
276 L.Done _ v -> case to v of
391 ISuccess a -> Right a
277 ISuccess a -> Right a
392 IError path msg -> Left (path, msg)
278 IError path msg -> Left (path, msg)
393− L.Fail _ ctx msg -> Left ([], buildMsg ctx msg)
279+ L.Fail _ _ msg -> Left ([], msg)
394− where
395− buildMsg :: [String] -> String -> String
396− buildMsg [] msg = msg
397− buildMsg (expectation:_) msg =
398− msg ++ ". Expecting " ++ expectation
399{-# INLINE eitherDecodeWith #-}
280{-# INLINE eitherDecodeWith #-}
400
281
401eitherDecodeStrictWith :: Parser Value -> (Value -> IResult a) -> B.ByteString
282eitherDecodeStrictWith :: Parser Value -> (Value -> IResult a) -> B.ByteString
@@ -448,11 +329,12 @@ data SP = SP !Integer {-# UNPACK #-}!Int
448
329
449decimal0 :: Parser Integer
330decimal0 :: Parser Integer
450decimal0 = do
331decimal0 = do
332+ let step a w = a * 10 + fromIntegral (w - zero)
451− let zero = 48
333+ zero = 48
452 digits <- A.takeWhile1 isDigit_w8
334 digits <- A.takeWhile1 isDigit_w8
453 if B.length digits > 1 && B.unsafeHead digits == zero
335 if B.length digits > 1 && B.unsafeHead digits == zero
454 then fail "leading zero"
336 then fail "leading zero"
455− else return (bsToInteger digits)
337+ else return (B.foldl' step 0 digits)
456
338
457-- | Parse a JSON number.
339-- | Parse a JSON number.
458scientific :: Parser Scientific
340scientific :: Parser Scientific
@@ -485,53 +367,3 @@ scientific = do
485 fmap (Sci.scientific signedCoeff . (e +)) (signed decimal)) <|>
367 fmap (Sci.scientific signedCoeff . (e +)) (signed decimal)) <|>
486 return (Sci.scientific signedCoeff e)
368 return (Sci.scientific signedCoeff e)
487{-# INLINE scientific #-}
369{-# INLINE scientific #-}
488−
489−------------------ Copy-pasted and adapted from base ------------------------
490−
491−bsToInteger :: B.ByteString -> Integer
492−bsToInteger bs
493− | l > 40 = valInteger 10 l [ fromIntegral (w - 48) | w <- B.unpack bs ]
494− | otherwise = bsToIntegerSimple bs
495− where
496− l = B.length bs
497−
498−bsToIntegerSimple :: B.ByteString -> Integer
499−bsToIntegerSimple = B.foldl' step 0 where
500− step a b = a * 10 + fromIntegral (b - 48) -- 48 = '0'
501−
502−-- A sub-quadratic algorithm for Integer. Pairs of adjacent radix b
503−-- digits are combined into a single radix b^2 digit. This process is
504−-- repeated until we are left with a single digit. This algorithm
505−-- performs well only on large inputs, so we use the simple algorithm
506−-- for smaller inputs.
507−valInteger :: Integer -> Int -> [Integer] -> Integer
508−valInteger = go
509− where
510− go :: Integer -> Int -> [Integer] -> Integer
511− go _ _ [] = 0
512− go _ _ [d] = d
513− go b l ds
514− | l > 40 = b' `seq` go b' l' (combine b ds')
515− | otherwise = valSimple b ds
516− where
517− -- ensure that we have an even number of digits
518− -- before we call combine:
519− ds' = if even l then ds else 0 : ds
520− b' = b * b
521− l' = (l + 1) `quot` 2
522−
523− combine b (d1 : d2 : ds) = d `seq` (d : combine b ds)
524− where
525− d = d1 * b + d2
526− combine _ [] = []
527− combine _ [_] = errorWithoutStackTrace "this should not happen"
528−
529−-- The following algorithm is only linear for types whose Num operations
530−-- are in constant time.
531−valSimple :: Integer -> [Integer] -> Integer
532−valSimple base = go 0
533− where
534− go r [] = r
535− go r (d : ds) = r' `seq` go r' ds
536− where
537− r' = r * base + fromIntegral d
Data/Aeson/Parser/Time.hs+1−2
@@ -1,5 +1,3 @@
1−{-# LANGUAGE NoImplicitPrelude #-}
2−
3module Data.Aeson.Parser.Time
1module Data.Aeson.Parser.Time
4 (
2 (
5 run
3 run
@@ -11,6 +9,7 @@ module Data.Aeson.Parser.Time
11 , zonedTime
9 , zonedTime
12 ) where
10 ) where
13
11
12+import Prelude ()
14import Prelude.Compat
13import Prelude.Compat
15
14
16import Data.Attoparsec.Text (Parser)
15import Data.Attoparsec.Text (Parser)
Data/Aeson/QQ/Simple.hsdeleted−25
@@ -1,25 +0,0 @@
1−-- | Like "Data.Aeson.QQ" but without interpolation.
2−module Data.Aeson.QQ.Simple (aesonQQ) where
3−
4−import Data.Aeson
5−import qualified Data.Text as T
6−import qualified Data.Text.Encoding as TE
7−import Language.Haskell.TH
8−import Language.Haskell.TH.Quote
9−import Language.Haskell.TH.Syntax (Lift (..))
10−import Prelude ()
11−import Prelude.Compat
12−
13−aesonQQ :: QuasiQuoter
14−aesonQQ = QuasiQuoter
15− { quoteExp = aesonExp
16− , quotePat = const $ error "No quotePat defined for jsonQQ"
17− , quoteType = const $ error "No quoteType defined for jsonQQ"
18− , quoteDec = const $ error "No quoteDec defined for jsonQQ"
19− }
20−
21−aesonExp :: String -> ExpQ
22−aesonExp txt =
23− case eitherDecodeStrict $ TE.encodeUtf8 $ T.pack txt of
24− Left err -> error $ "Error in aesonExp: " ++ show err
25− Right val -> lift (val :: Value)
Data/Aeson/TH.hs+363−248
@@ -85,6 +85,7 @@ $('deriveJSON' 'defaultOptions' ''(,,,))
85@
85@
86
86
87-}
87-}
88+
88module Data.Aeson.TH
89module Data.Aeson.TH
89 (
90 (
90 -- * Encoding configuration
91 -- * Encoding configuration
@@ -116,42 +117,49 @@ module Data.Aeson.TH
116 , mkLiftParseJSON2
117 , mkLiftParseJSON2
117 ) where
118 ) where
118
119
119−import Prelude.Compat
120+import Prelude ()
121+import Prelude.Compat hiding (exp)
120
122
121import Control.Applicative ((<|>))
123import Control.Applicative ((<|>))
122−import Data.Aeson (Object, (.:), FromJSON(..), FromJSON1(..), FromJSON2(..), ToJSON(..), ToJSON1(..), ToJSON2(..))
124+import Data.Aeson (Object, (.=), (.:), FromJSON(..), FromJSON1(..), FromJSON2(..), ToJSON(..), ToJSON1(..), ToJSON2(..))
123import Data.Aeson.Types (Options(..), Parser, SumEncoding(..), Value(..), defaultOptions, defaultTaggedObject)
125import Data.Aeson.Types (Options(..), Parser, SumEncoding(..), Value(..), defaultOptions, defaultTaggedObject)
124−import Data.Aeson.Types.Internal ((<?>), JSONPathElement(Key))
126+import Data.Aeson.Types.Internal ((<?>), Pair, JSONPathElement(Key))
125import Data.Aeson.Types.FromJSON (parseOptionalFieldWith)
127import Data.Aeson.Types.FromJSON (parseOptionalFieldWith)
126−import Data.Aeson.Types.ToJSON (fromPairs, pair)
127import Control.Monad (liftM2, unless, when)
128import Control.Monad (liftM2, unless, when)
128import Data.Foldable (foldr')
129import Data.Foldable (foldr')
129#if MIN_VERSION_template_haskell(2,8,0) && !MIN_VERSION_template_haskell(2,10,0)
130#if MIN_VERSION_template_haskell(2,8,0) && !MIN_VERSION_template_haskell(2,10,0)
130import Data.List (nub)
131import Data.List (nub)
131#endif
132#endif
132−import Data.List (foldl', genericLength, intercalate, partition, union)
133+import Data.List (foldl', genericLength , intercalate , intersperse, partition, union)
133import Data.List.NonEmpty ((<|), NonEmpty((:|)))
134import Data.List.NonEmpty ((<|), NonEmpty((:|)))
134import Data.Map (Map)
135import Data.Map (Map)
135import Data.Maybe (catMaybes, fromMaybe, mapMaybe)
136import Data.Maybe (catMaybes, fromMaybe, mapMaybe)
136−import qualified Data.Monoid as Monoid
137import Data.Set (Set)
137import Data.Set (Set)
138+#if MIN_VERSION_template_haskell(2,8,0)
138import Language.Haskell.TH hiding (Arity)
139import Language.Haskell.TH hiding (Arity)
140+#else
141+import Language.Haskell.TH
142+#endif
139import Language.Haskell.TH.Datatype
143import Language.Haskell.TH.Datatype
144+#if MIN_VERSION_template_haskell(2,7,0) && !(MIN_VERSION_template_haskell(2,8,0))
145+import Language.Haskell.TH.Lib (starK)
146+#endif
140#if MIN_VERSION_template_haskell(2,8,0) && !(MIN_VERSION_template_haskell(2,10,0))
147#if MIN_VERSION_template_haskell(2,8,0) && !(MIN_VERSION_template_haskell(2,10,0))
141import Language.Haskell.TH.Syntax (mkNameG_tc)
148import Language.Haskell.TH.Syntax (mkNameG_tc)
142#endif
149#endif
143import Text.Printf (printf)
150import Text.Printf (printf)
151+import qualified Data.Aeson as A
144import qualified Data.Aeson.Encoding.Internal as E
152import qualified Data.Aeson.Encoding.Internal as E
145import qualified Data.Foldable as F (all)
153import qualified Data.Foldable as F (all)
146−import qualified Data.HashMap.Strict as H (difference, fromList, keys, lookup, toList)
154+import qualified Data.HashMap.Strict as H (lookup, toList)
147import qualified Data.List.NonEmpty as NE (length, reverse)
155import qualified Data.List.NonEmpty as NE (length, reverse)
148import qualified Data.Map as M (fromList, keys, lookup , singleton, size)
156import qualified Data.Map as M (fromList, keys, lookup , singleton, size)
149−import qualified Data.Semigroup as Semigroup (Option(..))
150import qualified Data.Set as Set (empty, insert, member)
157import qualified Data.Set as Set (empty, insert, member)
151import qualified Data.Text as T (Text, pack, unpack)
158import qualified Data.Text as T (Text, pack, unpack)
152−import qualified Data.Vector as V (unsafeIndex, null, length, create, empty)
159+import qualified Data.Vector as V (unsafeIndex, null, length, create, fromList)
153import qualified Data.Vector.Mutable as VM (unsafeNew, unsafeWrite)
160import qualified Data.Vector.Mutable as VM (unsafeNew, unsafeWrite)
154
161
162+
155--------------------------------------------------------------------------------
163--------------------------------------------------------------------------------
156-- Convenience
164-- Convenience
157--------------------------------------------------------------------------------
165--------------------------------------------------------------------------------
@@ -245,8 +253,8 @@ deriveToJSONCommon :: JSONClass
245 -> Name
253 -> Name
246 -- ^ Name of the type for which to generate an instance.
254 -- ^ Name of the type for which to generate an instance.
247 -> Q [Dec]
255 -> Q [Dec]
248−deriveToJSONCommon = deriveJSONClass [ (ToJSON, \jc _ -> consToValue Value jc)
256+deriveToJSONCommon = deriveJSONClass [ (ToJSON, \jc _ -> consToValue jc)
249− , (ToEncoding, \jc _ -> consToValue Encoding jc)
257+ , (ToEncoding, \jc _ -> consToEncoding jc)
250 ]
258 ]
251
259
252-- | Generates a lambda expression which encodes the given data type or
260-- | Generates a lambda expression which encodes the given data type or
@@ -276,7 +284,7 @@ mkToJSONCommon :: JSONClass -- ^ Which class's method is being derived.
276 -> Options -- ^ Encoding options.
284 -> Options -- ^ Encoding options.
277 -> Name -- ^ Name of the encoded type.
285 -> Name -- ^ Name of the encoded type.
278 -> Q Exp
286 -> Q Exp
279−mkToJSONCommon = mkFunCommon (\jc _ -> consToValue Value jc)
287+mkToJSONCommon = mkFunCommon (\jc _ -> consToValue jc)
280
288
281-- | Generates a lambda expression which encodes the given data type or
289-- | Generates a lambda expression which encodes the given data type or
282-- data family instance constructor as a JSON string.
290-- data family instance constructor as a JSON string.
@@ -305,14 +313,12 @@ mkToEncodingCommon :: JSONClass -- ^ Which class's method is being derived.
305 -> Options -- ^ Encoding options.
313 -> Options -- ^ Encoding options.
306 -> Name -- ^ Name of the encoded type.
314 -> Name -- ^ Name of the encoded type.
307 -> Q Exp
315 -> Q Exp
308−mkToEncodingCommon = mkFunCommon (\jc _ -> consToValue Encoding jc)
316+mkToEncodingCommon = mkFunCommon (\jc _ -> consToEncoding jc)
309
317
310-- | Helper function used by both 'deriveToJSON' and 'mkToJSON'. Generates
318-- | Helper function used by both 'deriveToJSON' and 'mkToJSON'. Generates
311−-- code to generate a 'Value' or 'Encoding' of a number of constructors. All
319+-- code to generate a 'Value' of a number of constructors. All constructors
312−-- constructors must be from the same type.
320+-- must be from the same type.
313−consToValue :: ToJSONFun
314− -- ^ The method ('toJSON' or 'toEncoding') being derived.
315− -> JSONClass
321+consToValue :: JSONClass
316 -- ^ The ToJSON variant being derived.
322 -- ^ The ToJSON variant being derived.
317 -> Options
323 -> Options
318 -- ^ Encoding options.
324 -- ^ Encoding options.
@@ -322,16 +328,16 @@ consToValue :: ToJSONFun
322 -- ^ Constructors for which to generate JSON generating code.
328 -- ^ Constructors for which to generate JSON generating code.
323 -> Q Exp
329 -> Q Exp
324
330
325−consToValue _ _ _ _ [] = error $ "Data.Aeson.TH.consToValue: "
331+consToValue _ _ _ [] = error $ "Data.Aeson.TH.consToValue: "
326 ++ "Not a single constructor given!"
332 ++ "Not a single constructor given!"
327
333
328−consToValue target jc opts instTys cons = do
334+consToValue jc opts vars cons = do
329 value <- newName "value"
335 value <- newName "value"
330 tjs <- newNameList "_tj" $ arityInt jc
336 tjs <- newNameList "_tj" $ arityInt jc
331 tjls <- newNameList "_tjl" $ arityInt jc
337 tjls <- newNameList "_tjl" $ arityInt jc
332 let zippedTJs = zip tjs tjls
338 let zippedTJs = zip tjs tjls
333 interleavedTJs = interleave tjs tjls
339 interleavedTJs = interleave tjs tjls
334− lastTyVars = map varTToName $ drop (length instTys - arityInt jc) instTys
340+ lastTyVars = map varTToName $ drop (length vars - arityInt jc) vars
335 tvMap = M.fromList $ zip lastTyVars zippedTJs
341 tvMap = M.fromList $ zip lastTyVars zippedTJs
336 lamE (map varP $ interleavedTJs ++ [value]) $
342 lamE (map varP $ interleavedTJs ++ [value]) $
337 caseE (varE value) (matches tvMap)
343 caseE (varE value) (matches tvMap)
@@ -339,26 +345,66 @@ consToValue target jc opts instTys cons = do
339 matches tvMap = case cons of
345 matches tvMap = case cons of
340 -- A single constructor is directly encoded. The constructor itself may be
346 -- A single constructor is directly encoded. The constructor itself may be
341 -- forgotten.
347 -- forgotten.
342− [con] | not (tagSingleConstructors opts) -> [argsToValue target jc tvMap opts False con]
348+ [con] | not (tagSingleConstructors opts) -> [argsToValue jc tvMap opts False con]
343 _ | allNullaryToStringTag opts && all isNullary cons ->
349 _ | allNullaryToStringTag opts && all isNullary cons ->
344− [ match (conP conName []) (normalB $ conStr target opts conName) []
350+ [ match (conP conName []) (normalB $ conStr opts conName) []
345 | con <- cons
351 | con <- cons
346 , let conName = constructorName con
352 , let conName = constructorName con
347 ]
353 ]
348− | otherwise -> [argsToValue target jc tvMap opts True con | con <- cons]
354+ | otherwise -> [argsToValue jc tvMap opts True con | con <- cons]
349
355
350−-- | Name of the constructor as a quoted 'Value' or 'Encoding'.
351−conStr :: ToJSONFun -> Options -> Name -> Q Exp
356+conStr :: Options -> Name -> Q Exp
352−conStr Value opts = appE [|String|] . conTxt opts
357+conStr opts = appE [|String|] . conTxt opts
353−conStr Encoding opts = appE [|E.text|] . conTxt opts
354
358
355−-- | Name of the constructor as a quoted 'Text'.
356conTxt :: Options -> Name -> Q Exp
359conTxt :: Options -> Name -> Q Exp
357−conTxt opts = appE [|T.pack|] . stringE . conString opts
360+conTxt opts = appE [|T.pack|] . conStringE opts
361+
362+conStringE :: Options -> Name -> Q Exp
363+conStringE opts = stringE . constructorTagModifier opts . nameBase
364+
365+-- | Helper function used by both 'deriveToJSON' and 'mkToEncoding'. Generates
366+-- code to write out a value for a number of constructors. All constructors
367+-- must be from the same type.
368+consToEncoding :: JSONClass
369+ -- ^ The ToJSON variant being derived.
370+ -> Options
371+ -- ^ Encoding options.
372+ -> [Type]
373+ -- ^ The types from the data type/data family instance declaration
374+ -> [ConstructorInfo]
375+ -- ^ Constructors for which to generate JSON generating code.
376+ -> Q Exp
377+
378+consToEncoding _ _ _ [] = error $ "Data.Aeson.TH.consToEncoding: "
379+ ++ "Not a single constructor given!"
358
380
381+consToEncoding jc opts vars cons = do
382+ value <- newName "value"
383+ tes <- newNameList "_te" $ arityInt jc
384+ tels <- newNameList "_tel" $ arityInt jc
385+ let zippedTEs = zip tes tels
386+ interleavedTEs = interleave tes tels
387+ lastTyVars = map varTToName $ drop (length vars - arityInt jc) vars
388+ tvMap = M.fromList $ zip lastTyVars zippedTEs
389+ lamE (map varP $ interleavedTEs ++ [value]) $
390+ caseE (varE value) (matches tvMap)
391+ where
392+ matches tvMap = case cons of
393+ -- A single constructor is directly encoded. The constructor itself may be
394+ -- forgotten.
395+ [con] | not (tagSingleConstructors opts) -> [argsToEncoding jc tvMap opts False con]
359−-- | Name of the constructor.
396+ -- Encode just the name of the constructor of a sum type iff all the
397+ -- constructors are nullary.
398+ _ | allNullaryToStringTag opts && all isNullary cons ->
399+ [ match (conP conName [])
400+ (normalB $ encStr opts conName) []
401+ | con <- cons
402+ , let conName = constructorName con
403+ ]
404+ | otherwise -> [argsToEncoding jc tvMap opts True con | con <- cons]
405+
360−conString :: Options -> Name -> String
406+encStr :: Options -> Name -> Q Exp
361−conString opts = constructorTagModifier opts . nameBase
407+encStr opts = appE [|E.text|] . conTxt opts
362
408
363-- | If constructor is nullary.
409-- | If constructor is nullary.
364isNullary :: ConstructorInfo -> Bool
410isNullary :: ConstructorInfo -> Bool
@@ -366,133 +412,127 @@ isNullary ConstructorInfo { constructorVariant = NormalConstructor
366 , constructorFields = tys } = null tys
412 , constructorFields = tys } = null tys
367isNullary _ = False
413isNullary _ = False
368
414
369−-- | Wrap fields of a non-record constructor. See 'sumToValue'.
370−opaqueSumToValue :: ToJSONFun -> Options -> Bool -> Bool -> Name -> ExpQ -> ExpQ
415+sumToValue :: Options -> Bool -> Bool -> Name -> Q Exp -> Q Exp
371−opaqueSumToValue target opts multiCons nullary conName value =
372− sumToValue target opts multiCons nullary conName
373− value
374− pairs
375− where
376− pairs contentsFieldName = pairE contentsFieldName value
377−
378−-- | Wrap fields of a record constructor. See 'sumToValue'.
379−recordSumToValue :: ToJSONFun -> Options -> Bool -> Bool -> Name -> ExpQ -> ExpQ
380−recordSumToValue target opts multiCons nullary conName pairs =
381− sumToValue target opts multiCons nullary conName
416+sumToValue opts multiCons nullary conName exp
382− (fromPairsE pairs)
383− (const pairs)
384−
385−-- | Wrap fields of a constructor.
386−sumToValue
387− :: ToJSONFun
388− -- ^ The method being derived.
389− -> Options
390− -- ^ Deriving options.
391− -> Bool
392− -- ^ Does this type have multiple constructors.
393− -> Bool
394− -- ^ Is this constructor nullary.
395− -> Name
396− -- ^ Constructor name.
397− -> ExpQ
398− -- ^ Fields of the constructor as a 'Value' or 'Encoding'.
399− -> (String -> ExpQ)
400− -- ^ Representation of an 'Object' fragment used for the 'TaggedObject'
401− -- variant; of type @[(Text,Value)]@ or @[Encoding]@, depending on the method
402− -- being derived.
403− --
404− -- - For non-records, produces a pair @"contentsFieldName":value@,
405− -- given a @contentsFieldName@ as an argument. See 'opaqueSumToValue'.
406− -- - For records, produces the list of pairs corresponding to fields of the
407− -- encoded value (ignores the argument). See 'recordSumToValue'.
408− -> ExpQ
409−sumToValue target opts multiCons nullary conName value pairs
410 | multiCons =
417 | multiCons =
411 case sumEncoding opts of
418 case sumEncoding opts of
412 TwoElemArray ->
419 TwoElemArray ->
413− array target [conStr target opts conName, value]
420+ [|Array|] `appE` ([|V.fromList|] `appE` listE [conStr opts conName, exp])
414 TaggedObject{tagFieldName, contentsFieldName} ->
421 TaggedObject{tagFieldName, contentsFieldName} ->
415− -- TODO: Maybe throw an error in case
416− -- tagFieldName overwrites a field in pairs.
417− let tag = pairE tagFieldName (conStr target opts conName)
422+ let tag = infixApp [|T.pack tagFieldName|] [|(.=)|] (conStr opts conName)
418− content = pairs contentsFieldName
423+ contents = infixApp [|T.pack contentsFieldName|] [|(.=)|] exp
419− in fromPairsE $
424+ in
420− if nullary then tag else infixApp tag [|(Monoid.<>)|] content
425+ [|A.object|] `appE` listE (if nullary then [tag] else [tag, contents])
421 ObjectWithSingleField ->
426 ObjectWithSingleField ->
422− objectE [(conString opts conName, value)]
427+ [|A.object|] `appE` listE
428+ [ infixApp (conTxt opts conName) [|(.=)|] exp
429+ ]
423− UntaggedValue | nullary -> conStr target opts conName
430+ UntaggedValue | nullary -> conStr opts conName
424− UntaggedValue -> value
431+ UntaggedValue -> exp
425− | otherwise = value
432+ | otherwise = exp
426
433
427-- | Generates code to generate the JSON encoding of a single constructor.
434-- | Generates code to generate the JSON encoding of a single constructor.
428−argsToValue :: ToJSONFun -> JSONClass -> TyVarMap -> Options -> Bool -> ConstructorInfo -> Q Match
435+argsToValue :: JSONClass -> TyVarMap -> Options -> Bool -> ConstructorInfo -> Q Match
429
436
430-- Polyadic constructors with special case for unary constructors.
437-- Polyadic constructors with special case for unary constructors.
431−argsToValue target jc tvMap opts multiCons
438+argsToValue jc tvMap opts multiCons
432 ConstructorInfo { constructorName = conName
439 ConstructorInfo { constructorName = conName
433 , constructorVariant = NormalConstructor
440 , constructorVariant = NormalConstructor
434 , constructorFields = argTys } = do
441 , constructorFields = argTys } = do
435 argTys' <- mapM resolveTypeSynonyms argTys
442 argTys' <- mapM resolveTypeSynonyms argTys
436 let len = length argTys'
443 let len = length argTys'
437 args <- newNameList "arg" len
444 args <- newNameList "arg" len
438− let js = case [ dispatchToJSON target jc conName tvMap argTy
445+ js <- case [ dispatchToJSON jc conName tvMap argTy
439− `appE` varE arg
446+ `appE` varE arg
440− | (arg, argTy) <- zip args argTys'
447+ | (arg, argTy) <- zip args argTys'
441− ] of
448+ ] of
442− -- Single argument is directly converted.
449+ -- Single argument is directly converted.
443− [e] -> e
450+ [e] -> return e
444− -- Zero and multiple arguments are converted to a JSON array.
451+ -- Multiple arguments are converted to a JSON array.
445− es -> array target es
452+ es -> do
446−
453+ mv <- newName "mv"
454+ let newMV = bindS (varP mv)
455+ ([|VM.unsafeNew|] `appE`
456+ litE (integerL $ fromIntegral len))
457+ stmts = [ noBindS $
458+ [|VM.unsafeWrite|] `appE`
459+ varE mv `appE`
460+ litE (integerL ix) `appE`
461+ e
462+ | (ix, e) <- zip [(0::Integer)..] es
463+ ]
464+ ret = noBindS $ [|return|] `appE` varE mv
465+ return $ [|Array|] `appE`
466+ (varE 'V.create `appE`
467+ doE (newMV:stmts++[ret]))
447 match (conP conName $ map varP args)
468 match (conP conName $ map varP args)
448− (normalB $ opaqueSumToValue target opts multiCons (null argTys') conName js)
469+ (normalB $ sumToValue opts multiCons (null argTys') conName js)
449 []
470 []
450
471
451-- Records.
472-- Records.
452−argsToValue target jc tvMap opts multiCons
473+argsToValue jc tvMap opts multiCons
453 info@ConstructorInfo { constructorName = conName
474 info@ConstructorInfo { constructorName = conName
454 , constructorVariant = RecordConstructor fields
475 , constructorVariant = RecordConstructor fields
455 , constructorFields = argTys } =
476 , constructorFields = argTys } =
456 case (unwrapUnaryRecords opts, not multiCons, argTys) of
477 case (unwrapUnaryRecords opts, not multiCons, argTys) of
457− (True,True,[_]) -> argsToValue target jc tvMap opts multiCons
478+ (True,True,[_]) -> argsToValue jc tvMap opts multiCons
458 (info{constructorVariant = NormalConstructor})
479 (info{constructorVariant = NormalConstructor})
459 _ -> do
480 _ -> do
460 argTys' <- mapM resolveTypeSynonyms argTys
481 argTys' <- mapM resolveTypeSynonyms argTys
461 args <- newNameList "arg" $ length argTys'
482 args <- newNameList "arg" $ length argTys'
483+ let exp = [|A.object|] `appE` pairs
484+
462− let pairs | omitNothingFields opts = infixApp maybeFields
485+ pairs | omitNothingFields opts = infixApp maybeFields
463− [|(Monoid.<>)|]
486+ [|(++)|]
464 restFields
487 restFields
465− | otherwise = mconcatE (map pureToPair argCons)
488+ | otherwise = listE $ map toPair argCons
466
489
467− argCons = zip3 (map varE args) argTys' fields
490+ argCons = zip3 args argTys' fields
468
491
469− maybeFields = mconcatE (map maybeToPair maybes)
492+ maybeFields = [|catMaybes|] `appE` listE (map maybeToPair maybes)
470
493
471− restFields = mconcatE (map pureToPair rest)
494+ restFields = listE $ map toPair rest
472
495
473− (maybes0, rest0) = partition isMaybe argCons
496+ (maybes, rest) = partition isMaybe argCons
474− (options, rest) = partition isOption rest0
475− maybes = maybes0 ++ map optionToMaybe options
476
497
477− maybeToPair = toPairLifted True
498+ maybeToPair (arg, argTy, field) =
478− pureToPair = toPairLifted False
499+ infixApp ([|keyValuePairWith|]
500+ `appE` dispatchToJSON jc conName tvMap argTy
501+ `appE` toFieldName field)
502+ [|(<$>)|]
503+ (varE arg)
479
504
480− toPairLifted lifted (arg, argTy, field) =
505+ toPair (arg, argTy, field) =
506+ [|keyValuePairWith|]
481− let toValue = dispatchToJSON target jc conName tvMap argTy
507+ `appE` dispatchToJSON jc conName tvMap argTy
482− fieldName = fieldLabel opts field
508+ `appE` toFieldName field
483− e arg' = pairE fieldName (toValue `appE` arg')
509+ `appE` varE arg
484− in if lifted
510+
485− then do
511+ toFieldName field = [|T.pack|] `appE` fieldLabelExp opts field
486− x <- newName "x"
487− [|maybe mempty|] `appE` lam1E (varP x) (e (varE x)) `appE` arg
488− else e arg
489
512
490 match (conP conName $ map varP args)
513 match (conP conName $ map varP args)
491− (normalB $ recordSumToValue target opts multiCons (null argTys) conName pairs)
514+ ( normalB
515+ $ if multiCons
516+ then case sumEncoding opts of
517+ TwoElemArray -> [|toJSON|] `appE` tupE [conStr opts conName, exp]
518+ TaggedObject{tagFieldName} ->
519+ [|A.object|] `appE`
520+ -- TODO: Maybe throw an error in case
521+ -- tagFieldName overwrites a field in pairs.
522+ infixApp (infixApp [|T.pack tagFieldName|]
523+ [|(.=)|]
524+ (conStr opts conName))
525+ [|(:)|]
526+ pairs
527+ ObjectWithSingleField ->
528+ [|A.object|] `appE` listE
529+ [ infixApp (conTxt opts conName) [|(.=)|] exp ]
530+ UntaggedValue -> exp
531+ else exp
492− []
532+ ) []
493
533
494-- Infix constructors.
534-- Infix constructors.
495−argsToValue target jc tvMap opts multiCons
535+argsToValue jc tvMap opts multiCons
496 ConstructorInfo { constructorName = conName
536 ConstructorInfo { constructorName = conName
497 , constructorVariant = InfixConstructor
537 , constructorVariant = InfixConstructor
498 , constructorFields = argTys } = do
538 , constructorFields = argTys } = do
@@ -501,12 +541,11 @@ argsToValue target jc tvMap opts multiCons
501 ar <- newName "argR"
541 ar <- newName "argR"
502 match (infixP (varP al) conName (varP ar))
542 match (infixP (varP al) conName (varP ar))
503 ( normalB
543 ( normalB
504− $ opaqueSumToValue target opts multiCons False conName
544+ $ sumToValue opts multiCons False conName
505− $ array target
506− [ dispatchToJSON target jc conName tvMap aTy
545+ $ [|toJSON|] `appE` listE [ dispatchToJSON jc conName tvMap aTy
507− `appE` varE a
546+ `appE` varE a
508− | (a, aTy) <- [(al,alTy), (ar,arTy)]
547+ | (a, aTy) <- [(al,alTy), (ar,arTy)]
509− ]
548+ ]
510 )
549 )
511 []
550 []
512
551
@@ -514,63 +553,151 @@ isMaybe :: (a, Type, b) -> Bool
514isMaybe (_, AppT (ConT t) _, _) = t == ''Maybe
553isMaybe (_, AppT (ConT t) _, _) = t == ''Maybe
515isMaybe _ = False
554isMaybe _ = False
516
555
517−isOption :: (a, Type, b) -> Bool
518−isOption (_, AppT (ConT t) _, _) = t == ''Semigroup.Option
519−isOption _ = False
520−
521−optionToMaybe :: (ExpQ, b, c) -> (ExpQ, b, c)
522−optionToMaybe (a, b, c) = ([|Semigroup.getOption|] `appE` a, b, c)
523−
524(<^>) :: ExpQ -> ExpQ -> ExpQ
556(<^>) :: ExpQ -> ExpQ -> ExpQ
525(<^>) a b = infixApp a [|(E.><)|] b
557(<^>) a b = infixApp a [|(E.><)|] b
526infixr 6 <^>
558infixr 6 <^>
527
559
560+(<:>) :: ExpQ -> ExpQ -> ExpQ
561+(<:>) a b = a <^> [|E.colon|] <^> b
562+infixr 5 <:>
563+
528(<%>) :: ExpQ -> ExpQ -> ExpQ
564(<%>) :: ExpQ -> ExpQ -> ExpQ
529(<%>) a b = a <^> [|E.comma|] <^> b
565(<%>) a b = a <^> [|E.comma|] <^> b
530infixr 4 <%>
566infixr 4 <%>
531
567
532−-- | Wrap a list of quoted 'Value's in a quoted 'Array' (of type 'Value').
533−array :: ToJSONFun -> [ExpQ] -> ExpQ
568+array :: ExpQ -> ExpQ
534−array Encoding [] = [|E.emptyArray_|]
535−array Value [] = [|Array V.empty|]
536−array Encoding es = [|E.wrapArray|] `appE` foldr1 (<%>) es
569+array exp = [|E.wrapArray|] `appE` exp
537−array Value es = do
538− mv <- newName "mv"
539− let newMV = bindS (varP mv)
540− ([|VM.unsafeNew|] `appE`
541− litE (integerL $ fromIntegral (length es)))
542− stmts = [ noBindS $
543− [|VM.unsafeWrite|] `appE`
544− varE mv `appE`
545− litE (integerL ix) `appE`
546− e
547− | (ix, e) <- zip [(0::Integer)..] es
548− ]
549− ret = noBindS $ [|return|] `appE` varE mv
550− [|Array|] `appE`
551− (varE 'V.create `appE`
552− doE (newMV:stmts++[ret]))
553
570
554−-- | Wrap an associative list of keys and quoted values in a quoted 'Object'.
555−objectE :: [(String, ExpQ)] -> ExpQ
571+object :: ExpQ -> ExpQ
556−objectE = fromPairsE . mconcatE . fmap (uncurry pairE)
572+object exp = [|E.wrapObject|] `appE` exp
557
573
558−-- | 'mconcat' a list of fixed length.
574+sumToEncoding :: Options -> Bool -> Bool -> Name -> Q Exp -> Q Exp
559−--
575+sumToEncoding opts multiCons nullary conName exp
560−-- > mconcatE [ [|x|], [|y|], [|z|] ] = [| x <> (y <> z) |]
576+ | multiCons =
561−mconcatE :: [ExpQ] -> ExpQ
577+ let fexp = exp in
562−mconcatE [] = [|Monoid.mempty|]
578+ case sumEncoding opts of
563−mconcatE [x] = x
579+ TwoElemArray ->
564−mconcatE (x : xs) = infixApp x [|(Monoid.<>)|] (mconcatE xs)
580+ array (encStr opts conName <%> fexp)
581+ TaggedObject{tagFieldName, contentsFieldName} ->
582+ let tag = [|E.text (T.pack tagFieldName)|] <:> encStr opts conName
583+ contents = [|E.text (T.pack contentsFieldName)|] <:> fexp
584+ in
585+ object $
586+ if nullary then tag else tag <%> contents
587+ ObjectWithSingleField ->
588+ object (encStr opts conName <:> fexp)
589+ UntaggedValue | nullary -> encStr opts conName
590+ UntaggedValue -> exp
591+ | otherwise = exp
565
592
566−fromPairsE :: ExpQ -> ExpQ
593+-- | Generates code to generate the JSON encoding of a single constructor.
567−fromPairsE = ([|fromPairs|] `appE`)
594+argsToEncoding :: JSONClass -> TyVarMap -> Options -> Bool -> ConstructorInfo -> Q Match
568
595
569−-- | Create (an encoding of) a key-value pair.
596+-- Polyadic constructors with special case for unary constructors.
570−--
597+argsToEncoding jc tvMap opts multiCons
571−-- > pairE "k" [|v|] = [|pair "k" v|]
598+ ConstructorInfo { constructorName = conName
572−pairE :: String -> ExpQ -> ExpQ
599+ , constructorVariant = NormalConstructor
573−pairE k v = [|pair k|] `appE` v
600+ , constructorFields = argTys } = do
601+ argTys' <- mapM resolveTypeSynonyms argTys
602+ args <- newNameList "arg" $ length argTys'
603+ js <- case zip args argTys' of
604+ -- Nullary constructors are converted to an empty array.
605+ [] -> return [| E.emptyArray_ |]
606+
607+ -- Single argument is directly converted.
608+ [(e,eTy)] -> return (dispatchToEncoding jc conName tvMap eTy
609+ `appE` varE e)
610+ -- Multiple arguments are converted to a JSON array.
611+ es ->
612+ return (array (foldr1 (<%>) [ dispatchToEncoding jc conName tvMap xTy
613+ `appE` varE x
614+ | (x,xTy) <- es
615+ ]))
616+ match (conP conName $ map varP args)
617+ (normalB $ sumToEncoding opts multiCons (null argTys') conName js)
618+ []
619+
620+-- Records.
621+argsToEncoding jc tvMap opts multiCons
622+ info@ConstructorInfo { constructorName = conName
623+ , constructorVariant = RecordConstructor fields
624+ , constructorFields = argTys } =
625+ case (unwrapUnaryRecords opts, not multiCons, argTys) of
626+ (True,True,[_]) -> argsToEncoding jc tvMap opts multiCons
627+ (info{constructorVariant = NormalConstructor})
628+ _ -> do
629+ argTys' <- mapM resolveTypeSynonyms argTys
630+ args <- newNameList "arg" $ length argTys'
631+ let exp = object objBody
632+
633+ objBody = [|E.econcat|] `appE`
634+ ([|intersperse E.comma|] `appE` pairs)
635+ pairs | omitNothingFields opts = infixApp maybeFields
636+ [|(++)|]
637+ restFields
638+ | otherwise = listE (map toPair argCons)
639+
640+ argCons = zip3 args argTys' fields
641+
642+ maybeFields = [|catMaybes|] `appE` listE (map maybeToPair maybes)
643+
644+ restFields = listE (map toPair rest)
645+
646+ (maybes, rest) = partition isMaybe argCons
647+
648+ maybeToPair (arg, argTy, field) =
649+ infixApp
650+ (infixApp
651+ (infixE
652+ (Just $ toFieldName field <^> [|E.colon|])
653+ [|(E.><)|]
654+ Nothing)
655+ [|(.)|]
656+ (dispatchToEncoding jc conName tvMap argTy))
657+ [|(<$>)|]
658+ (varE arg)
659+
660+ toPair (arg, argTy, field) =
661+ toFieldName field
662+ <:> dispatchToEncoding jc conName tvMap argTy
663+ `appE` varE arg
664+
665+ toFieldName field = [|E.text|] `appE`
666+ ([|T.pack|] `appE` fieldLabelExp opts field)
667+
668+ match (conP conName $ map varP args)
669+ ( normalB
670+ $ if multiCons
671+ then case sumEncoding opts of
672+ TwoElemArray -> array $
673+ encStr opts conName <%> exp
674+ TaggedObject{tagFieldName} -> object $
675+ ([|E.text (T.pack tagFieldName)|] <:>
676+ encStr opts conName) <%>
677+ objBody
678+ ObjectWithSingleField -> object $
679+ encStr opts conName <:> exp
680+ UntaggedValue -> exp
681+ else exp
682+ ) []
683+
684+-- Infix constructors.
685+argsToEncoding jc tvMap opts multiCons
686+ ConstructorInfo { constructorName = conName
687+ , constructorVariant = InfixConstructor
688+ , constructorFields = argTys } = do
689+ al <- newName "argL"
690+ ar <- newName "argR"
691+ [alTy,arTy] <- mapM resolveTypeSynonyms argTys
692+ match (infixP (varP al) conName (varP ar))
693+ ( normalB
694+ $ sumToEncoding opts multiCons False conName
695+ $ array (foldr1 (<%>) [ dispatchToEncoding jc conName tvMap aTy
696+ `appE` varE a
697+ | (a,aTy) <- [(al,alTy), (ar,arTy)]
698+ ])
699+ )
700+ []
574
701
575--------------------------------------------------------------------------------
702--------------------------------------------------------------------------------
576-- FromJSON
703-- FromJSON
@@ -663,13 +790,13 @@ consFromJSON :: JSONClass
663consFromJSON _ _ _ _ [] = error $ "Data.Aeson.TH.consFromJSON: "
790consFromJSON _ _ _ _ [] = error $ "Data.Aeson.TH.consFromJSON: "
664 ++ "Not a single constructor given!"
791 ++ "Not a single constructor given!"
665
792
666−consFromJSON jc tName opts instTys cons = do
793+consFromJSON jc tName opts vars cons = do
667 value <- newName "value"
794 value <- newName "value"
668 pjs <- newNameList "_pj" $ arityInt jc
795 pjs <- newNameList "_pj" $ arityInt jc
669 pjls <- newNameList "_pjl" $ arityInt jc
796 pjls <- newNameList "_pjl" $ arityInt jc
670 let zippedPJs = zip pjs pjls
797 let zippedPJs = zip pjs pjls
671 interleavedPJs = interleave pjs pjls
798 interleavedPJs = interleave pjs pjls
672− lastTyVars = map varTToName $ drop (length instTys - arityInt jc) instTys
799+ lastTyVars = map varTToName $ drop (length vars - arityInt jc) vars
673 tvMap = M.fromList $ zip lastTyVars zippedPJs
800 tvMap = M.fromList $ zip lastTyVars zippedPJs
674 lamE (map varP $ interleavedPJs ++ [value]) $ lamExpr value tvMap
801 lamE (map varP $ interleavedPJs ++ [value]) $ lamExpr value tvMap
675
802
@@ -697,7 +824,8 @@ consFromJSON jc tName opts instTys cons = do
697 [ liftM2 (,) (normalG $
824 [ liftM2 (,) (normalG $
698 infixApp (varE txt)
825 infixApp (varE txt)
699 [|(==)|]
826 [|(==)|]
827+ ([|T.pack|] `appE`
700− (conTxt opts conName)
828+ conStringE opts conName)
701 )
829 )
702 ([|pure|] `appE` conE conName)
830 ([|pure|] `appE` conE conName)
703 | con <- cons
831 | con <- cons
@@ -789,7 +917,7 @@ consFromJSON jc tName opts instTys cons = do
789 caseE (varE conVal)
917 caseE (varE conVal)
790 [ match (conP 'String [varP str])
918 [ match (conP 'String [varP str])
791 (guardedB
919 (guardedB
792− [ liftM2 (,) (normalG $ infixApp (varE str) [|(==)|] (conTxt opts conName)
920+ [ liftM2 (,) (normalG $ infixApp (varE str) [|(==)|] ([|T.pack|] `appE` conStringE opts conName)
793 )
921 )
794 ([|pure|] `appE` conE conName)
922 ([|pure|] `appE` conE conName)
795 ]
923 ]
@@ -922,38 +1050,18 @@ parseRecord :: JSONClass
922 -> Name
1050 -> Name
923 -> [Name]
1051 -> [Name]
924 -> Name
1052 -> Name
925− -> Bool
926 -> ExpQ
1053 -> ExpQ
927−parseRecord jc tvMap argTys opts tName conName fields obj inTaggedObject =
1054+parseRecord jc tvMap argTys opts tName conName fields obj =
928− (if rejectUnknownFields opts
929− then infixApp checkUnknownRecords [|(>>)|]
930− else id) $
931 foldl' (\a b -> infixApp a [|(<*>)|] b)
1055 foldl' (\a b -> infixApp a [|(<*>)|] b)
932 (infixApp (conE conName) [|(<$>)|] x)
1056 (infixApp (conE conName) [|(<$>)|] x)
933 xs
1057 xs
934 where
1058 where
935− tagFieldNameAppender =
936− if inTaggedObject then (tagFieldName (sumEncoding opts) :) else id
937− knownFields = appE [|H.fromList|] $ listE $
938− map (\knownName -> tupE [appE [|T.pack|] $ litE $ stringL knownName, [|()|]]) $
939− tagFieldNameAppender $ map nameBase fields
940− checkUnknownRecords =
941− caseE (appE [|H.keys|] $ infixApp (varE obj) [|H.difference|] knownFields)
942− [ match (listP []) (normalB [|return ()|]) []
943− , newName "unknownFields" >>=
944− \unknownFields -> match (varP unknownFields)
945− (normalB $ appE [|fail|] $ infixApp
946− (litE (stringL "Unknown fields: "))
947− [|(++)|]
948− (appE [|show|] (varE unknownFields)))
949− []
950− ]
951 x:xs = [ [|lookupField|]
1059 x:xs = [ [|lookupField|]
952 `appE` dispatchParseJSON jc conName tvMap argTy
1060 `appE` dispatchParseJSON jc conName tvMap argTy
953 `appE` litE (stringL $ show tName)
1061 `appE` litE (stringL $ show tName)
954 `appE` litE (stringL $ constructorTagModifier opts $ nameBase conName)
1062 `appE` litE (stringL $ constructorTagModifier opts $ nameBase conName)
955 `appE` varE obj
1063 `appE` varE obj
956− `appE` ( [|T.pack|] `appE` stringE (fieldLabel opts field)
1064+ `appE` ( [|T.pack|] `appE` fieldLabelExp opts field
957 )
1065 )
958 | (field, argTy) <- zip fields argTys
1066 | (field, argTy) <- zip fields argTys
959 ]
1067 ]
@@ -1022,7 +1130,7 @@ parseArgs jc tvMap tName opts
1022 , constructorFields = argTys }
1130 , constructorFields = argTys }
1023 (Left (_, obj)) = do
1131 (Left (_, obj)) = do
1024 argTys' <- mapM resolveTypeSynonyms argTys
1132 argTys' <- mapM resolveTypeSynonyms argTys
1025− parseRecord jc tvMap argTys' opts tName conName fields obj True
1133+ parseRecord jc tvMap argTys' opts tName conName fields obj
1026parseArgs jc tvMap tName opts
1134parseArgs jc tvMap tName opts
1027 info@ConstructorInfo { constructorName = conName
1135 info@ConstructorInfo { constructorName = conName
1028 , constructorVariant = RecordConstructor fields
1136 , constructorVariant = RecordConstructor fields
@@ -1037,7 +1145,7 @@ parseArgs jc tvMap tName opts
1037 argTys' <- mapM resolveTypeSynonyms argTys
1145 argTys' <- mapM resolveTypeSynonyms argTys
1038 caseE (varE valName)
1146 caseE (varE valName)
1039 [ match (conP 'Object [varP obj]) (normalB $
1147 [ match (conP 'Object [varP obj]) (normalB $
1040− parseRecord jc tvMap argTys' opts tName conName fields obj False) []
1148+ parseRecord jc tvMap argTys' opts tName conName fields obj) []
1041 , matchFailed tName conName "Object"
1149 , matchFailed tName conName "Object"
1042 ]
1150 ]
1043
1151
@@ -1126,11 +1234,6 @@ instance OVERLAPPABLE_ LookupField a where
1126instance INCOHERENT_ LookupField (Maybe a) where
1234instance INCOHERENT_ LookupField (Maybe a) where
1127 lookupField pj _ _ = parseOptionalFieldWith pj
1235 lookupField pj _ _ = parseOptionalFieldWith pj
1128
1236
1129−instance INCOHERENT_ LookupField (Semigroup.Option a) where
1130− lookupField pj tName rec obj key =
1131− fmap Semigroup.Option
1132− (lookupField (fmap Semigroup.getOption . pj) tName rec obj key)
1133−
1134lookupFieldWith :: (Value -> Parser a) -> String -> String
1237lookupFieldWith :: (Value -> Parser a) -> String -> String
1135 -> Object -> T.Text -> Parser a
1238 -> Object -> T.Text -> Parser a
1136lookupFieldWith pj tName rec obj key =
1239lookupFieldWith pj tName rec obj key =
@@ -1138,6 +1241,9 @@ lookupFieldWith pj tName rec obj key =
1138 Nothing -> unknownFieldFail tName rec (T.unpack key)
1241 Nothing -> unknownFieldFail tName rec (T.unpack key)
1139 Just v -> pj v <?> Key key
1242 Just v -> pj v <?> Key key
1140
1243
1244+keyValuePairWith :: (v -> Value) -> T.Text -> v -> Pair
1245+keyValuePairWith tj name value = (name, tj value)
1246+
1141unknownFieldFail :: String -> String -> String -> Parser fail
1247unknownFieldFail :: String -> String -> String -> Parser fail
1142unknownFieldFail tName rec key =
1248unknownFieldFail tName rec key =
1143 fail $ printf "When parsing the record %s of type %s the key %s was not present."
1249 fail $ printf "When parsing the record %s of type %s the key %s was not present."
@@ -1220,27 +1326,23 @@ deriveJSONClass :: [(JSONFun, JSONClass -> Name -> Options -> [Type]
1220deriveJSONClass consFuns jc opts name = do
1326deriveJSONClass consFuns jc opts name = do
1221 info <- reifyDatatype name
1327 info <- reifyDatatype name
1222 case info of
1328 case info of
1223− DatatypeInfo { datatypeContext = ctxt
1329+ DatatypeInfo { datatypeContext = ctxt
1224− , datatypeName = parentName
1330+ , datatypeName = parentName
1225−#if MIN_VERSION_th_abstraction(0,3,0)
1226− , datatypeInstTypes = instTys
1227−#else
1228− , datatypeVars = instTys
1331+ , datatypeVars = vars
1229−#endif
1230− , datatypeVariant = variant
1332+ , datatypeVariant = variant
1231− , datatypeCons = cons
1333+ , datatypeCons = cons
1232 } -> do
1334 } -> do
1233 (instanceCxt, instanceType)
1335 (instanceCxt, instanceType)
1234− <- buildTypeInstance parentName jc ctxt instTys variant
1336+ <- buildTypeInstance parentName jc ctxt vars variant
1235 (:[]) <$> instanceD (return instanceCxt)
1337 (:[]) <$> instanceD (return instanceCxt)
1236 (return instanceType)
1338 (return instanceType)
1237− (methodDecs parentName instTys cons)
1339+ (methodDecs parentName vars cons)
1238 where
1340 where
1239 methodDecs :: Name -> [Type] -> [ConstructorInfo] -> [Q Dec]
1341 methodDecs :: Name -> [Type] -> [ConstructorInfo] -> [Q Dec]
1240− methodDecs parentName instTys cons = flip map consFuns $ \(jf, jfMaker) ->
1342+ methodDecs parentName vars cons = flip map consFuns $ \(jf, jfMaker) ->
1241 funD (jsonFunValName jf (arity jc))
1343 funD (jsonFunValName jf (arity jc))
1242 [ clause []
1344 [ clause []
1243− (normalB $ jfMaker jc parentName opts instTys cons)
1345+ (normalB $ jfMaker jc parentName opts vars cons)
1244 []
1346 []
1245 ]
1347 ]
1246
1348
@@ -1256,21 +1358,17 @@ mkFunCommon :: (JSONClass -> Name -> Options -> [Type] -> [ConstructorInfo] -> Q
1256mkFunCommon consFun jc opts name = do
1358mkFunCommon consFun jc opts name = do
1257 info <- reifyDatatype name
1359 info <- reifyDatatype name
1258 case info of
1360 case info of
1259− DatatypeInfo { datatypeContext = ctxt
1361+ DatatypeInfo { datatypeContext = ctxt
1260− , datatypeName = parentName
1362+ , datatypeName = parentName
1261−#if MIN_VERSION_th_abstraction(0,3,0)
1262− , datatypeInstTypes = instTys
1263−#else
1264− , datatypeVars = instTys
1363+ , datatypeVars = vars
1265−#endif
1266− , datatypeVariant = variant
1364+ , datatypeVariant = variant
1267− , datatypeCons = cons
1365+ , datatypeCons = cons
1268 } -> do
1366 } -> do
1269 -- We force buildTypeInstance here since it performs some checks for whether
1367 -- We force buildTypeInstance here since it performs some checks for whether
1270 -- or not the provided datatype's kind matches the derived method's
1368 -- or not the provided datatype's kind matches the derived method's
1271 -- typeclass, and produces errors if it can't.
1369 -- typeclass, and produces errors if it can't.
1272− !_ <- buildTypeInstance parentName jc ctxt instTys variant
1370+ !_ <- buildTypeInstance parentName jc ctxt vars variant
1273− consFun jc parentName opts instTys cons
1371+ consFun jc parentName opts vars cons
1274
1372
1275dispatchFunByType :: JSONClass
1373dispatchFunByType :: JSONClass
1276 -> JSONFun
1374 -> JSONFun
@@ -1315,13 +1413,10 @@ dispatchFunByType jc jf conName tvMap list ty = do
1315 (interleave rhsArgs rhsArgs)
1413 (interleave rhsArgs rhsArgs)
1316 else varE $ jsonFunValOrListName list jf Arity0
1414 else varE $ jsonFunValOrListName list jf Arity0
1317
1415
1318−dispatchToJSON
1319− :: ToJSONFun -> JSONClass -> Name -> TyVarMap -> Type -> Q Exp
1320−dispatchToJSON target jc n tvMap =
1321− dispatchFunByType jc (targetToJSONFun target) n tvMap False
1322−
1323−dispatchParseJSON
1416+dispatchToJSON, dispatchToEncoding, dispatchParseJSON
1324 :: JSONClass -> Name -> TyVarMap -> Type -> Q Exp
1417 :: JSONClass -> Name -> TyVarMap -> Type -> Q Exp
1418+dispatchToJSON jc n tvMap = dispatchFunByType jc ToJSON n tvMap False
1419+dispatchToEncoding jc n tvMap = dispatchFunByType jc ToEncoding n tvMap False
1325dispatchParseJSON jc n tvMap = dispatchFunByType jc ParseJSON n tvMap False
1420dispatchParseJSON jc n tvMap = dispatchFunByType jc ParseJSON n tvMap False
1326
1421
1327--------------------------------------------------------------------------------
1422--------------------------------------------------------------------------------
@@ -1603,13 +1698,21 @@ type TyVarMap = Map Name (Name, Name)
1603-- | Returns True if a Type has kind *.
1698-- | Returns True if a Type has kind *.
1604hasKindStar :: Type -> Bool
1699hasKindStar :: Type -> Bool
1605hasKindStar VarT{} = True
1700hasKindStar VarT{} = True
1701+#if MIN_VERSION_template_haskell(2,8,0)
1606hasKindStar (SigT _ StarT) = True
1702hasKindStar (SigT _ StarT) = True
1703+#else
1704+hasKindStar (SigT _ StarK) = True
1705+#endif
1607hasKindStar _ = False
1706hasKindStar _ = False
1608
1707
1609-- Returns True is a kind is equal to *, or if it is a kind variable.
1708-- Returns True is a kind is equal to *, or if it is a kind variable.
1610isStarOrVar :: Kind -> Bool
1709isStarOrVar :: Kind -> Bool
1710+#if MIN_VERSION_template_haskell(2,8,0)
1611isStarOrVar StarT = True
1711isStarOrVar StarT = True
1612isStarOrVar VarT{} = True
1712isStarOrVar VarT{} = True
1713+#else
1714+isStarOrVar StarK = True
1715+#endif
1613isStarOrVar _ = False
1716isStarOrVar _ = False
1614
1717
1615-- Generate a list of fresh names with a common prefix, and numbered suffixes.
1718-- Generate a list of fresh names with a common prefix, and numbered suffixes.
@@ -1667,7 +1770,9 @@ isTyFamily (ConT n) = do
1667#else
1770#else
1668 FamilyI (FamilyD TypeFam _ _ _) _ -> True
1771 FamilyI (FamilyD TypeFam _ _ _) _ -> True
1669#endif
1772#endif
1773+#if MIN_VERSION_template_haskell(2,9,0)
1670 FamilyI ClosedTypeFamilyD{} _ -> True
1774 FamilyI ClosedTypeFamilyD{} _ -> True
1775+#endif
1671 _ -> False
1776 _ -> False
1672isTyFamily _ = return False
1777isTyFamily _ = return False
1673
1778
@@ -1695,7 +1800,9 @@ mentionsName = go
1695 go :: Type -> [Name] -> Bool
1800 go :: Type -> [Name] -> Bool
1696 go (AppT t1 t2) names = go t1 names || go t2 names
1801 go (AppT t1 t2) names = go t1 names || go t2 names
1697 go (SigT t _k) names = go t names
1802 go (SigT t _k) names = go t names
1803+#if MIN_VERSION_template_haskell(2,8,0)
1698 || go _k names
1804 || go _k names
1805+#endif
1699 go (VarT n) names = n `elem` names
1806 go (VarT n) names = n `elem` names
1700 go _ _ = False
1807 go _ _ = False
1701
1808
@@ -1751,14 +1858,23 @@ uncurryTy t = ([], t :| [])
1751
1858
1752-- | Like uncurryType, except on a kind level.
1859-- | Like uncurryType, except on a kind level.
1753uncurryKind :: Kind -> NonEmpty Kind
1860uncurryKind :: Kind -> NonEmpty Kind
1861+#if MIN_VERSION_template_haskell(2,8,0)
1754uncurryKind = snd . uncurryTy
1862uncurryKind = snd . uncurryTy
1863+#else
1864+uncurryKind (ArrowK k1 k2) = k1 <| uncurryKind k2
1865+uncurryKind k = k :| []
1866+#endif
1755
1867
1756createKindChain :: Int -> Kind
1868createKindChain :: Int -> Kind
1757createKindChain = go starK
1869createKindChain = go starK
1758 where
1870 where
1759 go :: Kind -> Int -> Kind
1871 go :: Kind -> Int -> Kind
1760 go k 0 = k
1872 go k 0 = k
1873+#if MIN_VERSION_template_haskell(2,8,0)
1761 go k !n = go (AppT (AppT ArrowT StarT) k) (n - 1)
1874 go k !n = go (AppT (AppT ArrowT StarT) k) (n - 1)
1875+#else
1876+ go k !n = go (ArrowK StarK k) (n - 1)
1877+#endif
1762
1878
1763-- | Makes a string literal expression from a constructor's name.
1879-- | Makes a string literal expression from a constructor's name.
1764conNameExp :: Options -> ConstructorInfo -> Q Exp
1880conNameExp :: Options -> ConstructorInfo -> Q Exp
@@ -1768,11 +1884,11 @@ conNameExp opts = litE
1768 . nameBase
1884 . nameBase
1769 . constructorName
1885 . constructorName
1770
1886
1771−-- | Extracts a record field label.
1887+-- | Creates a string literal expression from a record field label.
1772−fieldLabel :: Options -- ^ Encoding options
1888+fieldLabelExp :: Options -- ^ Encoding options
1773− -> Name
1889+ -> Name
1774− -> String
1890+ -> Q Exp
1775−fieldLabel opts = fieldLabelModifier opts . nameBase
1891+fieldLabelExp opts = litE . stringL . fieldLabelModifier opts . nameBase
1776
1892
1777-- | The name of the outermost 'Value' constructor.
1893-- | The name of the outermost 'Value' constructor.
1778valueConName :: Value -> String
1894valueConName :: Value -> String
@@ -1812,7 +1928,11 @@ canEtaReduce remaining dropped =
1812-------------------------------------------------------------------------------
1928-------------------------------------------------------------------------------
1813
1929
1814applySubstitutionKind :: Map Name Kind -> Type -> Type
1930applySubstitutionKind :: Map Name Kind -> Type -> Type
1931+#if MIN_VERSION_template_haskell(2,8,0)
1815applySubstitutionKind = applySubstitution
1932applySubstitutionKind = applySubstitution
1933+#else
1934+applySubstitutionKind _ t = t
1935+#endif
1816
1936
1817substNameWithKind :: Name -> Kind -> Type -> Type
1937substNameWithKind :: Name -> Kind -> Type -> Type
1818substNameWithKind n k = applySubstitutionKind (M.singleton n k)
1938substNameWithKind n k = applySubstitutionKind (M.singleton n k)
@@ -1901,13 +2021,6 @@ data Direction = To | From
1901-- | A representation of which typeclass method is being spliced in.
2021-- | A representation of which typeclass method is being spliced in.
1902data JSONFun = ToJSON | ToEncoding | ParseJSON
2022data JSONFun = ToJSON | ToEncoding | ParseJSON
1903
2023
1904−-- | A refinement of JSONFun to [ToJSON, ToEncoding].
1905−data ToJSONFun = Value | Encoding
1906−
1907−targetToJSONFun :: ToJSONFun -> JSONFun
1908−targetToJSONFun Value = ToJSON
1909−targetToJSONFun Encoding = ToEncoding
1910−
1911-- | A representation of which typeclass is being derived.
2024-- | A representation of which typeclass is being derived.
1912data JSONClass = JSONClass { direction :: Direction, arity :: Arity }
2025data JSONClass = JSONClass { direction :: Direction, arity :: Arity }
1913
2026
@@ -1976,7 +2089,9 @@ data StarKindStatus = NotKindStar
1976canRealizeKindStar :: Type -> StarKindStatus
2089canRealizeKindStar :: Type -> StarKindStatus
1977canRealizeKindStar t = case t of
2090canRealizeKindStar t = case t of
1978 _ | hasKindStar t -> KindStar
2091 _ | hasKindStar t -> KindStar
2092+#if MIN_VERSION_template_haskell(2,8,0)
1979 SigT _ (VarT k) -> IsKindVar k
2093 SigT _ (VarT k) -> IsKindVar k
2094+#endif
1980 _ -> NotKindStar
2095 _ -> NotKindStar
1981
2096
1982-- | Returns 'Just' the kind variable 'Name' of a 'StarKindStatus' if it exists.
2097-- | Returns 'Just' the kind variable 'Name' of a 'StarKindStatus' if it exists.
Data/Aeson/Text.hs+4−5
@@ -1,5 +1,4 @@
1{-# LANGUAGE BangPatterns #-}
1{-# LANGUAGE BangPatterns #-}
2−{-# LANGUAGE NoImplicitPrelude #-}
3{-# LANGUAGE OverloadedStrings #-}
2{-# LANGUAGE OverloadedStrings #-}
4-- |
3-- |
5-- Module: Data.Aeson.Text
4-- Module: Data.Aeson.Text
@@ -15,18 +14,18 @@
15--
14--
16-- You can use the conversions to 'Builder's when embedding JSON messages as
15-- You can use the conversions to 'Builder's when embedding JSON messages as
17-- parts of a protocol.
16-- parts of a protocol.
18−
19module Data.Aeson.Text
17module Data.Aeson.Text
20 (
18 (
21 encodeToLazyText
19 encodeToLazyText
22 , encodeToTextBuilder
20 , encodeToTextBuilder
23 ) where
21 ) where
24
22
23+import Prelude ()
25import Prelude.Compat
24import Prelude.Compat
26
25
27import Data.Aeson.Types (Value(..), ToJSON(..))
26import Data.Aeson.Types (Value(..), ToJSON(..))
28import Data.Aeson.Encoding (encodingToLazyByteString)
27import Data.Aeson.Encoding (encodingToLazyByteString)
29−import Data.Semigroup ((<>))
28+import Data.Monoid ((<>))
30import Data.Scientific (FPFormat(..), Scientific, base10Exponent)
29import Data.Scientific (FPFormat(..), Scientific, base10Exponent)
31import Data.Text.Lazy.Builder
30import Data.Text.Lazy.Builder
32import Data.Text.Lazy.Builder.Scientific (formatScientificBuilder)
31import Data.Text.Lazy.Builder.Scientific (formatScientificBuilder)
@@ -47,8 +46,8 @@ encodeToLazyText = LT.decodeUtf8 . encodingToLazyByteString . toEncoding
47-- embedded efficiently in a text-based protocol.
46-- embedded efficiently in a text-based protocol.
48--
47--
49-- If you are going to immediately encode straight to a
48-- If you are going to immediately encode straight to a
50−-- 'L.ByteString', it is more efficient to use 'encode' (lazy ByteString)
49+-- 'L.ByteString', it is more efficient to use 'encodeToBuilder'
51−-- or @'fromEncoding' . 'toEncoding'@ (ByteString.Builder) instead.
50+-- instead.
52--
51--
53-- /Note:/ Uses 'toJSON'
52-- /Note:/ Uses 'toJSON'
54encodeToTextBuilder :: ToJSON a => a -> Builder
53encodeToTextBuilder :: ToJSON a => a -> Builder
Data/Aeson/Types.hs+3−39
@@ -1,4 +1,3 @@
1−{-# LANGUAGE NoImplicitPrelude #-}
2-- |
1-- |
3-- Module: Data.Aeson.Types
2-- Module: Data.Aeson.Types
4-- Copyright: (c) 2011-2016 Bryan O'Sullivan
3-- Copyright: (c) 2011-2016 Bryan O'Sullivan
@@ -26,7 +25,6 @@ module Data.Aeson.Types
26 -- * Convenience types and functions
25 -- * Convenience types and functions
27 , DotNetTime(..)
26 , DotNetTime(..)
28 , typeMismatch
27 , typeMismatch
29− , unexpected
30 -- * Type conversion
28 -- * Type conversion
31 , Parser
29 , Parser
32 , Result(..)
30 , Result(..)
@@ -38,7 +36,6 @@ module Data.Aeson.Types
38 , ToJSON(..)
36 , ToJSON(..)
39 , KeyValue(..)
37 , KeyValue(..)
40 , modifyFailure
38 , modifyFailure
41− , prependFailure
42 , parserThrowError
39 , parserThrowError
43 , parserCatchError
40 , parserCatchError
44
41
@@ -53,12 +50,6 @@ module Data.Aeson.Types
53 , coerceFromJSONKeyFunction
50 , coerceFromJSONKeyFunction
54 , mapFromJSONKeyFunction
51 , mapFromJSONKeyFunction
55
52
56− -- *** Generic keys
57− , GToJSONKey()
58− , genericToJSONKey
59− , GFromJSONKey()
60− , genericFromJSONKey
61−
62 -- ** Liftings to unary and binary type constructors
53 -- ** Liftings to unary and binary type constructors
63 , FromJSON1(..)
54 , FromJSON1(..)
64 , parseJSON1
55 , parseJSON1
@@ -90,9 +81,9 @@ module Data.Aeson.Types
90 , withObject
81 , withObject
91 , withText
82 , withText
92 , withArray
83 , withArray
84+ , withNumber
93 , withScientific
85 , withScientific
94 , withBool
86 , withBool
95− , withEmbeddedJSON
96
87
97 , pairs
88 , pairs
98 , foldable
89 , foldable
@@ -113,39 +104,15 @@ module Data.Aeson.Types
113 , listParser
104 , listParser
114
105
115 -- * Generic and TH encoding configuration
106 -- * Generic and TH encoding configuration
116− , Options
107+ , Options(..)
117−
118− -- ** Options fields
119− -- $optionsFields
120− , fieldLabelModifier
121− , constructorTagModifier
122− , allNullaryToStringTag
123− , omitNothingFields
124− , sumEncoding
125− , unwrapUnaryRecords
126− , tagSingleConstructors
127− , rejectUnknownFields
128−
129− -- ** Options utilities
130 , SumEncoding(..)
108 , SumEncoding(..)
131 , camelTo
109 , camelTo
132 , camelTo2
110 , camelTo2
133 , defaultOptions
111 , defaultOptions
134 , defaultTaggedObject
112 , defaultTaggedObject
135−
136− -- ** Options for object keys
137− , JSONKeyOptions
138− , keyModifier
139− , defaultJSONKeyOptions
140−
141− -- * Parsing context
142− , (<?>)
143− , JSONPath
144− , JSONPathElement(..)
145− , formatPath
146− , formatRelativePath
147 ) where
113 ) where
148
114
115+import Prelude ()
149import Prelude.Compat
116import Prelude.Compat
150
117
151import Data.Aeson.Encoding (Encoding, unsafeToEncoding, fromEncoding, Series, pairs)
118import Data.Aeson.Encoding (Encoding, unsafeToEncoding, fromEncoding, Series, pairs)
@@ -157,6 +124,3 @@ import Data.Foldable (toList)
157foldable :: (Foldable t, ToJSON a) => t a -> Encoding
124foldable :: (Foldable t, ToJSON a) => t a -> Encoding
158foldable = toEncoding . toList
125foldable = toEncoding . toList
159{-# INLINE foldable #-}
126{-# INLINE foldable #-}
160−
161−-- $optionsFields
162−-- The functions here are in fact record fields of the 'Options' type.
Data/Aeson/Types/Class.hs+3−8
@@ -4,8 +4,8 @@
4{-# LANGUAGE FlexibleInstances #-}
4{-# LANGUAGE FlexibleInstances #-}
5{-# LANGUAGE GADTs #-}
5{-# LANGUAGE GADTs #-}
6{-# LANGUAGE MultiParamTypeClasses #-}
6{-# LANGUAGE MultiParamTypeClasses #-}
7−{-# LANGUAGE NoImplicitPrelude #-}
8{-# LANGUAGE ScopedTypeVariables #-}
7{-# LANGUAGE ScopedTypeVariables #-}
8+{-# LANGUAGE TypeSynonymInstances #-}
9
9
10-- |
10-- |
11-- Module: Data.Aeson.Types.Class
11-- Module: Data.Aeson.Types.Class
@@ -58,11 +58,6 @@ module Data.Aeson.Types.Class
58 , fromJSONKeyCoerce
58 , fromJSONKeyCoerce
59 , coerceFromJSONKeyFunction
59 , coerceFromJSONKeyFunction
60 , mapFromJSONKeyFunction
60 , mapFromJSONKeyFunction
61− -- ** Generic keys
62− , GToJSONKey()
63− , genericToJSONKey
64− , GFromJSONKey()
65− , genericFromJSONKey
66 -- * Object key-value pairs
61 -- * Object key-value pairs
67 , KeyValue(..)
62 , KeyValue(..)
68
63
@@ -75,15 +70,14 @@ module Data.Aeson.Types.Class
75 , withObject
70 , withObject
76 , withText
71 , withText
77 , withArray
72 , withArray
73+ , withNumber
78 , withScientific
74 , withScientific
79 , withBool
75 , withBool
80− , withEmbeddedJSON
81
76
82 -- * Functions
77 -- * Functions
83 , fromJSON
78 , fromJSON
84 , ifromJSON
79 , ifromJSON
85 , typeMismatch
80 , typeMismatch
86− , unexpected
87 , parseField
81 , parseField
88 , parseFieldMaybe
82 , parseFieldMaybe
89 , parseFieldMaybe'
83 , parseFieldMaybe'
@@ -97,6 +91,7 @@ module Data.Aeson.Types.Class
97 , (.!=)
91 , (.!=)
98 ) where
92 ) where
99
93
94+import Prelude ()
100
95
101import Data.Aeson.Types.FromJSON
96import Data.Aeson.Types.FromJSON
102import Data.Aeson.Types.Generic (One, Zero)
97import Data.Aeson.Types.Generic (One, Zero)
Data/Aeson/Types/FromJSON.hs+412−831
@@ -5,16 +5,19 @@
5{-# LANGUAGE FlexibleInstances #-}
5{-# LANGUAGE FlexibleInstances #-}
6{-# LANGUAGE GADTs #-}
6{-# LANGUAGE GADTs #-}
7{-# LANGUAGE MultiParamTypeClasses #-}
7{-# LANGUAGE MultiParamTypeClasses #-}
8−{-# LANGUAGE NoImplicitPrelude #-}
8+{-# LANGUAGE NamedFieldPuns #-}
9{-# LANGUAGE OverloadedStrings #-}
9{-# LANGUAGE OverloadedStrings #-}
10−{-# LANGUAGE PolyKinds #-}
11{-# LANGUAGE RecordWildCards #-}
10{-# LANGUAGE RecordWildCards #-}
12{-# LANGUAGE ScopedTypeVariables #-}
11{-# LANGUAGE ScopedTypeVariables #-}
13{-# LANGUAGE TypeOperators #-}
12{-# LANGUAGE TypeOperators #-}
14−{-# LANGUAGE TupleSections #-}
13+{-# LANGUAGE TypeSynonymInstances #-}
15{-# LANGUAGE UndecidableInstances #-}
14{-# LANGUAGE UndecidableInstances #-}
16{-# LANGUAGE ViewPatterns #-}
15{-# LANGUAGE ViewPatterns #-}
17
16
17+#if __GLASGOW_HASKELL__ >= 706
18+{-# LANGUAGE PolyKinds #-}
19+#endif
20+
18#include "incoherent-compat.h"
21#include "incoherent-compat.h"
19#include "overlapping-compat.h"
22#include "overlapping-compat.h"
20
23
@@ -42,9 +45,6 @@ module Data.Aeson.Types.FromJSON
42 , coerceFromJSONKeyFunction
45 , coerceFromJSONKeyFunction
43 , mapFromJSONKeyFunction
46 , mapFromJSONKeyFunction
44
47
45− , GFromJSONKey()
46− , genericFromJSONKey
47−
48 -- * List functions
48 -- * List functions
49 , listParser
49 , listParser
50
50
@@ -52,22 +52,20 @@ module Data.Aeson.Types.FromJSON
52 , withObject
52 , withObject
53 , withText
53 , withText
54 , withArray
54 , withArray
55+ , withNumber
55 , withScientific
56 , withScientific
56 , withBool
57 , withBool
57− , withEmbeddedJSON
58
58
59 -- * Functions
59 -- * Functions
60 , fromJSON
60 , fromJSON
61 , ifromJSON
61 , ifromJSON
62 , typeMismatch
62 , typeMismatch
63− , unexpected
64 , parseField
63 , parseField
65 , parseFieldMaybe
64 , parseFieldMaybe
66 , parseFieldMaybe'
65 , parseFieldMaybe'
67 , explicitParseField
66 , explicitParseField
68 , explicitParseFieldMaybe
67 , explicitParseFieldMaybe
69 , explicitParseFieldMaybe'
68 , explicitParseFieldMaybe'
70− , parseIndexedJSON
71 -- ** Operators
69 -- ** Operators
72 , (.:)
70 , (.:)
73 , (.:?)
71 , (.:?)
@@ -78,16 +76,17 @@ module Data.Aeson.Types.FromJSON
78 , parseOptionalFieldWith
76 , parseOptionalFieldWith
79 ) where
77 ) where
80
78
79+import Prelude ()
81import Prelude.Compat
80import Prelude.Compat
82
81
83−import Control.Applicative ((<|>), Const(..), liftA2)
82+import Control.Applicative ((<|>), Const(..))
84−import Control.Monad (zipWithM)
83+import Control.Monad ((<=<), zipWithM)
85import Data.Aeson.Internal.Functions (mapKey)
84import Data.Aeson.Internal.Functions (mapKey)
86−import Data.Aeson.Parser.Internal (eitherDecodeWith, jsonEOF)
87import Data.Aeson.Types.Generic
85import Data.Aeson.Types.Generic
88import Data.Aeson.Types.Internal
86import Data.Aeson.Types.Internal
87+import Data.Attoparsec.Number (Number(..))
89import Data.Bits (unsafeShiftR)
88import Data.Bits (unsafeShiftR)
90−import Data.Fixed (Fixed, HasResolution (resolution), Nano)
89+import Data.Fixed (Fixed, HasResolution)
91import Data.Functor.Compose (Compose(..))
90import Data.Functor.Compose (Compose(..))
92import Data.Functor.Identity (Identity(..))
91import Data.Functor.Identity (Identity(..))
93import Data.Functor.Product (Product(..))
92import Data.Functor.Product (Product(..))
@@ -96,31 +95,26 @@ import Data.Hashable (Hashable(..))
96import Data.Int (Int16, Int32, Int64, Int8)
95import Data.Int (Int16, Int32, Int64, Int8)
97import Data.List.NonEmpty (NonEmpty(..))
96import Data.List.NonEmpty (NonEmpty(..))
98import Data.Maybe (fromMaybe)
97import Data.Maybe (fromMaybe)
99−import Data.Semigroup ((<>))
98+import Data.Monoid ((<>))
100import Data.Proxy (Proxy(..))
99import Data.Proxy (Proxy(..))
101import Data.Ratio ((%), Ratio)
100import Data.Ratio ((%), Ratio)
102−import Data.Scientific (Scientific, base10Exponent)
101+import Data.Scientific (Scientific)
103import Data.Tagged (Tagged(..))
102import Data.Tagged (Tagged(..))
104import Data.Text (Text, pack, unpack)
103import Data.Text (Text, pack, unpack)
105−import Data.Time (Day, DiffTime, LocalTime, NominalDiffTime, TimeOfDay, UTCTime, ZonedTime)
104+import Data.Time (Day, LocalTime, NominalDiffTime, TimeOfDay, UTCTime, ZonedTime)
106−import Data.Time.Calendar.Compat (CalendarDiffDays (..), DayOfWeek (..))
105+import Data.Time.Format (parseTime)
107−import Data.Time.LocalTime.Compat (CalendarDiffTime (..))
108−import Data.Time.Clock.System.Compat (SystemTime (..))
109−import Data.Time.Format.Compat (parseTimeM, defaultTimeLocale)
106+import Data.Time.Locale.Compat (defaultTimeLocale)
110import Data.Traversable as Tr (sequence)
107import Data.Traversable as Tr (sequence)
111import Data.Vector (Vector)
108import Data.Vector (Vector)
112import Data.Version (Version, parseVersion)
109import Data.Version (Version, parseVersion)
113−import Data.Void (Void)
114import Data.Word (Word16, Word32, Word64, Word8)
110import Data.Word (Word16, Word32, Word64, Word8)
115import Foreign.Storable (Storable)
111import Foreign.Storable (Storable)
116−import Foreign.C.Types (CTime (..))
117import GHC.Generics
112import GHC.Generics
118import Numeric.Natural (Natural)
113import Numeric.Natural (Natural)
119import Text.ParserCombinators.ReadP (readP_to_S)
114import Text.ParserCombinators.ReadP (readP_to_S)
120import Unsafe.Coerce (unsafeCoerce)
115import Unsafe.Coerce (unsafeCoerce)
121import qualified Data.Aeson.Parser.Time as Time
116import qualified Data.Aeson.Parser.Time as Time
122import qualified Data.Attoparsec.ByteString.Char8 as A (endOfInput, parseOnly, scientific)
117import qualified Data.Attoparsec.ByteString.Char8 as A (endOfInput, parseOnly, scientific)
123−import qualified Data.ByteString.Lazy as L
124import qualified Data.DList as DList
118import qualified Data.DList as DList
125import qualified Data.HashMap.Strict as H
119import qualified Data.HashMap.Strict as H
126import qualified Data.HashSet as HashSet
120import qualified Data.HashSet as HashSet
@@ -143,20 +137,18 @@ import qualified Data.Vector.Primitive as VP
143import qualified Data.Vector.Storable as VS
137import qualified Data.Vector.Storable as VS
144import qualified Data.Vector.Unboxed as VU
138import qualified Data.Vector.Unboxed as VU
145
139
146−import qualified GHC.Exts as Exts
140+#ifndef HAS_COERCIBLE
147−import qualified Data.Primitive.Array as PM
141+#define HAS_COERCIBLE (__GLASGOW_HASKELL__ >= 707)
148−import qualified Data.Primitive.SmallArray as PM
149−import qualified Data.Primitive.Types as PM
150−
151−#if MIN_VERSION_primitive(0,6,4)
152−#if !MIN_VERSION_primitive(0,7,0)
153−import qualified Data.Primitive.UnliftedArray as PM
154#endif
142#endif
155−import qualified Data.Primitive.PrimArray as PM
156−#endif
157−
158
143
144+#if HAS_COERCIBLE
159import Data.Coerce (Coercible, coerce)
145import Data.Coerce (Coercible, coerce)
146+coerce' :: Coercible a b => a -> b
147+coerce' = coerce
148+#else
149+coerce' :: a -> b
150+coerce' = unsafeCoerce
151+#endif
160
152
161parseIndexedJSON :: (Value -> Parser a) -> Int -> Value -> Parser a
153parseIndexedJSON :: (Value -> Parser a) -> Int -> Value -> Parser a
162parseIndexedJSON p idx value = p value <?> Index idx
154parseIndexedJSON p idx value = p value <?> Index idx
@@ -165,7 +157,7 @@ parseIndexedJSON p idx value = p value <?> Index idx
165parseIndexedJSONPair :: (Value -> Parser a) -> (Value -> Parser b) -> Int -> Value -> Parser (a, b)
157parseIndexedJSONPair :: (Value -> Parser a) -> (Value -> Parser b) -> Int -> Value -> Parser (a, b)
166parseIndexedJSONPair keyParser valParser idx value = p value <?> Index idx
158parseIndexedJSONPair keyParser valParser idx value = p value <?> Index idx
167 where
159 where
168− p = withArray "(k, v)" $ \ab ->
160+ p = withArray "(k,v)" $ \ab ->
169 let n = V.length ab
161 let n = V.length ab
170 in if n == 2
162 in if n == 2
171 then (,) <$> parseJSONElemAtIndex keyParser 0 ab
163 then (,) <$> parseJSONElemAtIndex keyParser 0 ab
@@ -177,34 +169,43 @@ parseIndexedJSONPair keyParser valParser idx value = p value <?> Index idx
177parseJSONElemAtIndex :: (Value -> Parser a) -> Int -> V.Vector Value -> Parser a
169parseJSONElemAtIndex :: (Value -> Parser a) -> Int -> V.Vector Value -> Parser a
178parseJSONElemAtIndex p idx ary = p (V.unsafeIndex ary idx) <?> Index idx
170parseJSONElemAtIndex p idx ary = p (V.unsafeIndex ary idx) <?> Index idx
179
171
172+scientificToNumber :: Scientific -> Number
173+scientificToNumber s
174+ | e < 0 = D $ Scientific.toRealFloat s
175+ | otherwise = I $ c * 10 ^ e
176+ where
177+ e = Scientific.base10Exponent s
178+ c = Scientific.coefficient s
179+{-# INLINE scientificToNumber #-}
180+
180parseRealFloat :: RealFloat a => String -> Value -> Parser a
181parseRealFloat :: RealFloat a => String -> Value -> Parser a
181−parseRealFloat _ (Number s) = pure $ Scientific.toRealFloat s
182+parseRealFloat _ (Number s) = pure $ Scientific.toRealFloat s
182−parseRealFloat _ Null = pure (0/0)
183+parseRealFloat _ Null = pure (0/0)
183−parseRealFloat name v = prependContext name (unexpected v)
184+parseRealFloat expected v = typeMismatch expected v
184{-# INLINE parseRealFloat #-}
185{-# INLINE parseRealFloat #-}
185
186
186−parseIntegralFromScientific :: forall a. Integral a => Scientific -> Parser a
187+parseIntegralFromScientific :: forall a. Integral a => String -> Scientific -> Parser a
187−parseIntegralFromScientific s =
188+parseIntegralFromScientific expected s =
188 case Scientific.floatingOrInteger s :: Either Double a of
189 case Scientific.floatingOrInteger s :: Either Double a of
189 Right x -> pure x
190 Right x -> pure x
190− Left _ -> fail $ "unexpected floating number " ++ show s
191+ Left _ -> fail $ "Floating number specified for " ++ expected ++ ": " ++ show s
191{-# INLINE parseIntegralFromScientific #-}
192{-# INLINE parseIntegralFromScientific #-}
192
193
193parseIntegral :: Integral a => String -> Value -> Parser a
194parseIntegral :: Integral a => String -> Value -> Parser a
194−parseIntegral name =
195+parseIntegral expected =
195− prependContext name . withBoundedScientific' parseIntegralFromScientific
196+ withScientific expected $ parseIntegralFromScientific expected
196{-# INLINE parseIntegral #-}
197{-# INLINE parseIntegral #-}
197
198
198−parseBoundedIntegralFromScientific :: (Bounded a, Integral a) => Scientific -> Parser a
199+parseBoundedIntegralFromScientific :: (Bounded a, Integral a) => String -> Scientific -> Parser a
199−parseBoundedIntegralFromScientific s = maybe
200+parseBoundedIntegralFromScientific expected s = maybe
200− (fail $ "value is either floating or will cause over or underflow " ++ show s)
201+ (fail $ expected ++ " is either floating or will cause over or underflow: " ++ show s)
201 pure
202 pure
202 (Scientific.toBoundedInteger s)
203 (Scientific.toBoundedInteger s)
203{-# INLINE parseBoundedIntegralFromScientific #-}
204{-# INLINE parseBoundedIntegralFromScientific #-}
204
205
205parseBoundedIntegral :: (Bounded a, Integral a) => String -> Value -> Parser a
206parseBoundedIntegral :: (Bounded a, Integral a) => String -> Value -> Parser a
206−parseBoundedIntegral name =
207+parseBoundedIntegral expected =
207− prependContext name . withScientific' parseBoundedIntegralFromScientific
208+ withScientific expected $ parseBoundedIntegralFromScientific expected
208{-# INLINE parseBoundedIntegral #-}
209{-# INLINE parseBoundedIntegral #-}
209
210
210parseScientificText :: Text -> Parser Scientific
211parseScientificText :: Text -> Parser Scientific
@@ -214,20 +215,13 @@ parseScientificText
214 . T.encodeUtf8
215 . T.encodeUtf8
215
216
216parseIntegralText :: Integral a => String -> Text -> Parser a
217parseIntegralText :: Integral a => String -> Text -> Parser a
217−parseIntegralText name t =
218+parseIntegralText expected t =
218− prependContext name $
219− parseScientificText t
220− >>= rejectLargeExponent
221− >>= parseIntegralFromScientific
219+ parseScientificText t >>= parseIntegralFromScientific expected
222− where
223− rejectLargeExponent :: Scientific -> Parser Scientific
224− rejectLargeExponent s = withBoundedScientific' pure (Number s)
225{-# INLINE parseIntegralText #-}
220{-# INLINE parseIntegralText #-}
226
221
227parseBoundedIntegralText :: (Bounded a, Integral a) => String -> Text -> Parser a
222parseBoundedIntegralText :: (Bounded a, Integral a) => String -> Text -> Parser a
228−parseBoundedIntegralText name t =
223+parseBoundedIntegralText expected t =
229− prependContext name $
230− parseScientificText t >>= parseBoundedIntegralFromScientific
224+ parseScientificText t >>= parseBoundedIntegralFromScientific expected
231
225
232parseOptionalFieldWith :: (Value -> Parser (Maybe a))
226parseOptionalFieldWith :: (Value -> Parser (Maybe a))
233 -> Object -> Text -> Parser (Maybe a)
227 -> Object -> Text -> Parser (Maybe a)
@@ -287,21 +281,14 @@ genericLiftParseJSON opts pj pjl = fmap to1 . gParseJSON opts (From1Args pj pjl)
287--
281--
288-- The basic ways to signal a failed conversion are as follows:
282-- The basic ways to signal a failed conversion are as follows:
289--
283--
290−-- * 'fail' yields a custom error message: it is the recommended way of
284+-- * 'empty' and 'mzero' work, but are terse and uninformative;
291−-- reporting a failure;
292--
285--
293−-- * 'Control.Applicative.empty' (or 'Control.Monad.mzero') is uninformative:
286+-- * 'fail' yields a custom error message;
294−-- use it when the error is meant to be caught by some @('<|>')@;
295--
287--
296−-- * 'typeMismatch' can be used to report a failure when the encountered value
288+-- * 'typeMismatch' produces an informative message for cases when the
297−-- is not of the expected JSON type; 'unexpected' is an appropriate alternative
289+-- value encountered is not of the expected type.
298−-- when more than one type may be expected, or to keep the expected type
299−-- implicit.
300--
290--
301−-- 'prependFailure' (or 'modifyFailure') add more information to a parser's
302−-- error messages.
303−--
304−-- An example type and instance using 'typeMismatch' and 'prependFailure':
291+-- An example type and instance using 'typeMismatch':
305--
292--
306-- @
293-- @
307-- \-- Allow ourselves to write 'Text' literals.
294-- \-- Allow ourselves to write 'Text' literals.
@@ -315,22 +302,20 @@ genericLiftParseJSON opts pj pjl = fmap to1 . gParseJSON opts (From1Args pj pjl)
315-- '<*>' v '.:' \"y\"
302-- '<*>' v '.:' \"y\"
316--
303--
317-- \-- We do not expect a non-'Object' value here.
304-- \-- We do not expect a non-'Object' value here.
318−-- \-- We could use 'Control.Applicative.empty' to fail, but 'typeMismatch'
305+-- \-- We could use 'mzero' to fail, but 'typeMismatch'
319-- \-- gives a much more informative error message.
306-- \-- gives a much more informative error message.
320−-- 'parseJSON' invalid =
307+-- 'parseJSON' invalid = 'typeMismatch' \"Coord\" invalid
321−-- 'prependFailure' "parsing Coord failed, "
322−-- ('typeMismatch' \"Object\" invalid)
323-- @
308-- @
324--
309--
325-- For this common case of only being concerned with a single
310-- For this common case of only being concerned with a single
326−-- type of JSON value, the functions 'withObject', 'withScientific', etc.
311+-- type of JSON value, the functions 'withObject', 'withNumber', etc.
327-- are provided. Their use is to be preferred when possible, since
312-- are provided. Their use is to be preferred when possible, since
328-- they are more terse. Using 'withObject', we can rewrite the above instance
313-- they are more terse. Using 'withObject', we can rewrite the above instance
329-- (assuming the same language extension and data type) as:
314-- (assuming the same language extension and data type) as:
330--
315--
331-- @
316-- @
332-- instance 'FromJSON' Coord where
317-- instance 'FromJSON' Coord where
333−-- 'parseJSON' = 'withObject' \"Coord\" $ \\v -> Coord
318+-- 'parseJSON' = 'withObject' \"Coord\" $ \v -> Coord
334-- '<$>' v '.:' \"x\"
319-- '<$>' v '.:' \"x\"
335-- '<*>' v '.:' \"y\"
320-- '<*>' v '.:' \"y\"
336-- @
321-- @
@@ -362,7 +347,7 @@ genericLiftParseJSON opts pj pjl = fmap to1 . gParseJSON opts (From1Args pj pjl)
362-- @
347-- @
363--
348--
364-- The default implementation will be equivalent to
349-- The default implementation will be equivalent to
365−-- @parseJSON = 'genericParseJSON' 'defaultOptions'@; if you need different
350+-- @parseJSON = 'genericParseJSON' 'defaultOptions'@; If you need different
366-- options, you can customize the generic decoding by defining:
351-- options, you can customize the generic decoding by defining:
367--
352--
368-- @
353-- @
@@ -380,11 +365,13 @@ class FromJSON a where
380 parseJSON = genericParseJSON defaultOptions
365 parseJSON = genericParseJSON defaultOptions
381
366
382 parseJSONList :: Value -> Parser [a]
367 parseJSONList :: Value -> Parser [a]
383− parseJSONList = withArray "[]" $ \a ->
368+ parseJSONList (Array a)
384− zipWithM (parseIndexedJSON parseJSON) [0..]
369+ = zipWithM (parseIndexedJSON parseJSON) [0..]
385 . V.toList
370 . V.toList
386 $ a
371 $ a
387
372
373+ parseJSONList v = typeMismatch "[a]" v
374+
388-------------------------------------------------------------------------------
375-------------------------------------------------------------------------------
389-- Classes and types for map keys
376-- Classes and types for map keys
390-------------------------------------------------------------------------------
377-------------------------------------------------------------------------------
@@ -396,16 +383,6 @@ class FromJSON a where
396-- > newtype SomeId = SomeId { getSomeId :: Text }
383-- > newtype SomeId = SomeId { getSomeId :: Text }
397-- > deriving (Eq,Ord,Hashable,FromJSONKey)
384-- > deriving (Eq,Ord,Hashable,FromJSONKey)
398--
385--
399−-- If you have a sum of nullary constructors, you may use the generic
400−-- implementation:
401−--
402−-- @
403−-- data Color = Red | Green | Blue
404−-- deriving Generic
405−--
406−-- instance 'FromJSONKey' Color where
407−-- 'fromJSONKey' = 'genericFromJSONKey' 'defaultJSONKeyOptions'
408−-- @
409class FromJSONKey a where
386class FromJSONKey a where
410 -- | Strategy for parsing the key of a map-like container.
387 -- | Strategy for parsing the key of a map-like container.
411 fromJSONKey :: FromJSONKeyFunction a
388 fromJSONKey :: FromJSONKeyFunction a
@@ -428,7 +405,11 @@ class FromJSONKey a where
428-- At the moment this type is intentionally not exported. 'FromJSONKeyFunction'
405-- At the moment this type is intentionally not exported. 'FromJSONKeyFunction'
429-- can be inspected, but cannot be constructed.
406-- can be inspected, but cannot be constructed.
430data CoerceText a where
407data CoerceText a where
408+#if HAS_COERCIBLE
431 CoerceText :: Coercible Text a => CoerceText a
409 CoerceText :: Coercible Text a => CoerceText a
410+#else
411+ CoerceText :: CoerceText a
412+#endif
432
413
433-- | This type is related to 'ToJSONKeyFunction'. If 'FromJSONKeyValue' is used in the
414-- | This type is related to 'ToJSONKeyFunction'. If 'FromJSONKeyValue' is used in the
434-- 'FromJSONKey' instance, then 'ToJSONKeyValue' should be used in the 'ToJSONKey'
415-- 'FromJSONKey' instance, then 'ToJSONKeyValue' should be used in the 'ToJSONKey'
@@ -448,7 +429,7 @@ data FromJSONKeyFunction a
448
429
449-- | Only law abiding up to interpretation
430-- | Only law abiding up to interpretation
450instance Functor FromJSONKeyFunction where
431instance Functor FromJSONKeyFunction where
451− fmap h (FromJSONKeyCoerce CoerceText) = FromJSONKeyText (h . coerce)
432+ fmap h (FromJSONKeyCoerce CoerceText) = FromJSONKeyText (h . coerce')
452 fmap h (FromJSONKeyText f) = FromJSONKeyText (h . f)
433 fmap h (FromJSONKeyText f) = FromJSONKeyText (h . f)
453 fmap h (FromJSONKeyTextParser f) = FromJSONKeyTextParser (fmap h . f)
434 fmap h (FromJSONKeyTextParser f) = FromJSONKeyTextParser (fmap h . f)
454 fmap h (FromJSONKeyValue f) = FromJSONKeyValue (fmap h . f)
435 fmap h (FromJSONKeyValue f) = FromJSONKeyValue (fmap h . f)
@@ -462,7 +443,9 @@ instance Functor FromJSONKeyFunction where
462--
443--
463-- On pre GHC 7.8 this is unconstrainted function.
444-- On pre GHC 7.8 this is unconstrainted function.
464fromJSONKeyCoerce ::
445fromJSONKeyCoerce ::
446+#if HAS_COERCIBLE
465 Coercible Text a =>
447 Coercible Text a =>
448+#endif
466 FromJSONKeyFunction a
449 FromJSONKeyFunction a
467fromJSONKeyCoerce = FromJSONKeyCoerce CoerceText
450fromJSONKeyCoerce = FromJSONKeyCoerce CoerceText
468
451
@@ -470,91 +453,60 @@ fromJSONKeyCoerce = FromJSONKeyCoerce CoerceText
470--
453--
471-- See note on 'fromJSONKeyCoerce'.
454-- See note on 'fromJSONKeyCoerce'.
472coerceFromJSONKeyFunction ::
455coerceFromJSONKeyFunction ::
456+#if HAS_COERCIBLE
473 Coercible a b =>
457 Coercible a b =>
458+#endif
474 FromJSONKeyFunction a -> FromJSONKeyFunction b
459 FromJSONKeyFunction a -> FromJSONKeyFunction b
460+#if HAS_COERCIBLE
475coerceFromJSONKeyFunction = coerce
461coerceFromJSONKeyFunction = coerce
462+#else
463+coerceFromJSONKeyFunction (FromJSONKeyCoerce CoerceText) = FromJSONKeyCoerce CoerceText
464+coerceFromJSONKeyFunction (FromJSONKeyText f) = FromJSONKeyText (coerce' . f)
465+coerceFromJSONKeyFunction (FromJSONKeyTextParser f) = FromJSONKeyTextParser (fmap coerce' . f)
466+coerceFromJSONKeyFunction (FromJSONKeyValue f) = FromJSONKeyValue (fmap coerce' . f)
467+#endif
476
468
477{-# RULES
469{-# RULES
478 "FromJSONKeyCoerce: fmap id" forall (x :: FromJSONKeyFunction a).
470 "FromJSONKeyCoerce: fmap id" forall (x :: FromJSONKeyFunction a).
479 fmap id x = x
471 fmap id x = x
480 #-}
472 #-}
473+#if HAS_COERCIBLE
481{-# RULES
474{-# RULES
482 "FromJSONKeyCoerce: fmap coerce" forall x .
475 "FromJSONKeyCoerce: fmap coerce" forall x .
483 fmap coerce x = coerceFromJSONKeyFunction x
476 fmap coerce x = coerceFromJSONKeyFunction x
484 #-}
477 #-}
478+#endif
485
479
486-- | Same as 'fmap'. Provided for the consistency with 'ToJSONKeyFunction'.
480-- | Same as 'fmap'. Provided for the consistency with 'ToJSONKeyFunction'.
487mapFromJSONKeyFunction :: (a -> b) -> FromJSONKeyFunction a -> FromJSONKeyFunction b
481mapFromJSONKeyFunction :: (a -> b) -> FromJSONKeyFunction a -> FromJSONKeyFunction b
488mapFromJSONKeyFunction = fmap
482mapFromJSONKeyFunction = fmap
489
483
490−-- | 'fromJSONKey' for 'Generic' types.
491−-- These types must be sums of nullary constructors, whose names will be used
492−-- as keys for JSON objects.
493−--
494−-- See also 'genericToJSONKey'.
495−--
496−-- === __Example__
497−--
498−-- @
499−-- data Color = Red | Green | Blue
500−-- deriving 'Generic'
501−--
502−-- instance 'FromJSONKey' Color where
503−-- 'fromJSONKey' = 'genericFromJSONKey' 'defaultJSONKeyOptions'
504−-- @
505−genericFromJSONKey :: forall a. (Generic a, GFromJSONKey (Rep a))
506− => JSONKeyOptions
507− -> FromJSONKeyFunction a
508−genericFromJSONKey opts = FromJSONKeyTextParser $ \t ->
509− case parseSumFromString (keyModifier opts) t of
510− Nothing -> fail $
511− "invalid key " ++ show t ++ ", expected one of " ++ show cnames
512− Just k -> pure (to k)
513− where
514− cnames = unTagged2 (constructorTags (keyModifier opts) :: Tagged2 (Rep a) [String])
515−
516−class (ConstructorNames f, SumFromString f) => GFromJSONKey f where
517−instance (ConstructorNames f, SumFromString f) => GFromJSONKey f where
518−
519-------------------------------------------------------------------------------
484-------------------------------------------------------------------------------
520-- Functions needed for documentation
485-- Functions needed for documentation
521-------------------------------------------------------------------------------
486-------------------------------------------------------------------------------
522
487
523-- | Fail parsing due to a type mismatch, with a descriptive message.
488-- | Fail parsing due to a type mismatch, with a descriptive message.
524--
489--
525−-- The following wrappers should generally be prefered:
490+-- Example usage:
526−-- 'withObject', 'withArray', 'withText', 'withBool'.
527−--
528−-- ==== Error message example
529--
491--
530−-- > typeMismatch "Object" (String "oops")
492+-- @
531−-- > -- Error: "expected Object, but encountered String"
493+-- instance FromJSON Coord where
494+-- parseJSON ('Object' v) = {- type matches, life is good -}
495+-- parseJSON wat = 'typeMismatch' \"Coord\" wat
496+-- @
532−typeMismatch :: String -- ^ The name of the JSON type being parsed
497+typeMismatch :: String -- ^ The name of the type you are trying to parse.
533− -- (@\"Object\"@, @\"Array\"@, @\"String\"@, @\"Number\"@,
534− -- @\"Boolean\"@, or @\"Null\"@).
535 -> Value -- ^ The actual value encountered.
498 -> Value -- ^ The actual value encountered.
536 -> Parser a
499 -> Parser a
537typeMismatch expected actual =
500typeMismatch expected actual =
538− fail $ "expected " ++ expected ++ ", but encountered " ++ typeOf actual
501+ fail $ "expected " ++ expected ++ ", encountered " ++ name
539−
502+ where
540−-- | Fail parsing due to a type mismatch, when the expected types are implicit.
541−--
542−-- ==== Error message example
543−--
544−-- > unexpected (String "oops")
545−-- > -- Error: "unexpected String"
546−unexpected :: Value -> Parser a
547−unexpected actual = fail $ "unexpected " ++ typeOf actual
548−
549−-- | JSON type of a value, name of the head constructor.
550−typeOf :: Value -> String
551−typeOf v = case v of
503+ name = case actual of
552− Object _ -> "Object"
504+ Object _ -> "Object"
553− Array _ -> "Array"
505+ Array _ -> "Array"
554− String _ -> "String"
506+ String _ -> "String"
555− Number _ -> "Number"
507+ Number _ -> "Number"
556− Bool _ -> "Boolean"
508+ Bool _ -> "Boolean"
557− Null -> "Null"
509+ Null -> "Null"
558
510
559-------------------------------------------------------------------------------
511-------------------------------------------------------------------------------
560-- Lifings of FromJSON and ToJSON to unary and binary type constructors
512-- Lifings of FromJSON and ToJSON to unary and binary type constructors
@@ -635,8 +587,9 @@ class FromJSON2 f where
635 -> (Value -> Parser b)
587 -> (Value -> Parser b)
636 -> (Value -> Parser [b])
588 -> (Value -> Parser [b])
637 -> Value -> Parser [f a b]
589 -> Value -> Parser [f a b]
638− liftParseJSONList2 fa ga fb gb = withArray "[]" $ \vals ->
590+ liftParseJSONList2 fa ga fb gb v = case v of
639− fmap V.toList (V.mapM (liftParseJSON2 fa ga fb gb) vals)
591+ Array vals -> fmap V.toList (V.mapM (liftParseJSON2 fa ga fb gb) vals)
592+ _ -> typeMismatch "[a]" v
640
593
641-- | Lift the standard 'parseJSON' function through the type constructor.
594-- | Lift the standard 'parseJSON' function through the type constructor.
642parseJSON2 :: (FromJSON2 f, FromJSON a, FromJSON b) => Value -> Parser (f a b)
595parseJSON2 :: (FromJSON2 f, FromJSON a, FromJSON b) => Value -> Parser (f a b)
@@ -650,7 +603,7 @@ parseJSON2 = liftParseJSON2 parseJSON parseJSONList parseJSON parseJSONList
650-- | Helper function to use with 'liftParseJSON'. See 'Data.Aeson.ToJSON.listEncoding'.
603-- | Helper function to use with 'liftParseJSON'. See 'Data.Aeson.ToJSON.listEncoding'.
651listParser :: (Value -> Parser a) -> Value -> Parser [a]
604listParser :: (Value -> Parser a) -> Value -> Parser [a]
652listParser f (Array xs) = fmap V.toList (V.mapM f xs)
605listParser f (Array xs) = fmap V.toList (V.mapM f xs)
653−listParser _ v = typeMismatch "Array" v
606+listParser _ v = typeMismatch "[a]" v
654{-# INLINE listParser #-}
607{-# INLINE listParser #-}
655
608
656-------------------------------------------------------------------------------
609-------------------------------------------------------------------------------
@@ -668,139 +621,49 @@ instance (FromJSON a) => FromJSON [a] where
668-- Functions
621-- Functions
669-------------------------------------------------------------------------------
622-------------------------------------------------------------------------------
670
623
671−-- | Add context to a failure message, indicating the name of the structure
672−-- being parsed.
673−--
674−-- > prependContext "MyType" (fail "[error message]")
675−-- > -- Error: "parsing MyType failed, [error message]"
676−prependContext :: String -> Parser a -> Parser a
677−prependContext name = prependFailure ("parsing " ++ name ++ " failed, ")
678−
679−-- | @'withObject' name f value@ applies @f@ to the 'Object' when @value@
624+-- | @'withObject' expected f value@ applies @f@ to the 'Object' when @value@
680−-- is an 'Data.Aeson.Object' and fails otherwise.
625+-- is an 'Object' and fails using @'typeMismatch' expected@ otherwise.
681−--
682−-- ==== Error message example
683−--
684−-- > withObject "MyType" f (String "oops")
685−-- > -- Error: "parsing MyType failed, expected Object, but encountered String"
686withObject :: String -> (Object -> Parser a) -> Value -> Parser a
626withObject :: String -> (Object -> Parser a) -> Value -> Parser a
687−withObject _ f (Object obj) = f obj
627+withObject _ f (Object obj) = f obj
688−withObject name _ v = prependContext name (typeMismatch "Object" v)
628+withObject expected _ v = typeMismatch expected v
689{-# INLINE withObject #-}
629{-# INLINE withObject #-}
690
630
691−-- | @'withText' name f value@ applies @f@ to the 'Text' when @value@ is a
631+-- | @'withText' expected f value@ applies @f@ to the 'Text' when @value@ is a
692−-- 'Data.Aeson.String' and fails otherwise.
632+-- 'String' and fails using @'typeMismatch' expected@ otherwise.
693−--
694−-- ==== Error message example
695−--
696−-- > withText "MyType" f Null
697−-- > -- Error: "parsing MyType failed, expected String, but encountered Null"
698withText :: String -> (Text -> Parser a) -> Value -> Parser a
633withText :: String -> (Text -> Parser a) -> Value -> Parser a
699−withText _ f (String txt) = f txt
634+withText _ f (String txt) = f txt
700−withText name _ v = prependContext name (typeMismatch "String" v)
635+withText expected _ v = typeMismatch expected v
701{-# INLINE withText #-}
636{-# INLINE withText #-}
702
637
703-- | @'withArray' expected f value@ applies @f@ to the 'Array' when @value@ is
638-- | @'withArray' expected f value@ applies @f@ to the 'Array' when @value@ is
704−-- an 'Data.Aeson.Array' and fails otherwise.
639+-- an 'Array' and fails using @'typeMismatch' expected@ otherwise.
705−--
706−-- ==== Error message example
707−--
708−-- > withArray "MyType" f (String "oops")
709−-- > -- Error: "parsing MyType failed, expected Array, but encountered String"
710withArray :: String -> (Array -> Parser a) -> Value -> Parser a
640withArray :: String -> (Array -> Parser a) -> Value -> Parser a
711−withArray _ f (Array arr) = f arr
641+withArray _ f (Array arr) = f arr
712−withArray name _ v = prependContext name (typeMismatch "Array" v)
642+withArray expected _ v = typeMismatch expected v
713{-# INLINE withArray #-}
643{-# INLINE withArray #-}
714
644
645+-- | @'withNumber' expected f value@ applies @f@ to the 'Number' when @value@
646+-- is a 'Number' and fails using @'typeMismatch' expected@ otherwise.
647+withNumber :: String -> (Number -> Parser a) -> Value -> Parser a
648+withNumber expected f = withScientific expected (f . scientificToNumber)
649+{-# INLINE withNumber #-}
650+{-# DEPRECATED withNumber "Use withScientific instead" #-}
651+
715−-- | @'withScientific' name f value@ applies @f@ to the 'Scientific' number
652+-- | @'withScientific' expected f value@ applies @f@ to the 'Scientific' number
716−-- when @value@ is a 'Data.Aeson.Number' and fails using 'typeMismatch'
653+-- when @value@ is a 'Number' and fails using @'typeMismatch' expected@
717-- otherwise.
654-- otherwise.
718−--
719−-- /Warning/: If you are converting from a scientific to an unbounded
720−-- type such as 'Integer' you may want to add a restriction on the
721−-- size of the exponent (see 'withBoundedScientific') to prevent
722−-- malicious input from filling up the memory of the target system.
723−--
724−-- ==== Error message example
725−--
726−-- > withScientific "MyType" f (String "oops")
727−-- > -- Error: "parsing MyType failed, expected Number, but encountered String"
728withScientific :: String -> (Scientific -> Parser a) -> Value -> Parser a
655withScientific :: String -> (Scientific -> Parser a) -> Value -> Parser a
729−withScientific _ f (Number scientific) = f scientific
656+withScientific _ f (Number scientific) = f scientific
730−withScientific name _ v = prependContext name (typeMismatch "Number" v)
657+withScientific expected _ v = typeMismatch expected v
731{-# INLINE withScientific #-}
658{-# INLINE withScientific #-}
732
659
733−-- | A variant of 'withScientific' which doesn't use 'prependContext', so that
734−-- such context can be added separately in a way that also applies when the
735−-- continuation @f :: Scientific -> Parser a@ fails.
736−--
737−-- /Warning/: If you are converting from a scientific to an unbounded
738−-- type such as 'Integer' you may want to add a restriction on the
739−-- size of the exponent (see 'withBoundedScientific') to prevent
740−-- malicious input from filling up the memory of the target system.
741−--
742−-- ==== Error message examples
743−--
744−-- > withScientific' f (String "oops")
745−-- > -- Error: "unexpected String"
746−-- >
747−-- > prependContext "MyType" (withScientific' f (String "oops"))
748−-- > -- Error: "parsing MyType failed, unexpected String"
749−withScientific' :: (Scientific -> Parser a) -> Value -> Parser a
750−withScientific' f v = case v of
751− Number n -> f n
752− _ -> typeMismatch "Number" v
753−{-# INLINE withScientific' #-}
754−
755−-- | @'withBoundedScientific' name f value@ applies @f@ to the 'Scientific' number
756−-- when @value@ is a 'Number' with exponent less than or equal to 1024.
757−withBoundedScientific :: String -> (Scientific -> Parser a) -> Value -> Parser a
758−withBoundedScientific name f v = withBoundedScientific_ (prependContext name) f v
759−{-# INLINE withBoundedScientific #-}
760−
761−-- | A variant of 'withBoundedScientific' which doesn't use 'prependContext',
762−-- so that such context can be added separately in a way that also applies
763−-- when the continuation @f :: Scientific -> Parser a@ fails.
764−withBoundedScientific' :: (Scientific -> Parser a) -> Value -> Parser a
765−withBoundedScientific' f v = withBoundedScientific_ id f v
766−{-# INLINE withBoundedScientific' #-}
767−
768−-- | A variant of 'withBoundedScientific_' parameterized by a function to apply
769−-- to the 'Parser' in case of failure.
770−withBoundedScientific_ :: (Parser a -> Parser a) -> (Scientific -> Parser a) -> Value -> Parser a
771−withBoundedScientific_ whenFail f (Number scientific) =
772− if exp10 > 1024
773− then whenFail (fail msg)
774− else f scientific
775− where
776− exp10 = base10Exponent scientific
777− msg = "found a number with exponent " ++ show exp10 ++ ", but it must not be greater than 1024"
778−withBoundedScientific_ whenFail _ v =
779− whenFail (typeMismatch "Number" v)
780−{-# INLINE withBoundedScientific_ #-}
781−
782-- | @'withBool' expected f value@ applies @f@ to the 'Bool' when @value@ is a
660-- | @'withBool' expected f value@ applies @f@ to the 'Bool' when @value@ is a
783−-- 'Boolean' and fails otherwise.
661+-- 'Bool' and fails using @'typeMismatch' expected@ otherwise.
784−--
785−-- ==== Error message example
786−--
787−-- > withBool "MyType" f (String "oops")
788−-- > -- Error: "parsing MyType failed, expected Boolean, but encountered String"
789withBool :: String -> (Bool -> Parser a) -> Value -> Parser a
662withBool :: String -> (Bool -> Parser a) -> Value -> Parser a
790−withBool _ f (Bool arr) = f arr
663+withBool _ f (Bool arr) = f arr
791−withBool name _ v = prependContext name (typeMismatch "Boolean" v)
664+withBool expected _ v = typeMismatch expected v
792{-# INLINE withBool #-}
665{-# INLINE withBool #-}
793
666
794−-- | Decode a nested JSON-encoded string.
795−withEmbeddedJSON :: String -> (Value -> Parser a) -> Value -> Parser a
796−withEmbeddedJSON _ innerParser (String txt) =
797− either fail innerParser $ eitherDecode (L.fromStrict $ T.encodeUtf8 txt)
798− where
799− eitherDecode = eitherFormatError . eitherDecodeWith jsonEOF ifromJSON
800− eitherFormatError = either (Left . uncurry formatError) Right
801−withEmbeddedJSON name _ v = prependContext name (typeMismatch "String" v)
802−{-# INLINE withEmbeddedJSON #-}
803−
804-- | Convert a value from JSON, failing if the types do not match.
667-- | Convert a value from JSON, failing if the types do not match.
805fromJSON :: (FromJSON a) => Value -> Result a
668fromJSON :: (FromJSON a) => Value -> Result a
806fromJSON = parse parseJSON
669fromJSON = parse parseJSON
@@ -863,7 +726,7 @@ parseFieldMaybe' = (.:!)
863-- E.g. @'explicitParseField' 'parseJSON1' :: ('FromJSON1' f, 'FromJSON' a) -> 'Object' -> 'Text' -> 'Parser' (f a)@
726-- E.g. @'explicitParseField' 'parseJSON1' :: ('FromJSON1' f, 'FromJSON' a) -> 'Object' -> 'Text' -> 'Parser' (f a)@
864explicitParseField :: (Value -> Parser a) -> Object -> Text -> Parser a
727explicitParseField :: (Value -> Parser a) -> Object -> Text -> Parser a
865explicitParseField p obj key = case H.lookup key obj of
728explicitParseField p obj key = case H.lookup key obj of
866− Nothing -> fail $ "key " ++ show key ++ " not found"
729+ Nothing -> fail $ "key " ++ show key ++ " not present"
867 Just v -> p v <?> Key key
730 Just v -> p v <?> Key key
868{-# INLINE explicitParseField #-}
731{-# INLINE explicitParseField #-}
869
732
@@ -903,47 +766,11 @@ pmval .!= val = fromMaybe val <$> pmval
903-- Generic parseJSON
766-- Generic parseJSON
904-------------------------------------------------------------------------------
767-------------------------------------------------------------------------------
905
768
906−instance GFromJSON arity V1 where
907− -- Whereof we cannot format, thereof we cannot parse:
908− gParseJSON _ _ _ = fail "Attempted to parse empty type"
909−
910−
911instance OVERLAPPABLE_ (GFromJSON arity a) => GFromJSON arity (M1 i c a) where
769instance OVERLAPPABLE_ (GFromJSON arity a) => GFromJSON arity (M1 i c a) where
912 -- Meta-information, which is not handled elsewhere, is just added to the
770 -- Meta-information, which is not handled elsewhere, is just added to the
913 -- parsed value:
771 -- parsed value:
914 gParseJSON opts fargs = fmap M1 . gParseJSON opts fargs
772 gParseJSON opts fargs = fmap M1 . gParseJSON opts fargs
915
773
916−-- Information for error messages
917−
918−type TypeName = String
919−type ConName = String
920−
921−-- | Add the name of the type being parsed to a parser's error messages.
922−contextType :: TypeName -> Parser a -> Parser a
923−contextType = prependContext
924−
925−-- | Add the tagKey that will be looked up while building an ADT
926−-- | Produce the error equivalent to
927−-- | Left "Error in $: parsing T failed, expected an object with keys "tag" and
928−-- | "contents", where "tag" i-- |s associated to one of ["Foo", "Bar"],
929−-- | The parser returned error was: could not find key "tag"
930−contextTag :: Text -> [String] -> Parser a -> Parser a
931−contextTag tagKey cnames = prependFailure
932− ("expected Object with key \"" ++ unpack tagKey ++ "\"" ++
933− " containing one of " ++ show cnames ++ ", ")
934−
935−-- | Add the name of the constructor being parsed to a parser's error messages.
936−contextCons :: ConName -> TypeName -> Parser a -> Parser a
937−contextCons cname tname = prependContext (showCons cname tname)
938−
939−-- | Render a constructor as @\"MyType(MyConstructor)\"@.
940−showCons :: ConName -> TypeName -> String
941−showCons cname tname = tname ++ "(" ++ cname ++ ")"
942−
943−--------------------------------------------------------------------------------
944−
945−-- Parsing single fields
946−
947instance (FromJSON a) => GFromJSON arity (K1 i a) where
774instance (FromJSON a) => GFromJSON arity (K1 i a) where
948 -- Constant values are decoded using their FromJSON instance:
775 -- Constant values are decoded using their FromJSON instance:
949 gParseJSON _opts _ = fmap K1 . parseJSON
776 gParseJSON _opts _ = fmap K1 . parseJSON
@@ -958,499 +785,339 @@ instance (FromJSON1 f) => GFromJSON One (Rec1 f) where
958 -- FromJSON1 instance:
785 -- FromJSON1 instance:
959 gParseJSON _opts (From1Args pj pjl) = fmap Rec1 . liftParseJSON pj pjl
786 gParseJSON _opts (From1Args pj pjl) = fmap Rec1 . liftParseJSON pj pjl
960
787
961−instance (FromJSON1 f, GFromJSON One g) => GFromJSON One (f :.: g) where
788+instance GFromJSON arity U1 where
962− -- If an occurrence of the last type parameter is nested inside two
789+ -- Empty constructors are expected to be encoded as an empty array:
963− -- composed types, it is decoded by using the outermost type's FromJSON1
964− -- instance to generically decode the innermost type:
965− gParseJSON opts fargs =
790+ gParseJSON _opts _ v
966− let gpj = gParseJSON opts fargs in
791+ | isEmptyArray v = pure U1
967− fmap Comp1 . liftParseJSON gpj (listParser gpj)
792+ | otherwise = typeMismatch "unit constructor (U1)" v
968−
969−--------------------------------------------------------------------------------
970−
971−instance (GFromJSON' arity a, Datatype d) => GFromJSON arity (D1 d a) where
972− -- Meta-information, which is not handled elsewhere, is just added to the
973− -- parsed value:
974− gParseJSON opts fargs = fmap M1 . gParseJSON' (tname :* opts :* fargs)
975− where
976− tname = moduleName proxy ++ "." ++ datatypeName proxy
977− proxy = undefined :: M1 _i d _f _p
978−
979−-- | 'GFromJSON', after unwrapping the 'D1' constructor, now carrying the data
980−-- type's name.
981−class GFromJSON' arity f where
982− gParseJSON' :: TypeName :* Options :* FromArgs arity a
983− -> Value
984− -> Parser (f a)
985
793
986−-- | Single constructor.
987instance ( ConsFromJSON arity a
794instance ( ConsFromJSON arity a
988 , AllNullary (C1 c a) allNullary
795 , AllNullary (C1 c a) allNullary
989 , ParseSum arity (C1 c a) allNullary
796 , ParseSum arity (C1 c a) allNullary
990− , Constructor c
991− ) => GFromJSON' arity (C1 c a) where
797+ ) => GFromJSON arity (D1 d (C1 c a)) where
992 -- The option 'tagSingleConstructors' determines whether to wrap
798 -- The option 'tagSingleConstructors' determines whether to wrap
993 -- a single-constructor type.
799 -- a single-constructor type.
994− gParseJSON' p@(_ :* opts :* _)
800+ gParseJSON opts fargs
995 | tagSingleConstructors opts
801 | tagSingleConstructors opts
802+ = fmap M1
996− = (unTagged :: Tagged allNullary (Parser (C1 c a p)) -> Parser (C1 c a p))
803+ . (unTagged :: Tagged allNullary (Parser (C1 c a p)) -> Parser (C1 c a p))
997− . parseSum p
804+ . parseSum opts fargs
998− | otherwise = fmap M1 . consParseJSON (cname :* p)
805+ | otherwise = fmap M1 . fmap M1 . consParseJSON opts fargs
999− where
806+
1000− cname = conName (undefined :: M1 _i c _f _p)
807+instance (ConsFromJSON arity a) => GFromJSON arity (C1 c a) where
1001−
808+ -- Constructors need to be decoded differently depending on whether they're
1002−-- | Multiple constructors.
809+ -- a record or not. This distinction is made by consParseJSON:
810+ gParseJSON opts fargs = fmap M1 . consParseJSON opts fargs
811+
812+instance ( FromProduct arity a, FromProduct arity b
813+ , ProductSize a, ProductSize b
814+ ) => GFromJSON arity (a :*: b) where
815+ -- Products are expected to be encoded to an array. Here we check whether we
816+ -- got an array of the same size as the product, then parse each of the
817+ -- product's elements using parseProduct:
818+ gParseJSON opts fargs = withArray "product (:*:)" $ \arr ->
819+ let lenArray = V.length arr
820+ lenProduct = (unTagged2 :: Tagged2 (a :*: b) Int -> Int)
821+ productSize in
822+ if lenArray == lenProduct
823+ then parseProduct opts fargs arr 0 lenProduct
824+ else fail $ "When expecting a product of " ++ show lenProduct ++
825+ " values, encountered an Array of " ++ show lenArray ++
826+ " elements instead"
827+
1003−instance ( AllNullary (a :+: b) allNullary
828+instance ( AllNullary (a :+: b) allNullary
1004− , ParseSum arity (a :+: b) allNullary
829+ , ParseSum arity (a :+: b) allNullary
1005− ) => GFromJSON' arity (a :+: b) where
830+ ) => GFromJSON arity (a :+: b) where
1006 -- If all constructors of a sum datatype are nullary and the
831 -- If all constructors of a sum datatype are nullary and the
1007 -- 'allNullaryToStringTag' option is set they are expected to be
832 -- 'allNullaryToStringTag' option is set they are expected to be
1008 -- encoded as strings. This distinction is made by 'parseSum':
833 -- encoded as strings. This distinction is made by 'parseSum':
1009− gParseJSON' p =
834+ gParseJSON opts fargs =
1010− (unTagged :: Tagged allNullary (Parser ((a :+: b) _d)) ->
835+ (unTagged :: Tagged allNullary (Parser ((a :+: b) d)) ->
1011− Parser ((a :+: b) _d))
836+ Parser ((a :+: b) d))
1012− . parseSum p
837+ . parseSum opts fargs
838+
839+instance (FromJSON1 f, GFromJSON One g) => GFromJSON One (f :.: g) where
840+ -- If an occurrence of the last type parameter is nested inside two
841+ -- composed types, it is decoded by using the outermost type's FromJSON1
842+ -- instance to generically decode the innermost type:
843+ gParseJSON opts fargs =
844+ let gpj = gParseJSON opts fargs in
845+ fmap Comp1 . liftParseJSON gpj (listParser gpj)
1013
846
1014--------------------------------------------------------------------------------
847--------------------------------------------------------------------------------
1015
848
1016class ParseSum arity f allNullary where
849class ParseSum arity f allNullary where
1017− parseSum :: TypeName :* Options :* FromArgs arity a
850+ parseSum :: Options -> FromArgs arity a
1018− -> Value
1019− -> Tagged allNullary (Parser (f a))
851+ -> Value -> Tagged allNullary (Parser (f a))
1020
852
1021−instance ( ConstructorNames f
1022− , SumFromString f
853+instance ( SumFromString f
1023 , FromPair arity f
854 , FromPair arity f
1024 , FromTaggedObject arity f
855 , FromTaggedObject arity f
1025 , FromUntaggedValue arity f
856 , FromUntaggedValue arity f
1026 ) => ParseSum arity f True where
857 ) => ParseSum arity f True where
1027− parseSum p@(tname :* opts :* _)
858+ parseSum opts fargs
1028− | allNullaryToStringTag opts = Tagged . parseAllNullarySum tname opts
859+ | allNullaryToStringTag opts = Tagged . parseAllNullarySum opts
1029− | otherwise = Tagged . parseNonAllNullarySum p
860+ | otherwise = Tagged . parseNonAllNullarySum opts fargs
1030
861
1031−instance ( ConstructorNames f
1032− , FromPair arity f
862+instance ( FromPair arity f
1033 , FromTaggedObject arity f
863 , FromTaggedObject arity f
1034 , FromUntaggedValue arity f
864 , FromUntaggedValue arity f
1035 ) => ParseSum arity f False where
865 ) => ParseSum arity f False where
1036− parseSum p = Tagged . parseNonAllNullarySum p
866+ parseSum opts fargs = Tagged . parseNonAllNullarySum opts fargs
1037
867
1038--------------------------------------------------------------------------------
868--------------------------------------------------------------------------------
1039
869
1040−parseAllNullarySum :: (SumFromString f, ConstructorNames f)
870+parseAllNullarySum :: SumFromString f => Options -> Value -> Parser (f a)
1041− => TypeName -> Options -> Value -> Parser (f a)
1042−parseAllNullarySum tname opts =
871+parseAllNullarySum opts = withText "Text" $ \key ->
1043− withText tname $ \tag ->
1044− maybe (badTag tag) return $
872+ maybe (notFound key) return $
1045− parseSumFromString modifier tag
873+ parseSumFromString opts key
1046− where
1047− badTag tag = failWithCTags tname modifier $ \cnames ->
1048− "expected one of the tags " ++ show cnames ++
1049− ", but found tag " ++ show tag
1050− modifier = constructorTagModifier opts
1051−
1052−-- | Fail with an informative error message about a mismatched tag.
1053−-- The error message is parameterized by the list of expected tags,
1054−-- to be inferred from the result type of the parser.
1055−failWithCTags
1056− :: forall f a t. ConstructorNames f
1057− => TypeName -> (String -> t) -> ([t] -> String) -> Parser (f a)
1058−failWithCTags tname modifier f =
1059− contextType tname . fail $ f cnames
1060− where
1061− cnames = unTagged2 (constructorTags modifier :: Tagged2 f [t])
1062
874
1063class SumFromString f where
875class SumFromString f where
1064− parseSumFromString :: (String -> String) -> Text -> Maybe (f a)
876+ parseSumFromString :: Options -> Text -> Maybe (f a)
1065
877
1066instance (SumFromString a, SumFromString b) => SumFromString (a :+: b) where
878instance (SumFromString a, SumFromString b) => SumFromString (a :+: b) where
1067 parseSumFromString opts key = (L1 <$> parseSumFromString opts key) <|>
879 parseSumFromString opts key = (L1 <$> parseSumFromString opts key) <|>
1068 (R1 <$> parseSumFromString opts key)
880 (R1 <$> parseSumFromString opts key)
1069
881
1070instance (Constructor c) => SumFromString (C1 c U1) where
882instance (Constructor c) => SumFromString (C1 c U1) where
1071− parseSumFromString modifier key
883+ parseSumFromString opts key | key == name = Just $ M1 U1
1072− | key == name = Just $ M1 U1
1073− | otherwise = Nothing
884+ | otherwise = Nothing
1074− where
885+ where
1075− name = pack $ modifier $ conName (undefined :: M1 _i c _f _p)
886+ name = pack $ constructorTagModifier opts $
1076−
1077−-- For genericFromJSONKey
1078−instance SumFromString a => SumFromString (D1 d a) where
1079− parseSumFromString modifier key = M1 <$> parseSumFromString modifier key
1080−
1081−-- | List of all constructor tags.
1082−constructorTags :: ConstructorNames a => (String -> t) -> Tagged2 a [t]
1083−constructorTags modifier =
1084− fmap DList.toList (constructorNames' modifier)
1085−
1086−-- | List of all constructor names of an ADT, after a given conversion
1087−-- function. (Better inlining.)
1088−class ConstructorNames a where
1089− constructorNames' :: (String -> t) -> Tagged2 a (DList.DList t)
1090−
1091−instance (ConstructorNames a, ConstructorNames b) => ConstructorNames (a :+: b) where
1092− constructorNames' = liftA2 append constructorNames' constructorNames'
1093− where
1094− append
1095− :: Tagged2 a (DList.DList t)
1096− -> Tagged2 b (DList.DList t)
1097− -> Tagged2 (a :+: b) (DList.DList t)
1098− append (Tagged2 xs) (Tagged2 ys) = Tagged2 (DList.append xs ys)
1099−
1100−instance Constructor c => ConstructorNames (C1 c a) where
1101− constructorNames' f = Tagged2 (pure (f cname))
1102− where
1103− cname = conName (undefined :: M1 _i c _f _p)
887+ conName (undefined :: t c U1 p)
1104−
1105−-- For genericFromJSONKey
1106−instance ConstructorNames a => ConstructorNames (D1 d a) where
1107− constructorNames' = retag . constructorNames'
1108− where
1109− retag :: Tagged2 a u -> Tagged2 (D1 d a) u
1110− retag (Tagged2 x) = Tagged2 x
1111
888
1112--------------------------------------------------------------------------------
889--------------------------------------------------------------------------------
890+
1113−parseNonAllNullarySum :: forall f c arity.
891+parseNonAllNullarySum :: ( FromPair arity f
1114− ( FromPair arity f
1115 , FromTaggedObject arity f
892 , FromTaggedObject arity f
1116 , FromUntaggedValue arity f
893 , FromUntaggedValue arity f
1117− , ConstructorNames f
1118− ) => TypeName :* Options :* FromArgs arity c
894+ ) => Options -> FromArgs arity c
1119 -> Value -> Parser (f c)
895 -> Value -> Parser (f c)
1120−parseNonAllNullarySum p@(tname :* opts :* _) =
896+parseNonAllNullarySum opts fargs =
1121 case sumEncoding opts of
897 case sumEncoding opts of
1122 TaggedObject{..} ->
898 TaggedObject{..} ->
1123− withObject tname $ \obj -> do
899+ withObject "Object" $ \obj -> do
1124− tag <- contextType tname . contextTag tagKey cnames_ $ obj .: tagKey
1125− fromMaybe (badTag tag <?> Key tagKey) $
1126− parseFromTaggedObject (tag :* contentsFieldName :* p) obj
1127− where
1128− tagKey = pack tagFieldName
900+ tag <- obj .: pack tagFieldName
1129− badTag tag = failWith_ $ \cnames ->
901+ fromMaybe (notFound tag) $
1130− "expected tag field to be one of " ++ show cnames ++
902+ parseFromTaggedObject opts fargs contentsFieldName obj tag
1131− ", but found tag " ++ show tag
1132− cnames_ = unTagged2 (constructorTags (constructorTagModifier opts) :: Tagged2 f [String])
1133
903
1134 ObjectWithSingleField ->
904 ObjectWithSingleField ->
905+ withObject "Object" $ \obj ->
1135− withObject tname $ \obj -> case H.toList obj of
906+ case H.toList obj of
1136− [(tag, v)] -> maybe (badTag tag) (<?> Key tag) $
907+ [pair@(tag, _)] -> fromMaybe (notFound tag) $
1137− parsePair (tag :* p) v
908+ parsePair opts fargs pair
1138− _ -> contextType tname . fail $
909+ _ -> fail "Object doesn't have a single field"
1139− "expected an Object with a single pair, but found " ++
1140− show (H.size obj) ++ " pairs"
1141− where
1142− badTag tag = failWith_ $ \cnames ->
1143− "expected an Object with a single pair where the tag is one of " ++
1144− show cnames ++ ", but found tag " ++ show tag
1145
910
1146 TwoElemArray ->
911 TwoElemArray ->
1147− withArray tname $ \arr -> case V.length arr of
912+ withArray "Array" $ \arr ->
913+ if V.length arr == 2
1148− 2 | String tag <- V.unsafeIndex arr 0 ->
914+ then case V.unsafeIndex arr 0 of
1149− maybe (badTag tag <?> Index 0) (<?> Index 1) $
915+ String tag -> fromMaybe (notFound tag) $
1150− parsePair (tag :* p) (V.unsafeIndex arr 1)
916+ parsePair opts fargs (tag, V.unsafeIndex arr 1)
1151− | otherwise ->
1152− contextType tname $
1153− fail "tag element is not a String" <?> Index 0
917+ _ -> fail "First element is not a String"
1154− len -> contextType tname . fail $
918+ else fail "Array doesn't have 2 elements"
1155− "expected a 2-element Array, but encountered an Array of length " ++
1156− show len
1157− where
1158− badTag tag = failWith_ $ \cnames ->
1159− "expected tag of the 2-element Array to be one of " ++
1160− show cnames ++ ", but found tag " ++ show tag
1161
919
1162− UntaggedValue -> parseUntaggedValue p
920+ UntaggedValue -> parseUntaggedValue opts fargs
1163− where
1164− failWith_ = failWithCTags tname (constructorTagModifier opts)
1165
921
1166--------------------------------------------------------------------------------
922--------------------------------------------------------------------------------
1167
923
1168class FromTaggedObject arity f where
924class FromTaggedObject arity f where
1169− -- The first two components of the parameter tuple are: the constructor tag
1170− -- to match against, and the contents field name.
1171− parseFromTaggedObject
925+ parseFromTaggedObject :: Options -> FromArgs arity a
1172− :: Text :* String :* TypeName :* Options :* FromArgs arity a
1173− -> Object
926+ -> String -> Object
1174− -> Maybe (Parser (f a))
927+ -> Text -> Maybe (Parser (f a))
1175
928
1176instance ( FromTaggedObject arity a, FromTaggedObject arity b) =>
929instance ( FromTaggedObject arity a, FromTaggedObject arity b) =>
1177 FromTaggedObject arity (a :+: b) where
930 FromTaggedObject arity (a :+: b) where
1178− parseFromTaggedObject p obj =
931+ parseFromTaggedObject opts fargs contentsFieldName obj tag =
1179− (fmap L1 <$> parseFromTaggedObject p obj) <|>
932+ (fmap L1 <$> parseFromTaggedObject opts fargs contentsFieldName obj tag) <|>
1180− (fmap R1 <$> parseFromTaggedObject p obj)
933+ (fmap R1 <$> parseFromTaggedObject opts fargs contentsFieldName obj tag)
1181
934
1182−instance ( IsRecord f isRecord
1183− , FromTaggedObject' arity f isRecord
935+instance ( FromTaggedObject' arity f
1184 , Constructor c
936 , Constructor c
1185 ) => FromTaggedObject arity (C1 c f) where
937 ) => FromTaggedObject arity (C1 c f) where
1186− parseFromTaggedObject (tag :* contentsFieldName :* p@(_ :* opts :* _))
938+ parseFromTaggedObject opts fargs contentsFieldName obj tag
1187− | tag == tag'
1188− = Just . fmap M1 .
1189− (unTagged :: Tagged isRecord (Parser (f a)) -> Parser (f a)) .
1190− parseFromTaggedObject' (contentsFieldName :* cname :* p)
939+ | tag == name = Just $ M1 <$> parseFromTaggedObject'
940+ opts fargs contentsFieldName obj
1191− | otherwise = const Nothing
941+ | otherwise = Nothing
1192− where
942+ where
1193− tag' = pack $ constructorTagModifier opts cname
943+ name = pack $ constructorTagModifier opts $
1194− cname = conName (undefined :: M1 _i c _f _p)
944+ conName (undefined :: t c f p)
1195
945
1196--------------------------------------------------------------------------------
946--------------------------------------------------------------------------------
1197
947
1198−class FromTaggedObject' arity f isRecord where
948+class FromTaggedObject' arity f where
1199− -- The first component of the parameter tuple is the contents field name.
949+ parseFromTaggedObject' :: Options -> FromArgs arity a -> String
1200− parseFromTaggedObject'
950+ -> Object -> Parser (f a)
951+
952+class FromTaggedObject'' arity f isRecord where
1201− :: String :* ConName :* TypeName :* Options :* FromArgs arity a
953+ parseFromTaggedObject'' :: Options -> FromArgs arity a -> String
1202− -> Object -> Tagged isRecord (Parser (f a))
954+ -> Object -> Tagged isRecord (Parser (f a))
1203−
955+
956+instance ( IsRecord f isRecord
957+ , FromTaggedObject'' arity f isRecord
1204−instance (RecordFromJSON arity f, FieldNames f) => FromTaggedObject' arity f True where
958+ ) => FromTaggedObject' arity f where
1205− -- Records are unpacked in the tagged object
959+ parseFromTaggedObject' opts fargs contentsFieldName =
1206− parseFromTaggedObject' (_ :* p) = Tagged . recordParseJSON (True :* p)
960+ (unTagged :: Tagged isRecord (Parser (f a)) -> Parser (f a)) .
1207−
961+ parseFromTaggedObject'' opts fargs contentsFieldName
962+
1208−instance (ConsFromJSON arity f) => FromTaggedObject' arity f False where
963+instance (FromRecord arity f) => FromTaggedObject'' arity f True where
1209− -- Nonnullary nonrecords are encoded in the contents field
1210− parseFromTaggedObject' p obj = Tagged $ do
964+ parseFromTaggedObject'' opts fargs _ =
1211− contents <- contextCons cname tname (obj .: key)
965+ Tagged . parseRecord opts fargs Nothing
1212− consParseJSON p' contents <?> Key key
1213− where
1214− key = pack contentsFieldName
1215− contentsFieldName :* p'@(cname :* tname :* _) = p
1216
966
967+instance (GFromJSON arity f) => FromTaggedObject'' arity f False where
968+ parseFromTaggedObject'' opts fargs contentsFieldName = Tagged .
969+ (gParseJSON opts fargs <=< (.: pack contentsFieldName))
970+
1217−instance OVERLAPPING_ FromTaggedObject' arity U1 False where
971+instance OVERLAPPING_ FromTaggedObject'' arity U1 False where
1218− -- Nullary constructors don't need a contents field
972+ parseFromTaggedObject'' _ _ _ _ = Tagged (pure U1)
1219− parseFromTaggedObject' _ _ = Tagged (pure U1)
1220
973
1221--------------------------------------------------------------------------------
974--------------------------------------------------------------------------------
1222
975
1223−-- | Constructors need to be decoded differently depending on whether they're
1224−-- a record or not. This distinction is made by 'ConsParseJSON'.
1225class ConsFromJSON arity f where
976class ConsFromJSON arity f where
1226− consParseJSON
1227− :: ConName :* TypeName :* Options :* FromArgs arity a
977+ consParseJSON :: Options -> FromArgs arity a
1228− -> Value -> Parser (f a)
978+ -> Value -> Parser (f a)
1229
979
1230class ConsFromJSON' arity f isRecord where
980class ConsFromJSON' arity f isRecord where
1231− consParseJSON'
1232− :: ConName :* TypeName :* Options :* FromArgs arity a
981+ consParseJSON' :: Options -> FromArgs arity a
982+ -> Maybe Text -- ^ A dummy label
983+ -- (Nothing to use proper label)
1233− -> Value -> Tagged isRecord (Parser (f a))
984+ -> Value -> Tagged isRecord (Parser (f a))
1234
985
1235instance ( IsRecord f isRecord
986instance ( IsRecord f isRecord
1236 , ConsFromJSON' arity f isRecord
987 , ConsFromJSON' arity f isRecord
1237 ) => ConsFromJSON arity f where
988 ) => ConsFromJSON arity f where
1238− consParseJSON p =
989+ consParseJSON opts fargs v = let
990+ (v2,lab) = case (unwrapUnaryRecords opts,isUnary (undefined :: f a)) of
991+ -- use a dummy object with a dummy label
992+ (True,True) -> (object [(pack "dummy",v)], Just $ pack "dummy")
993+ _ ->(v,Nothing)
1239− (unTagged :: Tagged isRecord (Parser (f a)) -> Parser (f a))
994+ in (unTagged :: Tagged isRecord (Parser (f a)) -> Parser (f a))
1240− . consParseJSON' p
995+ $ consParseJSON' opts fargs lab v2
1241
996
1242−instance OVERLAPPING_
1243− ( GFromJSON arity a, RecordFromJSON arity (S1 s a)
1244− ) => ConsFromJSON' arity (S1 s a) True where
1245− consParseJSON' p@(cname :* tname :* opts :* fargs)
1246− | unwrapUnaryRecords opts = Tagged . fmap M1 . gParseJSON opts fargs
1247− | otherwise = Tagged . withObject (showCons cname tname) (recordParseJSON (False :* p))
1248
997
1249−instance RecordFromJSON arity f => ConsFromJSON' arity f True where
998+instance (FromRecord arity f) => ConsFromJSON' arity f True where
1250− consParseJSON' p@(cname :* tname :* _) =
999+ consParseJSON' opts fargs mlab = Tagged . withObject "record (:*:)"
1251− Tagged . withObject (showCons cname tname) (recordParseJSON (False :* p))
1000+ (parseRecord opts fargs mlab)
1252
1001
1253−instance OVERLAPPING_
1254− ConsFromJSON' arity U1 False where
1255− -- Empty constructors are expected to be encoded as an empty array:
1256− consParseJSON' (cname :* tname :* _) v =
1257− Tagged . contextCons cname tname $ case v of
1258− Array a | V.null a -> pure U1
1259− | otherwise -> fail_ a
1260− _ -> typeMismatch "Array" v
1261− where
1262− fail_ a = fail $
1263− "expected an empty Array, but encountered an Array of length " ++
1264− show (V.length a)
1265−
1266−instance OVERLAPPING_
1267− GFromJSON arity f => ConsFromJSON' arity (S1 s f) False where
1002+instance (GFromJSON arity f) => ConsFromJSON' arity f False where
1268− consParseJSON' (_ :* _ :* opts :* fargs) =
1269− Tagged . fmap M1 . gParseJSON opts fargs
1003+ consParseJSON' opts fargs _ = Tagged . gParseJSON opts fargs
1270−
1271−instance (ProductFromJSON arity f, ProductSize f
1272− ) => ConsFromJSON' arity f False where
1273− consParseJSON' p = Tagged . productParseJSON0 p
1274
1004
1275--------------------------------------------------------------------------------
1005--------------------------------------------------------------------------------
1276
1006
1277−class FieldNames f where
1007+class FromRecord arity f where
1278− fieldNames :: f a -> [Text] -> [Text]
1008+ parseRecord :: Options -> FromArgs arity a
1279−
1009+ -> Maybe Text -- ^ A dummy label
1280−instance (FieldNames a, FieldNames b) => FieldNames (a :*: b) where
1010+ -- (Nothing to use proper label)
1281− fieldNames _ =
1011+ -> Object -> Parser (f a)
1282− fieldNames (undefined :: a x) .
1283− fieldNames (undefined :: b y)
1284
1012
1285−instance (Selector s) => FieldNames (S1 s f) where
1013+instance ( FromRecord arity a
1286− fieldNames _ = (pack (selName (undefined :: M1 _i s _f _p)) :)
1014+ , FromRecord arity b
1287−
1288−class RecordFromJSON arity f where
1289− recordParseJSON
1290− :: Bool :* ConName :* TypeName :* Options :* FromArgs arity a
1291− -> Object -> Parser (f a)
1292−
1293−instance ( FieldNames f
1294− , RecordFromJSON' arity f
1295− ) => RecordFromJSON arity f where
1296− recordParseJSON (fromTaggedSum :* p@(cname :* tname :* opts :* _)) =
1297− \obj -> checkUnknown obj >> recordParseJSON' p obj
1298− where
1299− knownFields :: H.HashMap Text ()
1300− knownFields = H.fromList $ map (,()) $
1301− fieldNames (undefined :: f a)
1302− [pack (tagFieldName (sumEncoding opts)) | fromTaggedSum]
1303− checkUnknown =
1304− if not (rejectUnknownFields opts)
1305− then \_ -> return ()
1306− else \obj -> case H.keys (H.difference obj knownFields) of
1307− [] -> return ()
1308− unknownFields -> contextCons cname tname $
1309− fail ("unknown fields: " ++ show unknownFields)
1310−
1311−class RecordFromJSON' arity f where
1312− recordParseJSON'
1313− :: ConName :* TypeName :* Options :* FromArgs arity a
1314− -> Object -> Parser (f a)
1315−
1316−instance ( RecordFromJSON' arity a
1317− , RecordFromJSON' arity b
1318− ) => RecordFromJSON' arity (a :*: b) where
1015+ ) => FromRecord arity (a :*: b) where
1319− recordParseJSON' p obj =
1016+ parseRecord opts fargs _ obj =
1320− (:*:) <$> recordParseJSON' p obj
1017+ (:*:) <$> parseRecord opts fargs Nothing obj
1321− <*> recordParseJSON' p obj
1018+ <*> parseRecord opts fargs Nothing obj
1322
1019
1323instance OVERLAPPABLE_ (Selector s, GFromJSON arity a) =>
1020instance OVERLAPPABLE_ (Selector s, GFromJSON arity a) =>
1324− RecordFromJSON' arity (S1 s a) where
1021+ FromRecord arity (S1 s a) where
1325− recordParseJSON' (cname :* tname :* opts :* fargs) obj = do
1022+ parseRecord opts fargs lab =
1326− fv <- contextCons cname tname (obj .: label)
1023+ (<?> Key label) . gParseJSON opts fargs <=< (.: label)
1327− M1 <$> gParseJSON opts fargs fv <?> Key label
1024+ where
1328− where
1025+ label = fromMaybe defLabel lab
1329− label = pack $ fieldLabelModifier opts sname
1330− sname = selName (undefined :: M1 _i s _f _p)
1331−
1332−instance INCOHERENT_ (Selector s, FromJSON a) =>
1333− RecordFromJSON' arity (S1 s (K1 i (Maybe a))) where
1334− recordParseJSON' (_ :* _ :* opts :* _) obj = M1 . K1 <$> obj .:? pack label
1335− where
1336− label = fieldLabelModifier opts sname
1026+ defLabel = pack . fieldLabelModifier opts $
1337− sname = selName (undefined :: M1 _i s _f _p)
1027+ selName (undefined :: t s a p)
1338
1028
1339−-- Parse an Option like a Maybe.
1340instance INCOHERENT_ (Selector s, FromJSON a) =>
1029instance INCOHERENT_ (Selector s, FromJSON a) =>
1341− RecordFromJSON' arity (S1 s (K1 i (Semigroup.Option a))) where
1030+ FromRecord arity (S1 s (K1 i (Maybe a))) where
1342− recordParseJSON' p obj = wrap <$> recordParseJSON' p obj
1031+ parseRecord _ _ (Just lab) obj = (M1 . K1) <$> obj .:? lab
1032+ parseRecord opts _ Nothing obj = (M1 . K1) <$> obj .:? pack label
1343− where
1033+ where
1344− wrap :: S1 s (K1 i (Maybe a)) p -> S1 s (K1 i (Semigroup.Option a)) p
1034+ label = fieldLabelModifier opts $
1345− wrap (M1 (K1 a)) = M1 (K1 (Semigroup.Option a))
1035+ selName (undefined :: t s (K1 i (Maybe a)) p)
1346
1036
1347--------------------------------------------------------------------------------
1037--------------------------------------------------------------------------------
1348
1038
1349−productParseJSON0
1350− :: forall f arity a. (ProductFromJSON arity f, ProductSize f)
1351− => ConName :* TypeName :* Options :* FromArgs arity a
1352− -> Value -> Parser (f a)
1353− -- Products are expected to be encoded to an array. Here we check whether we
1354− -- got an array of the same size as the product, then parse each of the
1355− -- product's elements using productParseJSON:
1356−productParseJSON0 p@(cname :* tname :* _ :* _) =
1357− withArray (showCons cname tname) $ \arr ->
1358− let lenArray = V.length arr
1359− lenProduct = (unTagged2 :: Tagged2 f Int -> Int)
1360− productSize in
1361− if lenArray == lenProduct
1362− then productParseJSON p arr 0 lenProduct
1363− else contextCons cname tname $
1364− fail $ "expected an Array of length " ++ show lenProduct ++
1365− ", but encountered an Array of length " ++ show lenArray
1366−
1367−--
1368−
1369−class ProductFromJSON arity f where
1039+class FromProduct arity f where
1370− productParseJSON :: ConName :* TypeName :* Options :* FromArgs arity a
1040+ parseProduct :: Options -> FromArgs arity a
1371 -> Array -> Int -> Int
1041 -> Array -> Int -> Int
1372 -> Parser (f a)
1042 -> Parser (f a)
1373
1043
1374−instance ( ProductFromJSON arity a
1044+instance ( FromProduct arity a
1375− , ProductFromJSON arity b
1045+ , FromProduct arity b
1376− ) => ProductFromJSON arity (a :*: b) where
1046+ ) => FromProduct arity (a :*: b) where
1377− productParseJSON p arr ix len =
1047+ parseProduct opts fargs arr ix len =
1378− (:*:) <$> productParseJSON p arr ix lenL
1048+ (:*:) <$> parseProduct opts fargs arr ix lenL
1379− <*> productParseJSON p arr ixR lenR
1049+ <*> parseProduct opts fargs arr ixR lenR
1380 where
1050 where
1381 lenL = len `unsafeShiftR` 1
1051 lenL = len `unsafeShiftR` 1
1382 ixR = ix + lenL
1052 ixR = ix + lenL
1383 lenR = len - lenL
1053 lenR = len - lenL
1384
1054
1385−instance (GFromJSON arity a) => ProductFromJSON arity (S1 s a) where
1055+instance (GFromJSON arity a) => FromProduct arity (S1 s a) where
1386− productParseJSON (_ :* _ :* opts :* fargs) arr ix _ =
1056+ parseProduct opts fargs arr ix _ =
1387− M1 <$> gParseJSON opts fargs (V.unsafeIndex arr ix) <?> Index ix
1057+ gParseJSON opts fargs $ V.unsafeIndex arr ix
1388
1058
1389--------------------------------------------------------------------------------
1059--------------------------------------------------------------------------------
1390
1060
1391class FromPair arity f where
1061class FromPair arity f where
1392− -- The first component of the parameter tuple is the tag to match.
1393− parsePair :: Text :* TypeName :* Options :* FromArgs arity a
1062+ parsePair :: Options -> FromArgs arity a
1394− -> Value
1395− -> Maybe (Parser (f a))
1063+ -> Pair -> Maybe (Parser (f a))
1396
1064
1397instance ( FromPair arity a
1065instance ( FromPair arity a
1398 , FromPair arity b
1066 , FromPair arity b
1399 ) => FromPair arity (a :+: b) where
1067 ) => FromPair arity (a :+: b) where
1400− parsePair p pair =
1401− (fmap L1 <$> parsePair p pair) <|>
1068+ parsePair opts fargs pair = (fmap L1 <$> parsePair opts fargs pair) <|>
1402− (fmap R1 <$> parsePair p pair)
1069+ (fmap R1 <$> parsePair opts fargs pair)
1403
1070
1404instance ( Constructor c
1071instance ( Constructor c
1072+ , GFromJSON arity a
1405 , ConsFromJSON arity a
1073 , ConsFromJSON arity a
1406 ) => FromPair arity (C1 c a) where
1074 ) => FromPair arity (C1 c a) where
1407− parsePair (tag :* p@(_ :* opts :* _)) v
1075+ parsePair opts fargs (tag, value)
1408− | tag == tag' = Just $ M1 <$> consParseJSON (cname :* p) v
1076+ | tag == tag' = Just $ gParseJSON opts fargs value
1409 | otherwise = Nothing
1077 | otherwise = Nothing
1410− where
1078+ where
1411− tag' = pack $ constructorTagModifier opts cname
1079+ tag' = pack $ constructorTagModifier opts $
1412− cname = conName (undefined :: M1 _i c _a _p)
1080+ conName (undefined :: t c a p)
1413
1081
1414--------------------------------------------------------------------------------
1082--------------------------------------------------------------------------------
1415
1083
1416class FromUntaggedValue arity f where
1084class FromUntaggedValue arity f where
1417− parseUntaggedValue :: TypeName :* Options :* FromArgs arity a
1085+ parseUntaggedValue :: Options -> FromArgs arity a
1418− -> Value
1419− -> Parser (f a)
1086+ -> Value -> Parser (f a)
1420
1087
1421instance
1088instance
1422 ( FromUntaggedValue arity a
1089 ( FromUntaggedValue arity a
1423 , FromUntaggedValue arity b
1090 , FromUntaggedValue arity b
1424 ) => FromUntaggedValue arity (a :+: b)
1091 ) => FromUntaggedValue arity (a :+: b)
1425 where
1092 where
1426− parseUntaggedValue p value =
1093+ parseUntaggedValue opts fargs value =
1427− L1 <$> parseUntaggedValue p value <|>
1094+ L1 <$> parseUntaggedValue opts fargs value <|>
1428− R1 <$> parseUntaggedValue p value
1095+ R1 <$> parseUntaggedValue opts fargs value
1429
1096
1430instance OVERLAPPABLE_
1097instance OVERLAPPABLE_
1431− ( ConsFromJSON arity a
1098+ ( GFromJSON arity a
1432− , Constructor c
1099+ , ConsFromJSON arity a
1433 ) => FromUntaggedValue arity (C1 c a)
1100 ) => FromUntaggedValue arity (C1 c a)
1434 where
1101 where
1435− parseUntaggedValue p = fmap M1 . consParseJSON (cname :* p)
1102+ parseUntaggedValue = gParseJSON
1436− where
1437− cname = conName (undefined :: M1 _i c _f _p)
1438
1103
1439instance OVERLAPPING_
1104instance OVERLAPPING_
1440 ( Constructor c )
1105 ( Constructor c )
1441 => FromUntaggedValue arity (C1 c U1)
1106 => FromUntaggedValue arity (C1 c U1)
1442 where
1107 where
1443− parseUntaggedValue (tname :* opts :* _) v =
1108+ parseUntaggedValue opts _ (String s)
1444− contextCons cname tname $ case v of
1109+ | s == pack (constructorTagModifier opts (conName (undefined :: t c U1 p))) =
1445− String tag
1446− | tag == tag' -> pure $ M1 U1
1110+ pure $ M1 U1
1447− | otherwise -> fail_ tag
1111+ | otherwise =
1448− _ -> typeMismatch "String" v
1112+ fail $ "Invalid tag: " ++ unpack s
1449− where
1113+ parseUntaggedValue _ _ v = typeMismatch (conName (undefined :: t c U1 p)) v
1450− tag' = pack $ constructorTagModifier opts cname
1114+
1451− cname = conName (undefined :: M1 _i c _f _p)
1115+
1452− fail_ tag = fail $
1116+--------------------------------------------------------------------------------
1453− "expected tag " ++ show tag' ++ ", but found tag " ++ show tag
1117+
1118+notFound :: Text -> Parser a
1119+notFound key = fail $ "The key \"" ++ unpack key ++ "\" was not found"
1120+{-# INLINE notFound #-}
1454
1121
1455-------------------------------------------------------------------------------
1122-------------------------------------------------------------------------------
1456-- Instances
1123-- Instances
@@ -1508,20 +1175,17 @@ instance (FromJSON a, FromJSON b) => FromJSON (Either a b) where
1508 parseJSON = parseJSON2
1175 parseJSON = parseJSON2
1509 {-# INLINE parseJSON #-}
1176 {-# INLINE parseJSON #-}
1510
1177
1511−instance FromJSON Void where
1178+
1512− parseJSON _ = fail "Cannot parse Void"
1513− {-# INLINE parseJSON #-}
1514
1179
1515instance FromJSON Bool where
1180instance FromJSON Bool where
1516− parseJSON (Bool b) = pure b
1181+ parseJSON = withBool "Bool" pure
1517− parseJSON v = typeMismatch "Bool" v
1518 {-# INLINE parseJSON #-}
1182 {-# INLINE parseJSON #-}
1519
1183
1520instance FromJSONKey Bool where
1184instance FromJSONKey Bool where
1521 fromJSONKey = FromJSONKeyTextParser $ \t -> case t of
1185 fromJSONKey = FromJSONKeyTextParser $ \t -> case t of
1522 "true" -> pure True
1186 "true" -> pure True
1523 "false" -> pure False
1187 "false" -> pure False
1524− _ -> fail $ "cannot parse key " ++ show t ++ " into Bool"
1188+ _ -> fail $ "Cannot parse key into Bool: " ++ T.unpack t
1525
1189
1526instance FromJSON Ordering where
1190instance FromJSON Ordering where
1527 parseJSON = withText "Ordering" $ \s ->
1191 parseJSON = withText "Ordering" $ \s ->
@@ -1529,30 +1193,25 @@ instance FromJSON Ordering where
1529 "LT" -> return LT
1193 "LT" -> return LT
1530 "EQ" -> return EQ
1194 "EQ" -> return EQ
1531 "GT" -> return GT
1195 "GT" -> return GT
1532− _ -> fail $ "parsing Ordering failed, unexpected " ++ show s ++
1533− " (expected \"LT\", \"EQ\", or \"GT\")"
1196+ _ -> fail "Parsing Ordering value failed: expected \"LT\", \"EQ\", or \"GT\""
1534
1197
1535instance FromJSON () where
1198instance FromJSON () where
1536 parseJSON = withArray "()" $ \v ->
1199 parseJSON = withArray "()" $ \v ->
1537 if V.null v
1200 if V.null v
1538 then pure ()
1201 then pure ()
1539− else prependContext "()" $ fail "expected an empty array"
1202+ else fail "Expected an empty array"
1540 {-# INLINE parseJSON #-}
1203 {-# INLINE parseJSON #-}
1541
1204
1542instance FromJSON Char where
1205instance FromJSON Char where
1543− parseJSON = withText "Char" parseChar
1206+ parseJSON = withText "Char" $ \t ->
1207+ if T.compareLength t 1 == EQ
1208+ then pure $ T.head t
1209+ else fail "Expected a string of length 1"
1544 {-# INLINE parseJSON #-}
1210 {-# INLINE parseJSON #-}
1545
1211
1546− parseJSONList (String s) = pure (T.unpack s)
1212+ parseJSONList = withText "String" $ pure . T.unpack
1547− parseJSONList v = typeMismatch "String" v
1548 {-# INLINE parseJSONList #-}
1213 {-# INLINE parseJSONList #-}
1549
1214
1550−parseChar :: Text -> Parser Char
1551−parseChar t =
1552− if T.compareLength t 1 == EQ
1553− then pure $ T.head t
1554− else prependContext "Char" $ fail "expected a string of length 1"
1555−
1556instance FromJSON Double where
1215instance FromJSON Double where
1557 parseJSON = parseRealFloat "Double"
1216 parseJSON = parseRealFloat "Double"
1558 {-# INLINE parseJSON #-}
1217 {-# INLINE parseJSON #-}
@@ -1564,6 +1223,12 @@ instance FromJSONKey Double where
1564 "-Infinity" -> pure (negate 1/0)
1223 "-Infinity" -> pure (negate 1/0)
1565 _ -> Scientific.toRealFloat <$> parseScientificText t
1224 _ -> Scientific.toRealFloat <$> parseScientificText t
1566
1225
1226+instance FromJSON Number where
1227+ parseJSON (Number s) = pure $ scientificToNumber s
1228+ parseJSON Null = pure (D (0/0))
1229+ parseJSON v = typeMismatch "Number" v
1230+ {-# INLINE parseJSON #-}
1231+
1567instance FromJSON Float where
1232instance FromJSON Float where
1568 parseJSON = parseRealFloat "Float"
1233 parseJSON = parseRealFloat "Float"
1569 {-# INLINE parseJSON #-}
1234 {-# INLINE parseJSON #-}
@@ -1576,30 +1241,17 @@ instance FromJSONKey Float where
1576 _ -> Scientific.toRealFloat <$> parseScientificText t
1241 _ -> Scientific.toRealFloat <$> parseScientificText t
1577
1242
1578instance (FromJSON a, Integral a) => FromJSON (Ratio a) where
1243instance (FromJSON a, Integral a) => FromJSON (Ratio a) where
1579− parseJSON (Number x)
1580− | exp10 <= 1024
1581− , exp10 >= -1024 = return $! realToFrac x
1582− | otherwise = prependContext "Ratio" $ fail msg
1583− where
1584− exp10 = base10Exponent x
1585− msg = "found a number with exponent " ++ show exp10
1586− ++ ", but it must not be greater than 1024 or less than -1024"
1587− parseJSON o = objParser o
1588− where
1589− objParser = withObject "Rational" $ \obj -> do
1244+ parseJSON = withObject "Rational" $ \obj ->
1590− numerator <- obj .: "numerator"
1245+ (%) <$> obj .: "numerator"
1591− denominator <- obj .: "denominator"
1592− if denominator == 0
1593− then fail "Ratio denominator was 0"
1594− else pure $ numerator % denominator
1246+ <*> obj .: "denominator"
1595− {-# INLINE parseJSON #-}
1247+ {-# INLINE parseJSON #-}
1596−
1248+
1597−-- | This instance includes a bounds check to prevent maliciously
1249+-- | /WARNING:/ Only parse fixed-precision numbers from trusted input
1598−-- large inputs to fill up the memory of the target system. You can
1250+-- since an attacker could easily fill up the memory of the target
1599−-- newtype 'Scientific' and provide your own instance using
1251+-- system by specifying a scientific number with a big exponent like
1600−-- 'withScientific' if you want to allow larger inputs.
1252+-- @1e1000000000@.
1601instance HasResolution a => FromJSON (Fixed a) where
1253instance HasResolution a => FromJSON (Fixed a) where
1602− parseJSON = prependContext "Fixed" . withBoundedScientific' (pure . realToFrac)
1254+ parseJSON = withScientific "Fixed" $ pure . realToFrac
1603 {-# INLINE parseJSON #-}
1255 {-# INLINE parseJSON #-}
1604
1256
1605instance FromJSON Int where
1257instance FromJSON Int where
@@ -1609,10 +1261,10 @@ instance FromJSON Int where
1609instance FromJSONKey Int where
1261instance FromJSONKey Int where
1610 fromJSONKey = FromJSONKeyTextParser $ parseBoundedIntegralText "Int"
1262 fromJSONKey = FromJSONKeyTextParser $ parseBoundedIntegralText "Int"
1611
1263
1612−-- | This instance includes a bounds check to prevent maliciously
1264+-- | /WARNING:/ Only parse Integers from trusted input since an
1613−-- large inputs to fill up the memory of the target system. You can
1265+-- attacker could easily fill up the memory of the target system by
1614−-- newtype 'Scientific' and provide your own instance using
1266+-- specifying a scientific number with a big exponent like
1615−-- 'withScientific' if you want to allow larger inputs.
1267+-- @1e1000000000@.
1616instance FromJSON Integer where
1268instance FromJSON Integer where
1617 parseJSON = parseIntegral "Integer"
1269 parseJSON = parseIntegral "Integer"
1618 {-# INLINE parseJSON #-}
1270 {-# INLINE parseJSON #-}
@@ -1621,21 +1273,16 @@ instance FromJSONKey Integer where
1621 fromJSONKey = FromJSONKeyTextParser $ parseIntegralText "Integer"
1273 fromJSONKey = FromJSONKeyTextParser $ parseIntegralText "Integer"
1622
1274
1623instance FromJSON Natural where
1275instance FromJSON Natural where
1624− parseJSON value = do
1276+ parseJSON = withScientific "Natural" $ \s ->
1625− integer <- parseIntegral "Natural" value
1277+ if Scientific.coefficient s < 0
1626− parseNatural integer
1278+ then fail $ "Expected a Natural number but got the negative number: " <> show s
1279+ else pure $ truncate s
1627
1280
1628instance FromJSONKey Natural where
1281instance FromJSONKey Natural where
1629− fromJSONKey = FromJSONKeyTextParser $ \text -> do
1282+ fromJSONKey = FromJSONKeyTextParser $ \t -> parseScientificText t >>= \s ->
1630− integer <- parseIntegralText "Natural" text
1283+ if Scientific.coefficient s < 0
1631− parseNatural integer
1632−
1633−parseNatural :: Integer -> Parser Natural
1634−parseNatural integer =
1635− if integer < 0 then
1636− fail $ "parsing Natural failed, unexpected negative number " <> show integer
1284+ then fail $ "Expected a Natural number but got the negative number: " <> show s
1637− else
1285+ else pure $ truncate s
1638− pure $ fromIntegral integer
1639
1286
1640instance FromJSON Int8 where
1287instance FromJSON Int8 where
1641 parseJSON = parseBoundedIntegral "Int8"
1288 parseJSON = parseBoundedIntegral "Int8"
@@ -1700,9 +1347,6 @@ instance FromJSON Word64 where
1700instance FromJSONKey Word64 where
1347instance FromJSONKey Word64 where
1701 fromJSONKey = FromJSONKeyTextParser $ parseBoundedIntegralText "Word64"
1348 fromJSONKey = FromJSONKeyTextParser $ parseBoundedIntegralText "Word64"
1702
1349
1703−instance FromJSON CTime where
1704− parseJSON = fmap CTime . parseJSON
1705− {-# INLINE parseJSON #-}
1706
1350
1707instance FromJSON Text where
1351instance FromJSON Text where
1708 parseJSON = withText "Text" pure
1352 parseJSON = withText "Text" pure
@@ -1732,17 +1376,17 @@ parseVersionText = go . readP_to_S parseVersion . unpack
1732 where
1376 where
1733 go [(v,[])] = return v
1377 go [(v,[])] = return v
1734 go (_ : xs) = go xs
1378 go (_ : xs) = go xs
1735− go _ = fail "parsing Version failed"
1379+ go _ = fail "could not parse Version"
1736
1380
1737-------------------------------------------------------------------------------
1381-------------------------------------------------------------------------------
1738-- semigroups NonEmpty
1382-- semigroups NonEmpty
1739-------------------------------------------------------------------------------
1383-------------------------------------------------------------------------------
1740
1384
1741instance FromJSON1 NonEmpty where
1385instance FromJSON1 NonEmpty where
1742− liftParseJSON p _ = withArray "NonEmpty" $
1386+ liftParseJSON p _ = withArray "NonEmpty a" $
1743 (>>= ne) . Tr.sequence . zipWith (parseIndexedJSON p) [0..] . V.toList
1387 (>>= ne) . Tr.sequence . zipWith (parseIndexedJSON p) [0..] . V.toList
1744 where
1388 where
1745− ne [] = fail "parsing NonEmpty failed, unexpected empty list"
1389+ ne [] = fail "Expected a NonEmpty but got an empty list"
1746 ne (x:xs) = pure (x :| xs)
1390 ne (x:xs) = pure (x :| xs)
1747 {-# INLINE liftParseJSON #-}
1391 {-# INLINE liftParseJSON #-}
1748
1392
@@ -1763,7 +1407,7 @@ instance FromJSON Scientific where
1763-------------------------------------------------------------------------------
1407-------------------------------------------------------------------------------
1764
1408
1765instance FromJSON1 DList.DList where
1409instance FromJSON1 DList.DList where
1766− liftParseJSON p _ = withArray "DList" $
1410+ liftParseJSON p _ = withArray "DList a" $
1767 fmap DList.fromList .
1411 fmap DList.fromList .
1768 Tr.sequence . zipWith (parseIndexedJSON p) [0..] . V.toList
1412 Tr.sequence . zipWith (parseIndexedJSON p) [0..] . V.toList
1769 {-# INLINE liftParseJSON #-}
1413 {-# INLINE liftParseJSON #-}
@@ -1840,7 +1484,7 @@ instance (FromJSON1 f, FromJSON1 g) => FromJSON1 (Sum f g) where
1840 inr = "InR"
1484 inr = "InR"
1841
1485
1842 liftParseJSON _ _ _ = fail $
1486 liftParseJSON _ _ _ = fail $
1843− "parsing Sum failed, expected an object with a single property " ++
1487+ "expected an object with a single property " ++
1844 "where the property key should be either " ++
1488 "where the property key should be either " ++
1845 "\"InL\" or \"InR\""
1489 "\"InL\" or \"InR\""
1846 {-# INLINE liftParseJSON #-}
1490 {-# INLINE liftParseJSON #-}
@@ -1854,7 +1498,7 @@ instance (FromJSON1 f, FromJSON1 g, FromJSON a) => FromJSON (Sum f g a) where
1854-------------------------------------------------------------------------------
1498-------------------------------------------------------------------------------
1855
1499
1856instance FromJSON1 Seq.Seq where
1500instance FromJSON1 Seq.Seq where
1857− liftParseJSON p _ = withArray "Seq" $
1501+ liftParseJSON p _ = withArray "Seq a" $
1858 fmap Seq.fromList .
1502 fmap Seq.fromList .
1859 Tr.sequence . zipWith (parseIndexedJSON p) [0..] . V.toList
1503 Tr.sequence . zipWith (parseIndexedJSON p) [0..] . V.toList
1860 {-# INLINE liftParseJSON #-}
1504 {-# INLINE liftParseJSON #-}
@@ -1888,13 +1532,13 @@ instance FromJSON a => FromJSON (IntMap.IntMap a) where
1888
1532
1889instance (FromJSONKey k, Ord k) => FromJSON1 (M.Map k) where
1533instance (FromJSONKey k, Ord k) => FromJSON1 (M.Map k) where
1890 liftParseJSON p _ = case fromJSONKey of
1534 liftParseJSON p _ = case fromJSONKey of
1891− FromJSONKeyCoerce _ -> withObject "Map" $
1535+ FromJSONKeyCoerce _-> withObject "Map k v" $
1892 fmap (H.foldrWithKey (M.insert . unsafeCoerce) M.empty) . H.traverseWithKey (\k v -> p v <?> Key k)
1536 fmap (H.foldrWithKey (M.insert . unsafeCoerce) M.empty) . H.traverseWithKey (\k v -> p v <?> Key k)
1893− FromJSONKeyText f -> withObject "Map" $
1537+ FromJSONKeyText f -> withObject "Map k v" $
1894 fmap (H.foldrWithKey (M.insert . f) M.empty) . H.traverseWithKey (\k v -> p v <?> Key k)
1538 fmap (H.foldrWithKey (M.insert . f) M.empty) . H.traverseWithKey (\k v -> p v <?> Key k)
1895− FromJSONKeyTextParser f -> withObject "Map" $
1539+ FromJSONKeyTextParser f -> withObject "Map k v" $
1896 H.foldrWithKey (\k v m -> M.insert <$> f k <?> Key k <*> p v <?> Key k <*> m) (pure M.empty)
1540 H.foldrWithKey (\k v m -> M.insert <$> f k <?> Key k <*> p v <?> Key k <*> m) (pure M.empty)
1897− FromJSONKeyValue f -> withArray "Map" $ \arr ->
1541+ FromJSONKeyValue f -> withArray "Map k v" $ \arr ->
1898 fmap M.fromList . Tr.sequence .
1542 fmap M.fromList . Tr.sequence .
1899 zipWith (parseIndexedJSONPair f p) [0..] . V.toList $ arr
1543 zipWith (parseIndexedJSONPair f p) [0..] . V.toList $ arr
1900 {-# INLINE liftParseJSON #-}
1544 {-# INLINE liftParseJSON #-}
@@ -1922,18 +1566,18 @@ instance (FromJSON v) => FromJSON (Tree.Tree v) where
1922
1566
1923instance FromJSON UUID.UUID where
1567instance FromJSON UUID.UUID where
1924 parseJSON = withText "UUID" $
1568 parseJSON = withText "UUID" $
1925− maybe (fail "invalid UUID") pure . UUID.fromText
1569+ maybe (fail "Invalid UUID") pure . UUID.fromText
1926
1570
1927instance FromJSONKey UUID.UUID where
1571instance FromJSONKey UUID.UUID where
1928 fromJSONKey = FromJSONKeyTextParser $
1572 fromJSONKey = FromJSONKeyTextParser $
1929− maybe (fail "invalid UUID") pure . UUID.fromText
1573+ maybe (fail "Invalid UUID") pure . UUID.fromText
1930
1574
1931-------------------------------------------------------------------------------
1575-------------------------------------------------------------------------------
1932-- vector
1576-- vector
1933-------------------------------------------------------------------------------
1577-------------------------------------------------------------------------------
1934
1578
1935instance FromJSON1 Vector where
1579instance FromJSON1 Vector where
1936− liftParseJSON p _ = withArray "Vector" $
1580+ liftParseJSON p _ = withArray "Vector a" $
1937 V.mapM (uncurry $ parseIndexedJSON p) . V.indexed
1581 V.mapM (uncurry $ parseIndexedJSON p) . V.indexed
1938 {-# INLINE liftParseJSON #-}
1582 {-# INLINE liftParseJSON #-}
1939
1583
@@ -1947,14 +1591,14 @@ vectorParseJSON s = withArray s $ fmap V.convert . V.mapM (uncurry $ parseIndexe
1947{-# INLINE vectorParseJSON #-}
1591{-# INLINE vectorParseJSON #-}
1948
1592
1949instance (Storable a, FromJSON a) => FromJSON (VS.Vector a) where
1593instance (Storable a, FromJSON a) => FromJSON (VS.Vector a) where
1950− parseJSON = vectorParseJSON "Data.Vector.Storable.Vector"
1594+ parseJSON = vectorParseJSON "Data.Vector.Storable.Vector a"
1951
1595
1952instance (VP.Prim a, FromJSON a) => FromJSON (VP.Vector a) where
1596instance (VP.Prim a, FromJSON a) => FromJSON (VP.Vector a) where
1953− parseJSON = vectorParseJSON "Data.Vector.Primitive.Vector"
1597+ parseJSON = vectorParseJSON "Data.Vector.Primitive.Vector a"
1954 {-# INLINE parseJSON #-}
1598 {-# INLINE parseJSON #-}
1955
1599
1956instance (VG.Vector VU.Vector a, FromJSON a) => FromJSON (VU.Vector a) where
1600instance (VG.Vector VU.Vector a, FromJSON a) => FromJSON (VU.Vector a) where
1957− parseJSON = vectorParseJSON "Data.Vector.Unboxed.Vector"
1601+ parseJSON = vectorParseJSON "Data.Vector.Unboxed.Vector a"
1958 {-# INLINE parseJSON #-}
1602 {-# INLINE parseJSON #-}
1959
1603
1960-------------------------------------------------------------------------------
1604-------------------------------------------------------------------------------
@@ -1968,13 +1612,13 @@ instance (Eq a, Hashable a, FromJSON a) => FromJSON (HashSet.HashSet a) where
1968
1612
1969instance (FromJSONKey k, Eq k, Hashable k) => FromJSON1 (H.HashMap k) where
1613instance (FromJSONKey k, Eq k, Hashable k) => FromJSON1 (H.HashMap k) where
1970 liftParseJSON p _ = case fromJSONKey of
1614 liftParseJSON p _ = case fromJSONKey of
1971− FromJSONKeyCoerce _ -> withObject "HashMap ~Text" $
1615+ FromJSONKeyCoerce _ -> withObject "HashMap ~Text v" $
1972 uc . H.traverseWithKey (\k v -> p v <?> Key k)
1616 uc . H.traverseWithKey (\k v -> p v <?> Key k)
1973− FromJSONKeyText f -> withObject "HashMap" $
1617+ FromJSONKeyText f -> withObject "HashMap k v" $
1974 fmap (mapKey f) . H.traverseWithKey (\k v -> p v <?> Key k)
1618 fmap (mapKey f) . H.traverseWithKey (\k v -> p v <?> Key k)
1975− FromJSONKeyTextParser f -> withObject "HashMap" $
1619+ FromJSONKeyTextParser f -> withObject "HashMap k v" $
1976 H.foldrWithKey (\k v m -> H.insert <$> f k <?> Key k <*> p v <?> Key k <*> m) (pure H.empty)
1620 H.foldrWithKey (\k v m -> H.insert <$> f k <?> Key k <*> p v <?> Key k <*> m) (pure H.empty)
1977− FromJSONKeyValue f -> withArray "Map" $ \arr ->
1621+ FromJSONKeyValue f -> withArray "Map k v" $ \arr ->
1978 fmap H.fromList . Tr.sequence .
1622 fmap H.fromList . Tr.sequence .
1979 zipWith (parseIndexedJSONPair f p) [0..] . V.toList $ arr
1623 zipWith (parseIndexedJSONPair f p) [0..] . V.toList $ arr
1980 where
1624 where
@@ -1997,33 +1641,11 @@ instance FromJSON DotNetTime where
1997 parseJSON = withText "DotNetTime" $ \t ->
1641 parseJSON = withText "DotNetTime" $ \t ->
1998 let (s,m) = T.splitAt (T.length t - 5) t
1642 let (s,m) = T.splitAt (T.length t - 5) t
1999 t' = T.concat [s,".",m]
1643 t' = T.concat [s,".",m]
2000− in case parseTimeM True defaultTimeLocale "/Date(%s%Q)/" (unpack t') of
1644+ in case parseTime defaultTimeLocale "/Date(%s%Q)/" (unpack t') of
2001 Just d -> pure (DotNetTime d)
1645 Just d -> pure (DotNetTime d)
2002 _ -> fail "could not parse .NET time"
1646 _ -> fail "could not parse .NET time"
2003 {-# INLINE parseJSON #-}
1647 {-# INLINE parseJSON #-}
2004
1648
2005−-------------------------------------------------------------------------------
2006−-- primitive
2007−-------------------------------------------------------------------------------
2008−
2009−instance FromJSON a => FromJSON (PM.Array a) where
2010− -- note: we could do better than this if vector exposed the data
2011− -- constructor in Data.Vector.
2012− parseJSON = fmap Exts.fromList . parseJSON
2013−
2014−instance FromJSON a => FromJSON (PM.SmallArray a) where
2015− parseJSON = fmap Exts.fromList . parseJSON
2016−
2017−#if MIN_VERSION_primitive(0,6,4)
2018−instance (PM.Prim a,FromJSON a) => FromJSON (PM.PrimArray a) where
2019− parseJSON = fmap Exts.fromList . parseJSON
2020−
2021−#if !MIN_VERSION_primitive(0,7,0)
2022−instance (PM.PrimUnlifted a,FromJSON a) => FromJSON (PM.UnliftedArray a) where
2023− parseJSON = fmap Exts.fromList . parseJSON
2024−#endif
2025−#endif
2026−
2027-------------------------------------------------------------------------------
1649-------------------------------------------------------------------------------
2028-- time
1650-- time
2029-------------------------------------------------------------------------------
1651-------------------------------------------------------------------------------
@@ -2074,57 +1696,14 @@ instance FromJSONKey UTCTime where
2074 fromJSONKey = FromJSONKeyTextParser (Time.run Time.utcTime)
1696 fromJSONKey = FromJSONKeyTextParser (Time.run Time.utcTime)
2075
1697
2076
1698
2077−-- | This instance includes a bounds check to prevent maliciously
1699+-- | /WARNING:/ Only parse lengths of time from trusted input
2078−-- large inputs to fill up the memory of the target system. You can
1700+-- since an attacker could easily fill up the memory of the target
2079−-- newtype 'Scientific' and provide your own instance using
1701+-- system by specifying a scientific number with a big exponent like
2080−-- 'withScientific' if you want to allow larger inputs.
1702+-- @1e1000000000@.
2081instance FromJSON NominalDiffTime where
1703instance FromJSON NominalDiffTime where
2082− parseJSON = withBoundedScientific "NominalDiffTime" $ pure . realToFrac
1704+ parseJSON = withScientific "NominalDiffTime" $ pure . realToFrac
2083− {-# INLINE parseJSON #-}
2084−
2085−
2086−-- | This instance includes a bounds check to prevent maliciously
2087−-- large inputs to fill up the memory of the target system. You can
2088−-- newtype 'Scientific' and provide your own instance using
2089−-- 'withScientific' if you want to allow larger inputs.
2090−instance FromJSON DiffTime where
2091− parseJSON = withBoundedScientific "DiffTime" $ pure . realToFrac
2092 {-# INLINE parseJSON #-}
1705 {-# INLINE parseJSON #-}
2093
1706
2094−instance FromJSON SystemTime where
2095− parseJSON v = prependContext "SystemTime" $ do
2096− n <- parseJSON v
2097− let n' = floor (n * fromInteger (resolution n) :: Nano)
2098− let (secs, nano) = n' `divMod` resolution n
2099− return (MkSystemTime (fromInteger secs) (fromInteger nano))
2100−
2101−instance FromJSON CalendarDiffTime where
2102− parseJSON = withObject "CalendarDiffTime" $ \obj -> CalendarDiffTime
2103− <$> obj .: "months"
2104− <*> obj .: "time"
2105−
2106−instance FromJSON CalendarDiffDays where
2107− parseJSON = withObject "CalendarDiffDays" $ \obj -> CalendarDiffDays
2108− <$> obj .: "months"
2109− <*> obj .: "days"
2110−
2111−instance FromJSON DayOfWeek where
2112− parseJSON = withText "DaysOfWeek" parseDayOfWeek
2113−
2114−parseDayOfWeek :: T.Text -> Parser DayOfWeek
2115−parseDayOfWeek t = case T.toLower t of
2116− "monday" -> return Monday
2117− "tuesday" -> return Tuesday
2118− "wednesday" -> return Wednesday
2119− "thursday" -> return Thursday
2120− "friday" -> return Friday
2121− "saturday" -> return Saturday
2122− "sunday" -> return Sunday
2123− _ -> fail "Invalid week day"
2124−
2125−instance FromJSONKey DayOfWeek where
2126− fromJSONKey = FromJSONKeyTextParser parseDayOfWeek
2127−
2128-------------------------------------------------------------------------------
1707-------------------------------------------------------------------------------
2129-- base Monoid/Semigroup
1708-- base Monoid/Semigroup
2130-------------------------------------------------------------------------------
1709-------------------------------------------------------------------------------
@@ -2245,15 +1824,14 @@ instance FromJSON a => FromJSON (Semigroup.Option a) where
2245
1824
2246instance FromJSON1 Proxy where
1825instance FromJSON1 Proxy where
2247 {-# INLINE liftParseJSON #-}
1826 {-# INLINE liftParseJSON #-}
2248− liftParseJSON _ _ = fromNull "Proxy" Proxy
1827+ liftParseJSON _ _ Null = pure Proxy
1828+ liftParseJSON _ _ v = typeMismatch "Proxy" v
2249
1829
2250instance FromJSON (Proxy a) where
1830instance FromJSON (Proxy a) where
2251 {-# INLINE parseJSON #-}
1831 {-# INLINE parseJSON #-}
2252− parseJSON = fromNull "Proxy" Proxy
1832+ parseJSON Null = pure Proxy
1833+ parseJSON v = typeMismatch "Proxy" v
2253
1834
2254−fromNull :: String -> a -> Value -> Parser a
2255−fromNull _ a Null = pure a
2256−fromNull c _ v = prependContext c (typeMismatch "Null" v)
2257
1835
2258instance FromJSON2 Tagged where
1836instance FromJSON2 Tagged where
2259 liftParseJSON2 _ _ p _ = fmap Tagged . p
1837 liftParseJSON2 _ _ p _ = fmap Tagged . p
@@ -2280,7 +1858,10 @@ instance (FromJSON a, FromJSON b, FromJSON c) => FromJSONKey (a,b,c)
2280instance (FromJSON a, FromJSON b, FromJSON c, FromJSON d) => FromJSONKey (a,b,c,d)
1858instance (FromJSON a, FromJSON b, FromJSON c, FromJSON d) => FromJSONKey (a,b,c,d)
2281
1859
2282instance FromJSONKey Char where
1860instance FromJSONKey Char where
2283− fromJSONKey = FromJSONKeyTextParser parseChar
1861+ fromJSONKey = FromJSONKeyTextParser $ \t ->
1862+ if T.length t == 1
1863+ then return (T.index t 0)
1864+ else typeMismatch "Expected Char but String didn't contain exactly one character" (String t)
2284 fromJSONKeyList = FromJSONKeyText T.unpack
1865 fromJSONKeyList = FromJSONKeyText T.unpack
2285
1866
2286instance (FromJSONKey a, FromJSON a) => FromJSONKey [a] where
1867instance (FromJSONKey a, FromJSON a) => FromJSONKey [a] where
Data/Aeson/Types/Generic.hs+8−12
@@ -1,11 +1,10 @@
1{-# LANGUAGE CPP #-}
1{-# LANGUAGE CPP #-}
2{-# LANGUAGE DataKinds #-}
2{-# LANGUAGE DataKinds #-}
3−{-# LANGUAGE DeriveFunctor #-}
4{-# LANGUAGE EmptyDataDecls #-}
3{-# LANGUAGE EmptyDataDecls #-}
5{-# LANGUAGE FlexibleInstances #-}
4{-# LANGUAGE FlexibleInstances #-}
6{-# LANGUAGE FunctionalDependencies #-}
5{-# LANGUAGE FunctionalDependencies #-}
7{-# LANGUAGE KindSignatures #-}
6{-# LANGUAGE KindSignatures #-}
8−{-# LANGUAGE NoImplicitPrelude #-}
7+{-# LANGUAGE MultiParamTypeClasses #-}
9{-# LANGUAGE ScopedTypeVariables #-}
8{-# LANGUAGE ScopedTypeVariables #-}
10{-# LANGUAGE TypeOperators #-}
9{-# LANGUAGE TypeOperators #-}
11{-# LANGUAGE UndecidableInstances #-}
10{-# LANGUAGE UndecidableInstances #-}
@@ -26,7 +25,7 @@
26
25
27module Data.Aeson.Types.Generic
26module Data.Aeson.Types.Generic
28 (
27 (
29− IsRecord
28+ IsRecord(..)
30 , AllNullary
29 , AllNullary
31 , Tagged2(..)
30 , Tagged2(..)
32 , True
31 , True
@@ -35,9 +34,9 @@ module Data.Aeson.Types.Generic
35 , Zero
34 , Zero
36 , One
35 , One
37 , ProductSize(..)
36 , ProductSize(..)
38− , (:*)(..)
39 ) where
37 ) where
40
38
39+import Prelude ()
41import Prelude.Compat
40import Prelude.Compat
42
41
43import GHC.Generics
42import GHC.Generics
@@ -45,8 +44,12 @@ import GHC.Generics
45--------------------------------------------------------------------------------
44--------------------------------------------------------------------------------
46
45
47class IsRecord (f :: * -> *) isRecord | f -> isRecord
46class IsRecord (f :: * -> *) isRecord | f -> isRecord
47+ where
48+ isUnary :: f a -> Bool
49+ isUnary = const True
48
50
49instance (IsRecord f isRecord) => IsRecord (f :*: g) isRecord
51instance (IsRecord f isRecord) => IsRecord (f :*: g) isRecord
52+ where isUnary = const False
50#if MIN_VERSION_base(4,9,0)
53#if MIN_VERSION_base(4,9,0)
51instance OVERLAPPING_ IsRecord (M1 S ('MetaSel 'Nothing u ss ds) f) False
54instance OVERLAPPING_ IsRecord (M1 S ('MetaSel 'Nothing u ss ds) f) False
52#else
55#else
@@ -58,6 +61,7 @@ instance IsRecord Par1 True
58instance IsRecord (Rec1 f) True
61instance IsRecord (Rec1 f) True
59instance IsRecord (f :.: g) True
62instance IsRecord (f :.: g) True
60instance IsRecord U1 False
63instance IsRecord U1 False
64+ where isUnary = const False
61
65
62--------------------------------------------------------------------------------
66--------------------------------------------------------------------------------
63
67
@@ -76,7 +80,6 @@ instance AllNullary (Rec1 f) False
76instance AllNullary U1 True
80instance AllNullary U1 True
77
81
78newtype Tagged2 (s :: * -> *) b = Tagged2 {unTagged2 :: b}
82newtype Tagged2 (s :: * -> *) b = Tagged2 {unTagged2 :: b}
79− deriving Functor
80
83
81--------------------------------------------------------------------------------
84--------------------------------------------------------------------------------
82
85
@@ -109,10 +112,3 @@ instance (ProductSize a, ProductSize b) => ProductSize (a :*: b) where
109
112
110instance ProductSize (S1 s a) where
113instance ProductSize (S1 s a) where
111 productSize = Tagged2 1
114 productSize = Tagged2 1
112−
113−--------------------------------------------------------------------------------
114−
115−-- | Simple extensible tuple type to simplify passing around many parameters.
116−data a :* b = a :* b
117−
118−infixr 1 :*
Data/Aeson/Types/Internal.hs+10−124
@@ -2,7 +2,6 @@
2{-# LANGUAGE DeriveDataTypeable #-}
2{-# LANGUAGE DeriveDataTypeable #-}
3{-# LANGUAGE DeriveGeneric #-}
3{-# LANGUAGE DeriveGeneric #-}
4{-# LANGUAGE GeneralizedNewtypeDeriving #-}
4{-# LANGUAGE GeneralizedNewtypeDeriving #-}
5−{-# LANGUAGE NoImplicitPrelude #-}
6{-# LANGUAGE Rank2Types #-}
5{-# LANGUAGE Rank2Types #-}
7#if __GLASGOW_HASKELL__ >= 800
6#if __GLASGOW_HASKELL__ >= 800
8-- a) THQ works on cross-compilers and unregisterised GHCs
7-- a) THQ works on cross-compilers and unregisterised GHCs
@@ -45,12 +44,9 @@ module Data.Aeson.Types.Internal
45 , parseEither
44 , parseEither
46 , parseMaybe
45 , parseMaybe
47 , modifyFailure
46 , modifyFailure
48− , prependFailure
49 , parserThrowError
47 , parserThrowError
50 , parserCatchError
48 , parserCatchError
51 , formatError
49 , formatError
52− , formatPath
53− , formatRelativePath
54 , (<?>)
50 , (<?>)
55 -- * Constructors and accessors
51 -- * Constructors and accessors
56 , object
52 , object
@@ -64,14 +60,11 @@ module Data.Aeson.Types.Internal
64 , sumEncoding
60 , sumEncoding
65 , unwrapUnaryRecords
61 , unwrapUnaryRecords
66 , tagSingleConstructors
62 , tagSingleConstructors
67− , rejectUnknownFields
68 )
63 )
69
64
70 , SumEncoding(..)
65 , SumEncoding(..)
71− , JSONKeyOptions(keyModifier)
72 , defaultOptions
66 , defaultOptions
73 , defaultTaggedObject
67 , defaultTaggedObject
74− , defaultJSONKeyOptions
75
68
76 -- * Used for changing CamelCase names into something else.
69 -- * Used for changing CamelCase names into something else.
77 , camelTo
70 , camelTo
@@ -81,6 +74,7 @@ module Data.Aeson.Types.Internal
81 , DotNetTime(..)
74 , DotNetTime(..)
82 ) where
75 ) where
83
76
77+import Prelude ()
84import Prelude.Compat
78import Prelude.Compat
85
79
86import Control.Applicative (Alternative(..))
80import Control.Applicative (Alternative(..))
@@ -102,7 +96,6 @@ import Data.Time.Format (FormatTime)
102import Data.Typeable (Typeable)
96import Data.Typeable (Typeable)
103import Data.Vector (Vector)
97import Data.Vector (Vector)
104import GHC.Generics (Generic)
98import GHC.Generics (Generic)
105−import qualified Control.Monad as Monad
106import qualified Control.Monad.Fail as Fail
99import qualified Control.Monad.Fail as Fail
107import qualified Data.HashMap.Strict as H
100import qualified Data.HashMap.Strict as H
108import qualified Data.Scientific as S
101import qualified Data.Scientific as S
@@ -121,7 +114,7 @@ data JSONPathElement = Key Text
121 | Index {-# UNPACK #-} !Int
114 | Index {-# UNPACK #-} !Int
122 -- ^ JSON path element of an index into an
115 -- ^ JSON path element of an index into an
123 -- array, \"array[index]\".
116 -- array, \"array[index]\".
124− deriving (Eq, Show, Typeable, Ord)
117+ deriving (Eq, Show, Typeable)
125type JSONPath = [JSONPathElement]
118type JSONPath = [JSONPathElement]
126
119
127-- | The internal result of running a 'Parser'.
120-- | The internal result of running a 'Parser'.
@@ -156,7 +149,7 @@ instance Functor Result where
156 fmap _ (Error err) = Error err
149 fmap _ (Error err) = Error err
157 {-# INLINE fmap #-}
150 {-# INLINE fmap #-}
158
151
159−instance Monad.Monad IResult where
152+instance Monad IResult where
160 return = pure
153 return = pure
161 {-# INLINE return #-}
154 {-# INLINE return #-}
162
155
@@ -164,16 +157,14 @@ instance Monad.Monad IResult where
164 IError path err >>= _ = IError path err
157 IError path err >>= _ = IError path err
165 {-# INLINE (>>=) #-}
158 {-# INLINE (>>=) #-}
166
159
167−#if !(MIN_VERSION_base(4,13,0))
168 fail = Fail.fail
160 fail = Fail.fail
169 {-# INLINE fail #-}
161 {-# INLINE fail #-}
170−#endif
171
162
172instance Fail.MonadFail IResult where
163instance Fail.MonadFail IResult where
173 fail err = IError [] err
164 fail err = IError [] err
174 {-# INLINE fail #-}
165 {-# INLINE fail #-}
175
166
176−instance Monad.Monad Result where
167+instance Monad Result where
177 return = pure
168 return = pure
178 {-# INLINE return #-}
169 {-# INLINE return #-}
179
170
@@ -181,10 +172,8 @@ instance Monad.Monad Result where
181 Error err >>= _ = Error err
172 Error err >>= _ = Error err
182 {-# INLINE (>>=) #-}
173 {-# INLINE (>>=) #-}
183
174
184−#if !(MIN_VERSION_base(4,13,0))
185 fail = Fail.fail
175 fail = Fail.fail
186 {-# INLINE fail #-}
176 {-# INLINE fail #-}
187−#endif
188
177
189instance Fail.MonadFail Result where
178instance Fail.MonadFail Result where
190 fail err = Error err
179 fail err = Error err
@@ -281,9 +270,7 @@ type Failure f r = JSONPath -> String -> f r
281-- | Success continuation.
270-- | Success continuation.
282type Success a f r = a -> f r
271type Success a f r = a -> f r
283
272
284−-- | A JSON parser. N.B. This might not fit your usual understanding of
273+-- | A JSON parser.
285−-- "parser". Instead you might like to think of 'Parser' as a "parse result",
286−-- i.e. a parser to which the input has already been applied.
287newtype Parser a = Parser {
274newtype Parser a = Parser {
288 runParser :: forall f r.
275 runParser :: forall f r.
289 JSONPath
276 JSONPath
@@ -292,17 +279,14 @@ newtype Parser a = Parser {
292 -> f r
279 -> f r
293 }
280 }
294
281
295−instance Monad.Monad Parser where
282+instance Monad Parser where
296 m >>= g = Parser $ \path kf ks -> let ks' a = runParser (g a) path kf ks
283 m >>= g = Parser $ \path kf ks -> let ks' a = runParser (g a) path kf ks
297 in runParser m path kf ks'
284 in runParser m path kf ks'
298 {-# INLINE (>>=) #-}
285 {-# INLINE (>>=) #-}
299 return = pure
286 return = pure
300 {-# INLINE return #-}
287 {-# INLINE return #-}
301−
302−#if !(MIN_VERSION_base(4,13,0))
303 fail = Fail.fail
288 fail = Fail.fail
304 {-# INLINE fail #-}
289 {-# INLINE fail #-}
305−#endif
306
290
307instance Fail.MonadFail Parser where
291instance Fail.MonadFail Parser where
308 fail msg = Parser $ \path kf _ks -> kf (reverse path) msg
292 fail msg = Parser $ \path kf _ks -> kf (reverse path) msg
@@ -345,7 +329,8 @@ instance Monoid (Parser a) where
345apP :: Parser (a -> b) -> Parser a -> Parser b
329apP :: Parser (a -> b) -> Parser a -> Parser b
346apP d e = do
330apP d e = do
347 b <- d
331 b <- d
348− b <$> e
332+ a <- e
333+ return (b a)
349{-# INLINE apP #-}
334{-# INLINE apP #-}
350
335
351-- | A JSON \"object\" (key\/value map).
336-- | A JSON \"object\" (key\/value map).
@@ -461,17 +446,7 @@ parseEither m v = runParser (m v) [] onError Right
461-- | Annotate an error message with a
446-- | Annotate an error message with a
462-- <http://goessner.net/articles/JsonPath/ JSONPath> error location.
447-- <http://goessner.net/articles/JsonPath/ JSONPath> error location.
463formatError :: JSONPath -> String -> String
448formatError :: JSONPath -> String -> String
464−formatError path msg = "Error in " ++ formatPath path ++ ": " ++ msg
449+formatError path msg = "Error in " ++ format "$" path ++ ": " ++ msg
465−
466−-- | Format a <http://goessner.net/articles/JsonPath/ JSONPath> as a 'String',
467−-- representing the root object as @$@.
468−formatPath :: JSONPath -> String
469−formatPath path = "$" ++ formatRelativePath path
470−
471−-- | Format a <http://goessner.net/articles/JsonPath/ JSONPath> as a 'String'
472−-- which represents the path relative to some root object.
473−formatRelativePath :: JSONPath -> String
474−formatRelativePath path = format "" path
475 where
450 where
476 format :: String -> JSONPath -> String
451 format :: String -> JSONPath -> String
477 format pfx [] = pfx
452 format pfx [] = pfx
@@ -537,15 +512,6 @@ modifyFailure :: (String -> String) -> Parser a -> Parser a
537modifyFailure f (Parser p) = Parser $ \path kf ks ->
512modifyFailure f (Parser p) = Parser $ \path kf ks ->
538 p path (\p' m -> kf p' (f m)) ks
513 p path (\p' m -> kf p' (f m)) ks
539
514
540−-- | If the inner 'Parser' failed, prepend the given string to the failure
541−-- message.
542−--
543−-- @
544−-- 'prependFailure' s = 'modifyFailure' (s '++')
545−-- @
546−prependFailure :: String -> Parser a -> Parser a
547−prependFailure = modifyFailure . (++)
548−
549-- | Throw a parser error with an additional path.
515-- | Throw a parser error with an additional path.
550--
516--
551-- @since 1.2.1.0
517-- @since 1.2.1.0
@@ -565,9 +531,6 @@ parserCatchError (Parser p) handler = Parser $ \path kf ks ->
565--------------------------------------------------------------------------------
531--------------------------------------------------------------------------------
566
532
567-- | Options that specify how to encode\/decode your datatype to\/from JSON.
533-- | Options that specify how to encode\/decode your datatype to\/from JSON.
568−--
569−-- Options can be set using record syntax on 'defaultOptions' with the fields
570−-- below.
571data Options = Options
534data Options = Options
572 { fieldLabelModifier :: String -> String
535 { fieldLabelModifier :: String -> String
573 -- ^ Function applied to field labels.
536 -- ^ Function applied to field labels.
@@ -584,39 +547,6 @@ data Options = Options
584 -- ^ If 'True' record fields with a 'Nothing' value will be
547 -- ^ If 'True' record fields with a 'Nothing' value will be
585 -- omitted from the resulting object. If 'False' the resulting
548 -- omitted from the resulting object. If 'False' the resulting
586 -- object will include those fields mapping to @null@.
549 -- object will include those fields mapping to @null@.
587− --
588− -- === Note
589− --
590− -- Setting 'omitNothingFields' to 'True' only affects fields which are of
591− -- type 'Maybe' /uniformly/ in the 'ToJSON' or 'FromJSON' instance. In
592− -- particular, if the type of a field is declared as a type variable, it
593− -- will not be omitted from the JSON object, unless the field is
594− -- specialized upfront in the instance.
595− --
596− -- ==== __Example__
597− --
598− -- The generic instance for the following type @Fruit@ depends on whether
599− -- the instance head is @Fruit a@ or @Fruit (Maybe a)@.
600− --
601− -- @
602− -- data Fruit a =
603− -- { apples :: a -- A field whose type is a type variable.
604− -- , oranges :: 'Maybe' Int
605− -- }
606− --
607− -- options :: 'Options'
608− -- options = 'defaultOptions' { 'omitNothingFields' = 'True' }
609− --
610− -- -- apples required, oranges optional
611− -- -- Even if 'Data.Aeson.fromJSON' is then specialized to (Fruit ('Maybe' a)).
612− -- instance 'Data.Aeson.FromJSON' a => 'Data.Aeson.FromJSON' (Fruit a) where
613− -- 'Data.Aeson.fromJSON' = 'Data.Aeson.genericFromJSON' options
614− --
615− -- -- apples optional, oranges optional
616− -- -- In this instance, the field apples is uniformly of type ('Maybe' a).
617− -- instance 'Data.Aeson.FromJSON' a => 'Data.Aeson.FromJSON' (Fruit ('Maybe' a)) where
618− -- 'Data.Aeson.fromJSON' = 'Data.Aeson.genericFromJSON' options
619− -- @
620 , sumEncoding :: SumEncoding
550 , sumEncoding :: SumEncoding
621 -- ^ Specifies how to encode constructors of a sum datatype.
551 -- ^ Specifies how to encode constructors of a sum datatype.
622 , unwrapUnaryRecords :: Bool
552 , unwrapUnaryRecords :: Bool
@@ -625,14 +555,10 @@ data Options = Options
625 , tagSingleConstructors :: Bool
555 , tagSingleConstructors :: Bool
626 -- ^ Encode types with a single constructor as sums,
556 -- ^ Encode types with a single constructor as sums,
627 -- so that `allNullaryToStringTag` and `sumEncoding` apply.
557 -- so that `allNullaryToStringTag` and `sumEncoding` apply.
628− , rejectUnknownFields :: Bool
629− -- ^ Applies only to 'Data.Aeson.FromJSON' instances. If a field appears in
630− -- the parsed object map, but does not appear in the target object, parsing
631− -- will fail, with an error message indicating which fields were unknown.
632 }
558 }
633
559
634instance Show Options where
560instance Show Options where
635− show (Options f c a o s u t r) =
561+ show (Options f c a o s u t) =
636 "Options {"
562 "Options {"
637 ++ intercalate ", "
563 ++ intercalate ", "
638 [ "fieldLabelModifier =~ " ++ show (f "exampleField")
564 [ "fieldLabelModifier =~ " ++ show (f "exampleField")
@@ -642,7 +568,6 @@ instance Show Options where
642 , "sumEncoding = " ++ show s
568 , "sumEncoding = " ++ show s
643 , "unwrapUnaryRecords = " ++ show u
569 , "unwrapUnaryRecords = " ++ show u
644 , "tagSingleConstructors = " ++ show t
570 , "tagSingleConstructors = " ++ show t
645− , "rejectUnknownFields = " ++ show r
646 ]
571 ]
647 ++ "}"
572 ++ "}"
648
573
@@ -686,33 +611,6 @@ data SumEncoding =
686 -- contents of the constructor.
611 -- contents of the constructor.
687 deriving (Eq, Show)
612 deriving (Eq, Show)
688
613
689−-- | Options for encoding keys with 'Data.Aeson.Types.genericFromJSONKey' and
690−-- 'Data.Aeson.Types.genericToJSONKey'.
691−data JSONKeyOptions = JSONKeyOptions
692− { keyModifier :: String -> String
693− -- ^ Function applied to keys. Its result is what goes into the encoded
694− -- 'Value'.
695− --
696− -- === __Example__
697− --
698− -- The following instances encode the constructor @Bar@ to lower-case keys
699− -- @\"bar\"@.
700− --
701− -- @
702− -- data Foo = Bar
703− -- deriving 'Generic'
704− --
705− -- opts :: 'JSONKeyOptions'
706− -- opts = 'defaultJSONKeyOptions' { 'keyModifier' = 'toLower' }
707− --
708− -- instance 'ToJSONKey' Foo where
709− -- 'toJSONKey' = 'genericToJSONKey' opts
710− --
711− -- instance 'FromJSONKey' Foo where
712− -- 'fromJSONKey' = 'genericFromJSONKey' opts
713− -- @
714− }
715−
716-- | Default encoding 'Options':
614-- | Default encoding 'Options':
717--
615--
718-- @
616-- @
@@ -724,7 +622,6 @@ data JSONKeyOptions = JSONKeyOptions
724-- , 'sumEncoding' = 'defaultTaggedObject'
622-- , 'sumEncoding' = 'defaultTaggedObject'
725-- , 'unwrapUnaryRecords' = False
623-- , 'unwrapUnaryRecords' = False
726-- , 'tagSingleConstructors' = False
624-- , 'tagSingleConstructors' = False
727−-- , 'rejectUnknownFields' = False
728-- }
625-- }
729-- @
626-- @
730defaultOptions :: Options
627defaultOptions :: Options
@@ -736,7 +633,6 @@ defaultOptions = Options
736 , sumEncoding = defaultTaggedObject
633 , sumEncoding = defaultTaggedObject
737 , unwrapUnaryRecords = False
634 , unwrapUnaryRecords = False
738 , tagSingleConstructors = False
635 , tagSingleConstructors = False
739− , rejectUnknownFields = False
740 }
636 }
741
637
742-- | Default 'TaggedObject' 'SumEncoding' options:
638-- | Default 'TaggedObject' 'SumEncoding' options:
@@ -753,16 +649,6 @@ defaultTaggedObject = TaggedObject
753 , contentsFieldName = "contents"
649 , contentsFieldName = "contents"
754 }
650 }
755
651
756−-- | Default 'JSONKeyOptions':
757−--
758−-- @
759−-- defaultJSONKeyOptions = 'JSONKeyOptions'
760−-- { 'keyModifier' = 'id'
761−-- }
762−-- @
763−defaultJSONKeyOptions :: JSONKeyOptions
764−defaultJSONKeyOptions = JSONKeyOptions id
765−
766-- | Converts from CamelCase to another lower case, interspersing
652-- | Converts from CamelCase to another lower case, interspersing
767-- the character between all capital letters and their previous
653-- the character between all capital letters and their previous
768-- entries, except those capital letters that appear together,
654-- entries, except those capital letters that appear together,
Data/Aeson/Types/ToJSON.hs+84−250
@@ -5,17 +5,21 @@
5{-# LANGUAGE FlexibleInstances #-}
5{-# LANGUAGE FlexibleInstances #-}
6{-# LANGUAGE FunctionalDependencies #-}
6{-# LANGUAGE FunctionalDependencies #-}
7{-# LANGUAGE GADTs #-}
7{-# LANGUAGE GADTs #-}
8−{-# LANGUAGE NoImplicitPrelude #-}
8+{-# LANGUAGE MultiParamTypeClasses #-}
9+{-# LANGUAGE NamedFieldPuns #-}
9{-# LANGUAGE OverloadedStrings #-}
10{-# LANGUAGE OverloadedStrings #-}
10{-# LANGUAGE PatternGuards #-}
11{-# LANGUAGE PatternGuards #-}
11−{-# LANGUAGE PolyKinds #-}
12{-# LANGUAGE RecordWildCards #-}
12{-# LANGUAGE RecordWildCards #-}
13{-# LANGUAGE ScopedTypeVariables #-}
13{-# LANGUAGE ScopedTypeVariables #-}
14{-# LANGUAGE TypeOperators #-}
14{-# LANGUAGE TypeOperators #-}
15+{-# LANGUAGE TypeSynonymInstances #-}
15{-# LANGUAGE UndecidableInstances #-}
16{-# LANGUAGE UndecidableInstances #-}
16
17
18+#if __GLASGOW_HASKELL__ >= 706
19+{-# LANGUAGE PolyKinds #-}
20+#endif
21+
17#include "overlapping-compat.h"
22#include "overlapping-compat.h"
18−#include "incoherent-compat.h"
19
23
20-- TODO: Drop this when we remove support for Data.Attoparsec.Number
24-- TODO: Drop this when we remove support for Data.Attoparsec.Number
21{-# OPTIONS_GHC -fno-warn-deprecations #-}
25{-# OPTIONS_GHC -fno-warn-deprecations #-}
@@ -43,20 +47,15 @@ module Data.Aeson.Types.ToJSON
43 , ToJSONKeyFunction(..)
47 , ToJSONKeyFunction(..)
44 , toJSONKeyText
48 , toJSONKeyText
45 , contramapToJSONKeyFunction
49 , contramapToJSONKeyFunction
46−
47− , GToJSONKey()
48− , genericToJSONKey
49−
50 -- * Object key-value pairs
50 -- * Object key-value pairs
51 , KeyValue(..)
51 , KeyValue(..)
52− , KeyValuePair(..)
53− , FromPairs(..)
54 -- * Functions needed for documentation
52 -- * Functions needed for documentation
55 -- * Encoding functions
53 -- * Encoding functions
56 , listEncoding
54 , listEncoding
57 , listValue
55 , listValue
58 ) where
56 ) where
59
57
58+import Prelude ()
60import Prelude.Compat
59import Prelude.Compat
61
60
62import Control.Applicative (Const(..))
61import Control.Applicative (Const(..))
@@ -64,42 +63,38 @@ import Control.Monad.ST (ST)
64import Data.Aeson.Encoding (Encoding, Encoding', Series, dict, emptyArray_)
63import Data.Aeson.Encoding (Encoding, Encoding', Series, dict, emptyArray_)
65import Data.Aeson.Encoding.Internal ((>*<))
64import Data.Aeson.Encoding.Internal ((>*<))
66import Data.Aeson.Internal.Functions (mapHashKeyVal, mapKeyVal)
65import Data.Aeson.Internal.Functions (mapHashKeyVal, mapKeyVal)
67−import Data.Aeson.Types.Generic (AllNullary, False, IsRecord, One, ProductSize, Tagged2(..), True, Zero, productSize)
66+import Data.Aeson.Types.Generic (AllNullary, False, IsRecord(..), One, ProductSize, Tagged2(..), True, Zero, productSize)
68import Data.Aeson.Types.Internal
67import Data.Aeson.Types.Internal
69import Data.Attoparsec.Number (Number(..))
68import Data.Attoparsec.Number (Number(..))
70import Data.Bits (unsafeShiftR)
69import Data.Bits (unsafeShiftR)
71import Data.DList (DList)
70import Data.DList (DList)
72−import Data.Fixed (Fixed, HasResolution, Nano)
71+import Data.Fixed (Fixed, HasResolution)
73import Data.Foldable (toList)
72import Data.Foldable (toList)
74import Data.Functor.Compose (Compose(..))
73import Data.Functor.Compose (Compose(..))
75−import Data.Functor.Contravariant (Contravariant (..))
76import Data.Functor.Identity (Identity(..))
74import Data.Functor.Identity (Identity(..))
77import Data.Functor.Product (Product(..))
75import Data.Functor.Product (Product(..))
78import Data.Functor.Sum (Sum(..))
76import Data.Functor.Sum (Sum(..))
79import Data.Int (Int16, Int32, Int64, Int8)
77import Data.Int (Int16, Int32, Int64, Int8)
80import Data.List (intersperse)
78import Data.List (intersperse)
81import Data.List.NonEmpty (NonEmpty(..))
79import Data.List.NonEmpty (NonEmpty(..))
82−import Data.Semigroup ((<>))
80+import Data.Monoid ((<>))
83import Data.Proxy (Proxy(..))
81import Data.Proxy (Proxy(..))
84import Data.Ratio (Ratio, denominator, numerator)
82import Data.Ratio (Ratio, denominator, numerator)
85import Data.Scientific (Scientific)
83import Data.Scientific (Scientific)
86import Data.Tagged (Tagged(..))
84import Data.Tagged (Tagged(..))
87import Data.Text (Text, pack)
85import Data.Text (Text, pack)
88−import Data.Time (Day, DiffTime, LocalTime, NominalDiffTime, TimeOfDay, UTCTime, ZonedTime)
86+import Data.Time (Day, LocalTime, NominalDiffTime, TimeOfDay, UTCTime, ZonedTime)
89−import Data.Time.Calendar.Compat (CalendarDiffDays (..), DayOfWeek (..))
90−import Data.Time.LocalTime.Compat (CalendarDiffTime (..))
91−import Data.Time.Clock.System.Compat (SystemTime (..))
92−import Data.Time.Format.Compat (FormatTime, formatTime, defaultTimeLocale)
87+import Data.Time.Format (FormatTime, formatTime)
88+import Data.Time.Locale.Compat (defaultTimeLocale)
93import Data.Vector (Vector)
89import Data.Vector (Vector)
94import Data.Version (Version, showVersion)
90import Data.Version (Version, showVersion)
95−import Data.Void (Void, absurd)
96import Data.Word (Word16, Word32, Word64, Word8)
91import Data.Word (Word16, Word32, Word64, Word8)
97import Foreign.Storable (Storable)
92import Foreign.Storable (Storable)
98−import Foreign.C.Types (CTime (..))
99import GHC.Generics
93import GHC.Generics
100import Numeric.Natural (Natural)
94import Numeric.Natural (Natural)
101import qualified Data.Aeson.Encoding as E
95import qualified Data.Aeson.Encoding as E
102import qualified Data.Aeson.Encoding.Internal as E (InArray, comma, econcat, retagEncoding)
96import qualified Data.Aeson.Encoding.Internal as E (InArray, comma, econcat, retagEncoding)
97+import qualified Data.ByteString as S
103import qualified Data.ByteString.Lazy as L
98import qualified Data.ByteString.Lazy as L
104import qualified Data.DList as DList
99import qualified Data.DList as DList
105import qualified Data.HashMap.Strict as H
100import qualified Data.HashMap.Strict as H
@@ -128,18 +123,16 @@ import qualified Data.Vector.Unboxed as VU
128import qualified Data.Aeson.Encoding.Builder as EB
123import qualified Data.Aeson.Encoding.Builder as EB
129import qualified Data.ByteString.Builder as B
124import qualified Data.ByteString.Builder as B
130
125
131−import qualified GHC.Exts as Exts
126+#if !(MIN_VERSION_bytestring(0,10,0))
132−import qualified Data.Primitive.Array as PM
127+import Foreign.ForeignPtr (withForeignPtr)
133−import qualified Data.Primitive.SmallArray as PM
128+import Foreign.Marshal.Utils (copyBytes)
134−import qualified Data.Primitive.Types as PM
129+import Foreign.Ptr (plusPtr)
135−
130+import qualified Data.ByteString.Internal as S
136−#if MIN_VERSION_primitive(0,6,4)
137−#if !MIN_VERSION_primitive(0,7,0)
138−import qualified Data.Primitive.UnliftedArray as PM
139−#endif
140−import qualified Data.Primitive.PrimArray as PM
131+import qualified Data.ByteString.Lazy.Internal as L
141#endif
132#endif
142
133
134+{-# ANN module ("HLint: ignore Reduce duplication"::String) #-}
135+
143toJSONPair :: (a -> Value) -> (b -> Value) -> (a, b) -> Value
136toJSONPair :: (a -> Value) -> (b -> Value) -> (a, b) -> Value
144toJSONPair a b = liftToJSON2 a (listValue a) b (listValue b)
137toJSONPair a b = liftToJSON2 a (listValue a) b (listValue b)
145{-# INLINE toJSONPair #-}
138{-# INLINE toJSONPair #-}
@@ -267,7 +260,7 @@ genericLiftToEncoding opts te tel = gToJSON opts (To1Args te tel) . from1
267-- @
260-- @
268--
261--
269-- Previous versions of this library only had the 'toJSON' method. Adding
262-- Previous versions of this library only had the 'toJSON' method. Adding
270−-- 'toEncoding' had two reasons:
263+-- 'toEncoding' had to reasons:
271--
264--
272-- 1. toEncoding is more efficient for the common case that the output of
265-- 1. toEncoding is more efficient for the common case that the output of
273-- 'toJSON' is directly serialized to a @ByteString@.
266-- 'toJSON' is directly serialized to a @ByteString@.
@@ -338,13 +331,6 @@ instance KeyValue Pair where
338 name .= value = (name, toJSON value)
331 name .= value = (name, toJSON value)
339 {-# INLINE (.=) #-}
332 {-# INLINE (.=) #-}
340
333
341−-- | Constructs a singleton 'H.HashMap'. For calling functions that
342−-- demand an 'Object' for constructing objects. To be used in
343−-- conjunction with 'mconcat'. Prefer to use 'object' where possible.
344−instance KeyValue Object where
345− name .= value = H.singleton name (toJSON value)
346− {-# INLINE (.=) #-}
347−
348-------------------------------------------------------------------------------
334-------------------------------------------------------------------------------
349-- Classes and types for map keys
335-- Classes and types for map keys
350-------------------------------------------------------------------------------
336-------------------------------------------------------------------------------
@@ -402,7 +388,7 @@ instance KeyValue Object where
402-- newtype wrapper around 'Text'. The recommended approach is to use
388-- newtype wrapper around 'Text'. The recommended approach is to use
403-- generalized newtype deriving:
389-- generalized newtype deriving:
404--
390--
405−-- > newtype RecordId = RecordId { getRecordId :: Text }
391+-- > newtype RecordId = RecordId { getRecordId :: Text}
406-- > deriving (Eq,Ord,ToJSONKey)
392-- > deriving (Eq,Ord,ToJSONKey)
407--
393--
408-- Then we may write:
394-- Then we may write:
@@ -417,18 +403,8 @@ instance KeyValue Object where
417--
403--
418-- It is possible to get the 'ToJSONKey' instance for free as we did
404-- It is possible to get the 'ToJSONKey' instance for free as we did
419-- with 'Foo'. However, in this case, we have a natural way to go to
405-- with 'Foo'. However, in this case, we have a natural way to go to
420−-- and from 'Text' that does not require any escape sequences. So
406+-- and from 'Text' that does not require any escape sequences. So, in
421−-- 'ToJSONKeyText' can be used instead of 'ToJSONKeyValue' to encode maps
407+-- this example, 'ToJSONKeyText' will be used instead of 'ToJSONKeyValue'.
422−-- as objects instead of arrays of pairs. This instance may be
423−-- implemented using generics as follows:
424−--
425−-- @
426−-- instance 'ToJSONKey' Color where
427−-- 'toJSONKey' = 'genericToJSONKey' 'defaultJSONKeyOptions'
428−-- @
429−--
430−-- === __Low-level implementations__
431−--
432-- The 'Show' instance can be used to help write 'ToJSONKey':
408-- The 'Show' instance can be used to help write 'ToJSONKey':
433--
409--
434-- > instance ToJSONKey Color where
410-- > instance ToJSONKey Color where
@@ -503,42 +479,16 @@ toJSONKeyTextEnc e = ToJSONKeyText tot e
503 -- TODO: dropAround is also used in stringEncoding, which is unfortunate atm
479 -- TODO: dropAround is also used in stringEncoding, which is unfortunate atm
504 tot = T.dropAround (== '"')
480 tot = T.dropAround (== '"')
505 . T.decodeLatin1
481 . T.decodeLatin1
506− . L.toStrict
482+ . lazyToStrictByteString
507 . E.encodingToLazyByteString
483 . E.encodingToLazyByteString
508 . e
484 . e
509
485
510−instance Contravariant ToJSONKeyFunction where
511− contramap = contramapToJSONKeyFunction
512−
513-- | Contravariant map, as 'ToJSONKeyFunction' is a contravariant functor.
486-- | Contravariant map, as 'ToJSONKeyFunction' is a contravariant functor.
514contramapToJSONKeyFunction :: (b -> a) -> ToJSONKeyFunction a -> ToJSONKeyFunction b
487contramapToJSONKeyFunction :: (b -> a) -> ToJSONKeyFunction a -> ToJSONKeyFunction b
515contramapToJSONKeyFunction h x = case x of
488contramapToJSONKeyFunction h x = case x of
516 ToJSONKeyText f g -> ToJSONKeyText (f . h) (g . h)
489 ToJSONKeyText f g -> ToJSONKeyText (f . h) (g . h)
517 ToJSONKeyValue f g -> ToJSONKeyValue (f . h) (g . h)
490 ToJSONKeyValue f g -> ToJSONKeyValue (f . h) (g . h)
518
491
519−-- 'toJSONKey' for 'Generic' types.
520−-- Deriving is supported for enumeration types, i.e. the sums of nullary
521−-- constructors. The names of constructors will be used as keys for JSON
522−-- objects.
523−--
524−-- See also 'genericFromJSONKey'.
525−--
526−-- === __Example__
527−--
528−-- @
529−-- data Color = Red | Green | Blue
530−-- deriving 'Generic'
531−--
532−-- instance 'ToJSONKey' Color where
533−-- 'toJSONKey' = 'genericToJSONKey' 'defaultJSONKeyOptions'
534−-- @
535−genericToJSONKey :: (Generic a, GToJSONKey (Rep a))
536− => JSONKeyOptions -> ToJSONKeyFunction a
537−genericToJSONKey opts = toJSONKeyText (pack . keyModifier opts . getConName . from)
538−
539−class GetConName f => GToJSONKey f
540−instance GetConName f => GToJSONKey f
541−
542-------------------------------------------------------------------------------
492-------------------------------------------------------------------------------
543-- Lifings of FromJSON and ToJSON to unary and binary type constructors
493-- Lifings of FromJSON and ToJSON to unary and binary type constructors
544-------------------------------------------------------------------------------
494-------------------------------------------------------------------------------
@@ -703,13 +653,11 @@ instance (ToJSON a) => ToJSON [a] where
703instance OVERLAPPABLE_ (GToJSON enc arity a) => GToJSON enc arity (M1 i c a) where
653instance OVERLAPPABLE_ (GToJSON enc arity a) => GToJSON enc arity (M1 i c a) where
704 -- Meta-information, which is not handled elsewhere, is ignored:
654 -- Meta-information, which is not handled elsewhere, is ignored:
705 gToJSON opts targs = gToJSON opts targs . unM1
655 gToJSON opts targs = gToJSON opts targs . unM1
706− {-# INLINE gToJSON #-}
707
656
708instance GToJSON enc One Par1 where
657instance GToJSON enc One Par1 where
709 -- Direct occurrences of the last type parameter are encoded with the
658 -- Direct occurrences of the last type parameter are encoded with the
710 -- function passed in as an argument:
659 -- function passed in as an argument:
711 gToJSON _opts (To1Args tj _) = tj . unPar1
660 gToJSON _opts (To1Args tj _) = tj . unPar1
712− {-# INLINE gToJSON #-}
713
661
714instance ( ConsToJSON enc arity a
662instance ( ConsToJSON enc arity a
715 , AllNullary (C1 c a) allNullary
663 , AllNullary (C1 c a) allNullary
@@ -722,13 +670,11 @@ instance ( ConsToJSON enc arity a
722 . sumToJSON opts targs
670 . sumToJSON opts targs
723 . unM1
671 . unM1
724 | otherwise = consToJSON opts targs . unM1 . unM1
672 | otherwise = consToJSON opts targs . unM1 . unM1
725− {-# INLINE gToJSON #-}
726
673
727instance (ConsToJSON enc arity a) => GToJSON enc arity (C1 c a) where
674instance (ConsToJSON enc arity a) => GToJSON enc arity (C1 c a) where
728 -- Constructors need to be encoded differently depending on whether they're
675 -- Constructors need to be encoded differently depending on whether they're
729 -- a record or not. This distinction is made by 'consToJSON':
676 -- a record or not. This distinction is made by 'consToJSON':
730 gToJSON opts targs = consToJSON opts targs . unM1
677 gToJSON opts targs = consToJSON opts targs . unM1
731− {-# INLINE gToJSON #-}
732
678
733instance ( AllNullary (a :+: b) allNullary
679instance ( AllNullary (a :+: b) allNullary
734 , SumToJSON enc arity (a :+: b) allNullary
680 , SumToJSON enc arity (a :+: b) allNullary
@@ -739,7 +685,6 @@ instance ( AllNullary (a :+: b) allNullary
739 -- strings. This distinction is made by 'sumToJSON':
685 -- strings. This distinction is made by 'sumToJSON':
740 gToJSON opts targs = (unTagged :: Tagged allNullary enc -> enc)
686 gToJSON opts targs = (unTagged :: Tagged allNullary enc -> enc)
741 . sumToJSON opts targs
687 . sumToJSON opts targs
742− {-# INLINE gToJSON #-}
743
688
744--------------------------------------------------------------------------------
689--------------------------------------------------------------------------------
745-- Generic toJSON
690-- Generic toJSON
@@ -748,27 +693,18 @@ instance ( AllNullary (a :+: b) allNullary
748-- possible but makes error messages a bit harder to understand for missing
693-- possible but makes error messages a bit harder to understand for missing
749-- instances.
694-- instances.
750
695
751−instance GToJSON Value arity V1 where
752− -- Empty values do not exist, which makes the job of formatting them
753− -- rather easy:
754− gToJSON _ _ x = x `seq` error "case: V1"
755− {-# INLINE gToJSON #-}
756−
757instance ToJSON a => GToJSON Value arity (K1 i a) where
696instance ToJSON a => GToJSON Value arity (K1 i a) where
758 -- Constant values are encoded using their ToJSON instance:
697 -- Constant values are encoded using their ToJSON instance:
759 gToJSON _opts _ = toJSON . unK1
698 gToJSON _opts _ = toJSON . unK1
760− {-# INLINE gToJSON #-}
761
699
762instance ToJSON1 f => GToJSON Value One (Rec1 f) where
700instance ToJSON1 f => GToJSON Value One (Rec1 f) where
763 -- Recursive occurrences of the last type parameter are encoded using their
701 -- Recursive occurrences of the last type parameter are encoded using their
764 -- ToJSON1 instance:
702 -- ToJSON1 instance:
765 gToJSON _opts (To1Args tj tjl) = liftToJSON tj tjl . unRec1
703 gToJSON _opts (To1Args tj tjl) = liftToJSON tj tjl . unRec1
766− {-# INLINE gToJSON #-}
767
704
768instance GToJSON Value arity U1 where
705instance GToJSON Value arity U1 where
769 -- Empty constructors are encoded to an empty array:
706 -- Empty constructors are encoded to an empty array:
770 gToJSON _opts _ _ = emptyArray
707 gToJSON _opts _ _ = emptyArray
771− {-# INLINE gToJSON #-}
772
708
773instance ( WriteProduct arity a, WriteProduct arity b
709instance ( WriteProduct arity a, WriteProduct arity b
774 , ProductSize a, ProductSize b
710 , ProductSize a, ProductSize b
@@ -785,7 +721,6 @@ instance ( WriteProduct arity a, WriteProduct arity b
785 where
721 where
786 lenProduct = (unTagged2 :: Tagged2 (a :*: b) Int -> Int)
722 lenProduct = (unTagged2 :: Tagged2 (a :*: b) Int -> Int)
787 productSize
723 productSize
788− {-# INLINE gToJSON #-}
789
724
790instance ( ToJSON1 f
725instance ( ToJSON1 f
791 , GToJSON Value One g
726 , GToJSON Value One g
@@ -797,7 +732,6 @@ instance ( ToJSON1 f
797 gToJSON opts targs =
732 gToJSON opts targs =
798 let gtj = gToJSON opts targs in
733 let gtj = gToJSON opts targs in
799 liftToJSON gtj (listValue gtj) . unComp1
734 liftToJSON gtj (listValue gtj) . unComp1
800− {-# INLINE gToJSON #-}
801
735
802--------------------------------------------------------------------------------
736--------------------------------------------------------------------------------
803-- Generic toEncoding
737-- Generic toEncoding
@@ -805,18 +739,15 @@ instance ( ToJSON1 f
805instance ToJSON a => GToJSON Encoding arity (K1 i a) where
739instance ToJSON a => GToJSON Encoding arity (K1 i a) where
806 -- Constant values are encoded using their ToJSON instance:
740 -- Constant values are encoded using their ToJSON instance:
807 gToJSON _opts _ = toEncoding . unK1
741 gToJSON _opts _ = toEncoding . unK1
808− {-# INLINE gToJSON #-}
809
742
810instance ToJSON1 f => GToJSON Encoding One (Rec1 f) where
743instance ToJSON1 f => GToJSON Encoding One (Rec1 f) where
811 -- Recursive occurrences of the last type parameter are encoded using their
744 -- Recursive occurrences of the last type parameter are encoded using their
812 -- ToEncoding1 instance:
745 -- ToEncoding1 instance:
813 gToJSON _opts (To1Args te tel) = liftToEncoding te tel . unRec1
746 gToJSON _opts (To1Args te tel) = liftToEncoding te tel . unRec1
814− {-# INLINE gToJSON #-}
815
747
816instance GToJSON Encoding arity U1 where
748instance GToJSON Encoding arity U1 where
817 -- Empty constructors are encoded to an empty array:
749 -- Empty constructors are encoded to an empty array:
818 gToJSON _opts _ _ = E.emptyArray_
750 gToJSON _opts _ _ = E.emptyArray_
819− {-# INLINE gToJSON #-}
820
751
821instance ( EncodeProduct arity a
752instance ( EncodeProduct arity a
822 , EncodeProduct arity b
753 , EncodeProduct arity b
@@ -826,7 +757,6 @@ instance ( EncodeProduct arity a
826 -- the same size as the product and write the product's elements to it using
757 -- the same size as the product and write the product's elements to it using
827 -- 'encodeProduct':
758 -- 'encodeProduct':
828 gToJSON opts targs p = E.list E.retagEncoding [encodeProduct opts targs p]
759 gToJSON opts targs p = E.list E.retagEncoding [encodeProduct opts targs p]
829− {-# INLINE gToJSON #-}
830
760
831instance ( ToJSON1 f
761instance ( ToJSON1 f
832 , GToJSON Encoding One g
762 , GToJSON Encoding One g
@@ -838,7 +768,6 @@ instance ( ToJSON1 f
838 gToJSON opts targs =
768 gToJSON opts targs =
839 let gte = gToJSON opts targs in
769 let gte = gToJSON opts targs in
840 liftToEncoding gte (listEncoding gte) . unComp1
770 liftToEncoding gte (listEncoding gte) . unComp1
841− {-# INLINE gToJSON #-}
842
771
843--------------------------------------------------------------------------------
772--------------------------------------------------------------------------------
844
773
@@ -922,14 +851,14 @@ instance ( IsRecord a isRecord
922 , TaggedObject' enc pairs arity a isRecord
851 , TaggedObject' enc pairs arity a isRecord
923 , FromPairs enc pairs
852 , FromPairs enc pairs
924 , FromString enc
853 , FromString enc
925− , KeyValuePair enc pairs
854+ , GKeyValue enc pairs
926 , Constructor c
855 , Constructor c
927 ) => TaggedObject enc arity (C1 c a)
856 ) => TaggedObject enc arity (C1 c a)
928 where
857 where
929 taggedObject opts targs tagFieldName contentsFieldName =
858 taggedObject opts targs tagFieldName contentsFieldName =
930− fromPairs . mappend tag . contents
859+ fromPairs . (tag <>) . contents
931 where
860 where
932− tag = tagFieldName `pair`
861+ tag = tagFieldName `gPair`
933 (fromString (constructorTagModifier opts (conName (undefined :: t c a p)))
862 (fromString (constructorTagModifier opts (conName (undefined :: t c a p)))
934 :: enc)
863 :: enc)
935 contents =
864 contents =
@@ -941,11 +870,11 @@ class TaggedObject' enc pairs arity f isRecord where
941 -> String -> f a -> Tagged isRecord pairs
870 -> String -> f a -> Tagged isRecord pairs
942
871
943instance ( GToJSON enc arity f
872instance ( GToJSON enc arity f
944− , KeyValuePair enc pairs
873+ , GKeyValue enc pairs
945 ) => TaggedObject' enc pairs arity f False
874 ) => TaggedObject' enc pairs arity f False
946 where
875 where
947 taggedObject' opts targs contentsFieldName =
876 taggedObject' opts targs contentsFieldName =
948− Tagged . (contentsFieldName `pair`) . gToJSON opts targs
877+ Tagged . (contentsFieldName `gPair`) . gToJSON opts targs
949
878
950instance OVERLAPPING_ Monoid pairs => TaggedObject' enc pairs arity U1 False where
879instance OVERLAPPING_ Monoid pairs => TaggedObject' enc pairs arity U1 False where
951 taggedObject' _ _ _ _ = Tagged mempty
880 taggedObject' _ _ _ _ = Tagged mempty
@@ -968,9 +897,6 @@ instance (GetConName a, GetConName b) => GetConName (a :+: b) where
968instance (Constructor c) => GetConName (C1 c a) where
897instance (Constructor c) => GetConName (C1 c a) where
969 getConName = conName
898 getConName = conName
970
899
971−-- For genericToJSONKey
972−instance GetConName a => GetConName (D1 d a) where
973− getConName (M1 x) = getConName x
974
900
975--------------------------------------------------------------------------------
901--------------------------------------------------------------------------------
976
902
@@ -1026,6 +952,7 @@ class ConsToJSON enc arity f where
1026
952
1027class ConsToJSON' enc arity f isRecord where
953class ConsToJSON' enc arity f isRecord where
1028 consToJSON' :: Options -> ToArgs enc arity a
954 consToJSON' :: Options -> ToArgs enc arity a
955+ -> Bool -- ^ Are we a record with one field?
1029 -> f a -> Tagged isRecord enc
956 -> f a -> Tagged isRecord enc
1030
957
1031instance ( IsRecord f isRecord
958instance ( IsRecord f isRecord
@@ -1034,29 +961,23 @@ instance ( IsRecord f isRecord
1034 where
961 where
1035 consToJSON opts targs =
962 consToJSON opts targs =
1036 (unTagged :: Tagged isRecord enc -> enc)
963 (unTagged :: Tagged isRecord enc -> enc)
1037− . consToJSON' opts targs
964+ . consToJSON' opts targs (isUnary (undefined :: f a))
1038 {-# INLINE consToJSON #-}
965 {-# INLINE consToJSON #-}
1039
966
1040−instance OVERLAPPING_
1041− ( RecordToPairs enc pairs arity (S1 s f)
1042− , FromPairs enc pairs
1043− , GToJSON enc arity f
1044− ) => ConsToJSON' enc arity (S1 s f) True
1045− where
1046− consToJSON' opts targs
1047− | unwrapUnaryRecords opts = Tagged . gToJSON opts targs
1048− | otherwise = Tagged . fromPairs . recordToPairs opts targs
1049− {-# INLINE consToJSON' #-}
1050−
1051instance ( RecordToPairs enc pairs arity f
967instance ( RecordToPairs enc pairs arity f
1052 , FromPairs enc pairs
968 , FromPairs enc pairs
969+ , GToJSON enc arity f
1053 ) => ConsToJSON' enc arity f True
970 ) => ConsToJSON' enc arity f True
1054 where
971 where
972+ consToJSON' opts targs isUn =
973+ Tagged .
974+ case (isUn, unwrapUnaryRecords opts) of
975+ (True, True) -> gToJSON opts targs
1055− consToJSON' opts targs = Tagged . fromPairs . recordToPairs opts targs
976+ _ -> fromPairs . recordToPairs opts targs
1056 {-# INLINE consToJSON' #-}
977 {-# INLINE consToJSON' #-}
1057
978
1058instance GToJSON enc arity f => ConsToJSON' enc arity f False where
979instance GToJSON enc arity f => ConsToJSON' enc arity f False where
1059− consToJSON' opts targs = Tagged . gToJSON opts targs
980+ consToJSON' opts targs _ = Tagged . gToJSON opts targs
1060 {-# INLINE consToJSON' #-}
981 {-# INLINE consToJSON' #-}
1061
982
1062--------------------------------------------------------------------------------
983--------------------------------------------------------------------------------
@@ -1074,7 +995,7 @@ instance ( Monoid pairs
1074 ) => RecordToPairs enc pairs arity (a :*: b)
995 ) => RecordToPairs enc pairs arity (a :*: b)
1075 where
996 where
1076 recordToPairs opts (targs :: ToArgs enc arity p) (a :*: b) =
997 recordToPairs opts (targs :: ToArgs enc arity p) (a :*: b) =
1077− pairsOf a `mappend` pairsOf b
998+ pairsOf a <> pairsOf b
1078 where
999 where
1079 pairsOf :: (RecordToPairs enc pairs arity f) => f p -> pairs
1000 pairsOf :: (RecordToPairs enc pairs arity f) => f p -> pairs
1080 pairsOf = recordToPairs opts targs
1001 pairsOf = recordToPairs opts targs
@@ -1082,16 +1003,16 @@ instance ( Monoid pairs
1082
1003
1083instance ( Selector s
1004instance ( Selector s
1084 , GToJSON enc arity a
1005 , GToJSON enc arity a
1085− , KeyValuePair enc pairs
1006+ , GKeyValue enc pairs
1086 ) => RecordToPairs enc pairs arity (S1 s a)
1007 ) => RecordToPairs enc pairs arity (S1 s a)
1087 where
1008 where
1088 recordToPairs = fieldToPair
1009 recordToPairs = fieldToPair
1089 {-# INLINE recordToPairs #-}
1010 {-# INLINE recordToPairs #-}
1090
1011
1091−instance INCOHERENT_
1012+instance OVERLAPPING_
1092 ( Selector s
1013 ( Selector s
1093 , GToJSON enc arity (K1 i (Maybe a))
1014 , GToJSON enc arity (K1 i (Maybe a))
1094− , KeyValuePair enc pairs
1015+ , GKeyValue enc pairs
1095 , Monoid pairs
1016 , Monoid pairs
1096 ) => RecordToPairs enc pairs arity (S1 s (K1 i (Maybe a)))
1017 ) => RecordToPairs enc pairs arity (S1 s (K1 i (Maybe a)))
1097 where
1018 where
@@ -1100,28 +1021,15 @@ instance INCOHERENT_
1100 recordToPairs opts targs m1 = fieldToPair opts targs m1
1021 recordToPairs opts targs m1 = fieldToPair opts targs m1
1101 {-# INLINE recordToPairs #-}
1022 {-# INLINE recordToPairs #-}
1102
1023
1103−instance INCOHERENT_
1104− ( Selector s
1105− , GToJSON enc arity (K1 i (Maybe a))
1106− , KeyValuePair enc pairs
1107− , Monoid pairs
1108− ) => RecordToPairs enc pairs arity (S1 s (K1 i (Semigroup.Option a)))
1109− where
1110− recordToPairs opts targs = recordToPairs opts targs . unwrap
1111− where
1112− unwrap :: S1 s (K1 i (Semigroup.Option a)) p -> S1 s (K1 i (Maybe a)) p
1113− unwrap (M1 (K1 (Semigroup.Option a))) = M1 (K1 a)
1114− {-# INLINE recordToPairs #-}
1115−
1116fieldToPair :: (Selector s
1024fieldToPair :: (Selector s
1117 , GToJSON enc arity a
1025 , GToJSON enc arity a
1118− , KeyValuePair enc pairs)
1026+ , GKeyValue enc pairs)
1119 => Options -> ToArgs enc arity p
1027 => Options -> ToArgs enc arity p
1120 -> S1 s a p -> pairs
1028 -> S1 s a p -> pairs
1121fieldToPair opts targs m1 =
1029fieldToPair opts targs m1 =
1122 let key = fieldLabelModifier opts (selName m1)
1030 let key = fieldLabelModifier opts (selName m1)
1123 value = gToJSON opts targs (unM1 m1)
1031 value = gToJSON opts targs (unM1 m1)
1124− in key `pair` value
1032+ in key `gPair` value
1125{-# INLINE fieldToPair #-}
1033{-# INLINE fieldToPair #-}
1126
1034
1127--------------------------------------------------------------------------------
1035--------------------------------------------------------------------------------
@@ -1145,12 +1053,10 @@ instance ( WriteProduct arity a
1145 lenL = len `unsafeShiftR` 1
1053 lenL = len `unsafeShiftR` 1
1146 lenR = len - lenL
1054 lenR = len - lenL
1147 ixR = ix + lenL
1055 ixR = ix + lenL
1148− {-# INLINE writeProduct #-}
1149
1056
1150instance OVERLAPPABLE_ (GToJSON Value arity a) => WriteProduct arity a where
1057instance OVERLAPPABLE_ (GToJSON Value arity a) => WriteProduct arity a where
1151 writeProduct opts targs mv ix _ =
1058 writeProduct opts targs mv ix _ =
1152 VM.unsafeWrite mv ix . gToJSON opts targs
1059 VM.unsafeWrite mv ix . gToJSON opts targs
1153− {-# INLINE writeProduct #-}
1154
1060
1155--------------------------------------------------------------------------------
1061--------------------------------------------------------------------------------
1156
1062
@@ -1168,23 +1074,21 @@ instance ( EncodeProduct arity a
1168 encodeProduct opts targs (a :*: b) =
1074 encodeProduct opts targs (a :*: b) =
1169 encodeProduct opts targs a >*<
1075 encodeProduct opts targs a >*<
1170 encodeProduct opts targs b
1076 encodeProduct opts targs b
1171− {-# INLINE encodeProduct #-}
1172
1077
1173instance OVERLAPPABLE_ (GToJSON Encoding arity a) => EncodeProduct arity a where
1078instance OVERLAPPABLE_ (GToJSON Encoding arity a) => EncodeProduct arity a where
1174 encodeProduct opts targs a = E.retagEncoding $ gToJSON opts targs a
1079 encodeProduct opts targs a = E.retagEncoding $ gToJSON opts targs a
1175− {-# INLINE encodeProduct #-}
1176
1080
1177--------------------------------------------------------------------------------
1081--------------------------------------------------------------------------------
1178
1082
1179instance ( GToJSON enc arity a
1083instance ( GToJSON enc arity a
1180 , ConsToJSON enc arity a
1084 , ConsToJSON enc arity a
1181 , FromPairs enc pairs
1085 , FromPairs enc pairs
1182− , KeyValuePair enc pairs
1086+ , GKeyValue enc pairs
1183 , Constructor c
1087 , Constructor c
1184 ) => SumToJSON' ObjectWithSingleField enc arity (C1 c a)
1088 ) => SumToJSON' ObjectWithSingleField enc arity (C1 c a)
1185 where
1089 where
1186 sumToJSON' opts targs =
1090 sumToJSON' opts targs =
1187− Tagged . fromPairs . (typ `pair`) . gToJSON opts targs
1091+ Tagged . fromPairs . (typ `gPair`) . gToJSON opts targs
1188 where
1092 where
1189 typ = constructorTagModifier opts $
1093 typ = constructorTagModifier opts $
1190 conName (undefined :: t c a p)
1094 conName (undefined :: t c a p)
@@ -1276,13 +1180,6 @@ instance (ToJSON a, ToJSON b) => ToJSON (Either a b) where
1276 toEncoding = toEncoding2
1180 toEncoding = toEncoding2
1277 {-# INLINE toEncoding #-}
1181 {-# INLINE toEncoding #-}
1278
1182
1279−instance ToJSON Void where
1280− toJSON = absurd
1281− {-# INLINE toJSON #-}
1282−
1283− toEncoding = absurd
1284− {-# INLINE toEncoding #-}
1285−
1286
1183
1287instance ToJSON Bool where
1184instance ToJSON Bool where
1288 toJSON = Bool
1185 toJSON = Bool
@@ -1468,6 +1365,7 @@ instance ToJSONKey Int64 where
1468 toJSONKey = toJSONKeyTextEnc E.int64Text
1365 toJSONKey = toJSONKeyTextEnc E.int64Text
1469 {-# INLINE toJSONKey #-}
1366 {-# INLINE toJSONKey #-}
1470
1367
1368+
1471instance ToJSON Word where
1369instance ToJSON Word where
1472 toJSON = Number . fromIntegral
1370 toJSON = Number . fromIntegral
1473 {-# INLINE toJSON #-}
1371 {-# INLINE toJSON #-}
@@ -1527,12 +1425,6 @@ instance ToJSONKey Word64 where
1527 toJSONKey = toJSONKeyTextEnc E.word64Text
1425 toJSONKey = toJSONKeyTextEnc E.word64Text
1528 {-# INLINE toJSONKey #-}
1426 {-# INLINE toJSONKey #-}
1529
1427
1530−instance ToJSON CTime where
1531− toJSON (CTime i) = toJSON i
1532− {-# INLINE toJSON #-}
1533−
1534− toEncoding (CTime i) = toEncoding i
1535− {-# INLINE toEncoding #-}
1536
1428
1537instance ToJSON Text where
1429instance ToJSON Text where
1538 toJSON = String
1430 toJSON = String
@@ -1966,32 +1858,6 @@ dotNetTime (DotNetTime t) = secs ++ formatMillis t ++ ")/"
1966formatMillis :: (FormatTime t) => t -> String
1858formatMillis :: (FormatTime t) => t -> String
1967formatMillis = take 3 . formatTime defaultTimeLocale "%q"
1859formatMillis = take 3 . formatTime defaultTimeLocale "%q"
1968
1860
1969−-------------------------------------------------------------------------------
1970−-- primitive
1971−-------------------------------------------------------------------------------
1972−
1973−instance ToJSON a => ToJSON (PM.Array a) where
1974− -- note: we could do better than this if vector exposed the data
1975− -- constructor in Data.Vector.
1976− toJSON = toJSON . Exts.toList
1977− toEncoding = toEncoding . Exts.toList
1978−
1979−instance ToJSON a => ToJSON (PM.SmallArray a) where
1980− toJSON = toJSON . Exts.toList
1981− toEncoding = toEncoding . Exts.toList
1982−
1983−#if (MIN_VERSION_primitive(0,6,4))
1984−instance (PM.Prim a,ToJSON a) => ToJSON (PM.PrimArray a) where
1985− toJSON = toJSON . Exts.toList
1986− toEncoding = toEncoding . Exts.toList
1987−
1988−#if !MIN_VERSION_primitive(0,7,0)
1989−instance (PM.PrimUnlifted a,ToJSON a) => ToJSON (PM.UnliftedArray a) where
1990− toJSON = toJSON . Exts.toList
1991− toEncoding = toEncoding . Exts.toList
1992−#endif
1993−#endif
1994−
1995-------------------------------------------------------------------------------
1861-------------------------------------------------------------------------------
1996-- time
1862-- time
1997-------------------------------------------------------------------------------
1863-------------------------------------------------------------------------------
@@ -2040,11 +1906,10 @@ stringEncoding :: Encoding' Text -> Value
2040stringEncoding = String
1906stringEncoding = String
2041 . T.dropAround (== '"')
1907 . T.dropAround (== '"')
2042 . T.decodeLatin1
1908 . T.decodeLatin1
2043− . L.toStrict
1909+ . lazyToStrictByteString
2044 . E.encodingToLazyByteString
1910 . E.encodingToLazyByteString
2045{-# INLINE stringEncoding #-}
1911{-# INLINE stringEncoding #-}
2046
1912
2047−
2048instance ToJSON NominalDiffTime where
1913instance ToJSON NominalDiffTime where
2049 toJSON = Number . realToFrac
1914 toJSON = Number . realToFrac
2050 {-# INLINE toJSON #-}
1915 {-# INLINE toJSON #-}
@@ -2052,58 +1917,6 @@ instance ToJSON NominalDiffTime where
2052 toEncoding = E.scientific . realToFrac
1917 toEncoding = E.scientific . realToFrac
2053 {-# INLINE toEncoding #-}
1918 {-# INLINE toEncoding #-}
2054
1919
2055−
2056−instance ToJSON DiffTime where
2057− toJSON = Number . realToFrac
2058− {-# INLINE toJSON #-}
2059−
2060− toEncoding = E.scientific . realToFrac
2061− {-# INLINE toEncoding #-}
2062−
2063−-- | Encoded as number
2064−instance ToJSON SystemTime where
2065− toJSON (MkSystemTime secs nsecs) =
2066− toJSON (fromIntegral secs + fromIntegral nsecs / 1000000000 :: Nano)
2067− toEncoding (MkSystemTime secs nsecs) =
2068− toEncoding (fromIntegral secs + fromIntegral nsecs / 1000000000 :: Nano)
2069−
2070−instance ToJSON CalendarDiffTime where
2071− toJSON (CalendarDiffTime m nt) = object
2072− [ "months" .= m
2073− , "time" .= nt
2074− ]
2075− toEncoding (CalendarDiffTime m nt) = E.pairs
2076− ("months" .= m <> "time" .= nt)
2077−
2078−instance ToJSON CalendarDiffDays where
2079− toJSON (CalendarDiffDays m d) = object
2080− [ "months" .= m
2081− , "days" .= d
2082− ]
2083− toEncoding (CalendarDiffDays m d) = E.pairs
2084− ("months" .= m <> "days" .= d)
2085−
2086−instance ToJSON DayOfWeek where
2087− toJSON Monday = "monday"
2088− toJSON Tuesday = "tuesday"
2089− toJSON Wednesday = "wednesday"
2090− toJSON Thursday = "thursday"
2091− toJSON Friday = "friday"
2092− toJSON Saturday = "saturday"
2093− toJSON Sunday = "sunday"
2094−
2095−toEncodingDayOfWeek :: DayOfWeek -> E.Encoding' Text
2096−toEncodingDayOfWeek Monday = E.unsafeToEncoding "\"monday\""
2097−toEncodingDayOfWeek Tuesday = E.unsafeToEncoding "\"tuesday\""
2098−toEncodingDayOfWeek Wednesday = E.unsafeToEncoding "\"wednesday\""
2099−toEncodingDayOfWeek Thursday = E.unsafeToEncoding "\"thursday\""
2100−toEncodingDayOfWeek Friday = E.unsafeToEncoding "\"friday\""
2101−toEncodingDayOfWeek Saturday = E.unsafeToEncoding "\"saturday\""
2102−toEncodingDayOfWeek Sunday = E.unsafeToEncoding "\"sunday\""
2103−
2104−instance ToJSONKey DayOfWeek where
2105− toJSONKey = toJSONKeyTextEnc toEncodingDayOfWeek
2106−
2107-------------------------------------------------------------------------------
1920-------------------------------------------------------------------------------
2108-- base Monoid/Semigroup
1921-- base Monoid/Semigroup
2109-------------------------------------------------------------------------------
1922-------------------------------------------------------------------------------
@@ -2847,26 +2660,47 @@ instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f, ToJSON g,
2847 toEncoding = toEncoding2
2660 toEncoding = toEncoding2
2848 {-# INLINE toEncoding #-}
2661 {-# INLINE toEncoding #-}
2849
2662
2663+-------------------------------------------------------------------------------
2664+-- pre-bytestring-0.10 compatibility
2665+-------------------------------------------------------------------------------
2666+
2667+{-# INLINE lazyToStrictByteString #-}
2668+lazyToStrictByteString :: L.ByteString -> S.ByteString
2669+#if MIN_VERSION_bytestring(0,10,0)
2670+lazyToStrictByteString = L.toStrict
2671+#else
2672+lazyToStrictByteString = packChunks
2673+
2674+-- packChunks is taken from the blaze-builder package.
2675+
2676+-- | Pack the chunks of a lazy bytestring into a single strict bytestring.
2677+packChunks :: L.ByteString -> S.ByteString
2678+packChunks lbs =
2679+ S.unsafeCreate (fromIntegral $ L.length lbs) (copyChunks lbs)
2680+ where
2681+ copyChunks L.Empty _pf = return ()
2682+ copyChunks (L.Chunk (S.PS fpbuf o l) lbs') pf = do
2683+ withForeignPtr fpbuf $ \pbuf ->
2684+ copyBytes pf (pbuf `plusPtr` o) l
2685+ copyChunks lbs' (pf `plusPtr` l)
2686+#endif
2687+
2850--------------------------------------------------------------------------------
2688--------------------------------------------------------------------------------
2851
2689
2852−-- | Wrap a list of pairs as an object.
2853class Monoid pairs => FromPairs enc pairs | enc -> pairs where
2690class Monoid pairs => FromPairs enc pairs | enc -> pairs where
2854 fromPairs :: pairs -> enc
2691 fromPairs :: pairs -> enc
2855
2692
2856−instance (a ~ Value) => FromPairs (Encoding' a) Series where
2693+instance FromPairs Encoding Series where
2857 fromPairs = E.pairs
2694 fromPairs = E.pairs
2858
2695
2859instance FromPairs Value (DList Pair) where
2696instance FromPairs Value (DList Pair) where
2860 fromPairs = object . toList
2697 fromPairs = object . toList
2861
2698
2862−-- | Like 'KeyValue' but the value is already converted to JSON
2863−-- ('Value' or 'Encoding'), and the result actually represents lists of pairs
2864−-- so it can be readily concatenated.
2865−class Monoid kv => KeyValuePair v kv where
2699+class Monoid kv => GKeyValue v kv where
2866− pair :: String -> v -> kv
2700+ gPair :: String -> v -> kv
2867
2701
2868−instance (v ~ Value) => KeyValuePair v (DList Pair) where
2702+instance ToJSON v => GKeyValue v (DList Pair) where
2869− pair k v = DList.singleton (pack k .= v)
2703+ gPair k v = DList.singleton (pack k .= v)
2870
2704
2871−instance (e ~ Encoding) => KeyValuePair e Series where
2705+instance GKeyValue Encoding Series where
2872− pair = E.pairStr
2706+ gPair = E.pairStr
HLint.hsnew file+10
@@ -0,0 +1,10 @@
1+module HLint.HLint where
2+
3+import "hint" HLint.Default
4+import "hint" HLint.Builtin.All
5+import "hint" HLint.Dollar
6+
7+ignore "Avoid lambda"
8+ignore "Eta reduce"
9+ignore "Functor law"
10+ignore "Use >=>"
Makefile+1−1
@@ -1,2 +1,2 @@
1lint:
1lint:
2− ./run-hlint.sh --cpp-include include/ --cpp-file travis/cabal_macros.h Data/ attoparsec-iso8601/ benchmarks/ examples/ ffi/ pure/ tests/
2+ hlint --cpp-include include/ --cpp-file .stack-work/dist/*/*Cabal-*/build/autogen/cabal_macros.h Data/ attoparsec-iso8601/ benchmarks/ examples/ ffi/ pure/ tests/
README.markdown+2−1
@@ -24,4 +24,5 @@ See what's changed in recent (and upcoming) releases:
24
24
25# Authors
25# Authors
26
26
27−This library was originally written by Bryan O'Sullivan.
27+This library is written and maintained by Bryan O'Sullivan,
28+<bos@serpentine.com>.
aeson.cabal+53−65
@@ -1,5 +1,5 @@
1name: aeson
1name: aeson
2−version: 1.4.6.0
2+version: 1.2.1.0
3license: BSD3
3license: BSD3
4license-file: LICENSE
4license-file: LICENSE
5category: Text, Web, JSON
5category: Text, Web, JSON
@@ -21,6 +21,20 @@ description:
21 To get started, see the documentation for the @Data.Aeson@ module
21 To get started, see the documentation for the @Data.Aeson@ module
22 below.
22 below.
23 .
23 .
24+ Parsing performance on a late 2013 MacBook Pro (2.6GHz Core i7),
25+ running 64-bit GHC 7.10.1, for mostly-English tweets from Twitter's
26+ JSON search API:
27+ .
28+ * 6.4 KB payloads, English: 7570 msg\/sec (47.6 MB\/sec)
29+ .
30+ * 14.6 KB payloads, Japanese: 3261 msg\/sec (46.6 MB\/sec)
31+ .
32+ Encoding performance on the same machine and data:
33+ .
34+ * 6.4 KB payloads, English: 22738 msg\/sec (142.9 MB\/sec)
35+ .
36+ * 14.6 KB payloads, Japanese: 15911 msg\/sec (227.4 MB\/sec)
37+ .
24 (A note on naming: in Greek mythology, Aeson was the father of Jason.)
38 (A note on naming: in Greek mythology, Aeson was the father of Jason.)
25
39
26extra-source-files:
40extra-source-files:
@@ -42,7 +56,6 @@ extra-source-files:
42 include/*.h
56 include/*.h
43 tests/JSONTestSuite/test_parsing/*.json
57 tests/JSONTestSuite/test_parsing/*.json
44 tests/JSONTestSuite/test_transform/*.json
58 tests/JSONTestSuite/test_transform/*.json
45− tests/golden/*.expected
46 pure/Data/Aeson/Parser/*.hs
59 pure/Data/Aeson/Parser/*.hs
47
60
48flag developer
61flag developer
@@ -76,7 +89,6 @@ library
76 Data.Aeson.Text
89 Data.Aeson.Text
77 Data.Aeson.Types
90 Data.Aeson.Types
78 Data.Aeson.TH
91 Data.Aeson.TH
79− Data.Aeson.QQ.Simple
80
92
81 Data.Aeson.Encoding.Internal
93 Data.Aeson.Encoding.Internal
82 Data.Aeson.Internal
94 Data.Aeson.Internal
@@ -100,59 +112,43 @@ library
100 Data.Attoparsec.Time
112 Data.Attoparsec.Time
101 Data.Attoparsec.Time.Internal
113 Data.Attoparsec.Time.Internal
102
114
103− -- GHC bundled libs
104 build-depends:
115 build-depends:
105− base >= 4.7.0.0 && < 5,
116+ attoparsec >= 0.13.0.1,
106− bytestring >= 0.10.4.0 && < 0.11,
117+ base >= 4.5 && < 5,
118+ base-compat >= 0.9.1 && < 0.10,
107− containers >= 0.5.5.1 && < 0.7,
119+ containers >= 0.4.2,
108− deepseq >= 1.3.0.0 && < 1.5,
120+ deepseq >= 1.3 && < 1.5,
109− ghc-prim >= 0.2 && < 0.6,
121+ dlist >= 0.6,
122+ ghc-prim >= 0.2,
123+ hashable >= 1.1.2.0,
124+ scientific >= 0.3.4.7 && < 0.4,
125+ tagged >=0.8.3 && <0.9,
110− template-haskell >= 2.9.0.0 && < 2.16,
126+ template-haskell >= 2.7,
111− text >= 1.2.3.0 && < 1.3,
127+ text >= 1.1.1.0,
128+ th-abstraction >= 0.2.2 && < 0.3,
112− time >= 1.4 && < 1.10
129+ time >= 1.1.1.4,
113−
130+ time-locale-compat >= 0.1.1 && < 0.2,
114− if impl(ghc >= 8.0)
131+ unordered-containers >= 0.2.5.0 && < 0.3,
115− build-depends: bytestring >= 0.10.8.1
132+ uuid-types >= 1.0.3 && <1.1,
116−
133+ vector >= 0.8
117− -- Compat
118− build-depends:
119− base-compat >= 0.9.1 && < 0.12,
120− time-compat >= 1.9.2.2 && < 1.10
121
134
122− if !impl(ghc >= 8.6)
135+ if flag(bytestring-builder)
123− build-depends:
136+ build-depends: bytestring >= 0.9.2 && < 0.10.4,
124− contravariant >=1.4.1 && <1.6
137+ bytestring-builder >= 0.10.4 && < 1
138+ else
139+ build-depends: bytestring >= 0.10.4 && < 0.11
125
140
126 if !impl(ghc >= 8.0)
141 if !impl(ghc >= 8.0)
127 -- `Data.Semigroup` and `Control.Monad.Fail` and `Control.Monad.IO.Class` are available in base only since GHC 8.0 / base 4.9
142 -- `Data.Semigroup` and `Control.Monad.Fail` and `Control.Monad.IO.Class` are available in base only since GHC 8.0 / base 4.9
128 build-depends:
143 build-depends:
129− semigroups >= 0.18.5 && < 0.20,
144+ semigroups >= 0.18.2 && < 0.19,
130− transformers >= 0.3.0.0 && < 0.6,
145+ transformers >= 0.2.2.0,
131− transformers-compat >= 0.6.2 && < 0.7,
146+ transformers-compat >= 0.3,
132 fail == 4.9.*
147 fail == 4.9.*
133
148
134 if !impl(ghc >= 7.10)
149 if !impl(ghc >= 7.10)
135 -- `Numeric.Natural` is available in base only since GHC 7.10 / base 4.8
150 -- `Numeric.Natural` is available in base only since GHC 7.10 / base 4.8
136− build-depends: nats >= 1.1.1 && < 1.2,
151+ build-depends: nats >= 1 && < 1.2
137− void >= 0.7.2 && < 0.8
138−
139− -- cannot use latest version
140− build-depends:
141− unordered-containers >= 0.2.8.0 && < 0.3,
142−
143− -- not in LTS-12.10
144− tagged >= 0.8.5 && < 0.9,
145− primitive >= 0.6.3.0 && < 0.8
146−
147− -- Other dependencies
148− build-depends:
149− attoparsec >= 0.13.2.2 && < 0.14,
150− dlist >= 0.8.0.4 && < 0.9,
151− hashable >= 1.2.7.0 && < 1.4,
152− scientific >= 0.3.6.2 && < 0.4,
153− th-abstraction >= 0.2.8.0 && < 0.4,
154− uuid-types >= 1.0.3 && < 1.1,
155− vector >= 0.12.0.1 && < 0.13
156
152
157 ghc-options: -Wall
153 ghc-options: -Wall
158
154
@@ -195,49 +191,42 @@ test-suite tests
195 Functions
191 Functions
196 Instances
192 Instances
197 Options
193 Options
198− PropUtils
199 Properties
194 Properties
200− PropertyGeneric
201− PropertyKeys
202− PropertyRoundTrip
203− PropertyRTFunctors
204− PropertyTH
205 SerializationFormatSpec
195 SerializationFormatSpec
206 Types
196 Types
207 UnitTests
197 UnitTests
208 UnitTests.NullaryConstructors
198 UnitTests.NullaryConstructors
209
199
210 build-depends:
200 build-depends:
201+ HUnit,
211− QuickCheck >= 2.10.0.1 && < 2.14,
202+ QuickCheck >= 2.8 && <2.11,
212 aeson,
203 aeson,
213 integer-logarithms >= 1 && <1.1,
204 integer-logarithms >= 1 && <1.1,
214 attoparsec,
205 attoparsec,
215 base,
206 base,
216 base-compat,
207 base-compat,
217− base-orphans >= 0.5.3 && <0.9,
208+ base-orphans >= 0.5.3 && <0.7,
218 base16-bytestring,
209 base16-bytestring,
219 containers,
210 containers,
220 directory,
211 directory,
221 dlist,
212 dlist,
222− Diff >= 0.4 && < 0.5,
223 filepath,
213 filepath,
224− generic-deriving >= 1.10 && < 1.14,
214+ generic-deriving >= 1.10 && < 1.12,
225 ghc-prim >= 0.2,
215 ghc-prim >= 0.2,
226 hashable >= 1.2.4.0,
216 hashable >= 1.2.4.0,
227 scientific,
217 scientific,
228 tagged,
218 tagged,
229 template-haskell,
219 template-haskell,
230− tasty,
220+ test-framework,
231− tasty-golden,
221+ test-framework-hunit,
232− tasty-hunit,
222+ test-framework-quickcheck2,
233− tasty-quickcheck,
234 text,
223 text,
235 time,
224 time,
236− time-compat,
225+ time-locale-compat,
237 unordered-containers,
226 unordered-containers,
238 uuid-types,
227 uuid-types,
239 vector,
228 vector,
240− quickcheck-instances >= 0.3.21 && <0.4
229+ quickcheck-instances >=0.3.14
241
230
242 if flag(bytestring-builder)
231 if flag(bytestring-builder)
243 build-depends: bytestring >= 0.9 && < 0.10.4,
232 build-depends: bytestring >= 0.9 && < 0.10.4,
@@ -247,13 +236,12 @@ test-suite tests
247
236
248 if !impl(ghc >= 8.0)
237 if !impl(ghc >= 8.0)
249 build-depends:
238 build-depends:
250− semigroups >= 0.18.2 && < 0.20,
239+ semigroups >= 0.18.2 && < 0.19,
251 transformers >= 0.2.2.0,
240 transformers >= 0.2.2.0,
252 transformers-compat >= 0.3
241 transformers-compat >= 0.3
253
242
254 if !impl(ghc >= 7.10)
243 if !impl(ghc >= 7.10)
255− build-depends: nats >=1 && <1.2,
244+ build-depends: nats >=1 && <1.2
256− void >=0.7.2 && <0.8
257
245
258 if impl(ghc >= 7.8)
246 if impl(ghc >= 7.8)
259 build-depends: hashable-time >= 0.2 && <0.3
247 build-depends: hashable-time >= 0.2 && <0.3
attoparsec-iso8601/Data/Attoparsec/Time.hs+5−4
@@ -1,6 +1,6 @@
1{-# LANGUAGE BangPatterns #-}
1{-# LANGUAGE BangPatterns #-}
2−{-# LANGUAGE NoImplicitPrelude #-}
3{-# LANGUAGE ScopedTypeVariables #-}
2{-# LANGUAGE ScopedTypeVariables #-}
3+
4-- |
4-- |
5-- Module: Data.Aeson.Parser.Time
5-- Module: Data.Aeson.Parser.Time
6-- Copyright: (c) 2015-2016 Bryan O'Sullivan
6-- Copyright: (c) 2015-2016 Bryan O'Sullivan
@@ -21,6 +21,7 @@ module Data.Attoparsec.Time
21 , zonedTime
21 , zonedTime
22 ) where
22 ) where
23
23
24+import Prelude ()
24import Prelude.Compat
25import Prelude.Compat
25
26
26import Control.Applicative ((<|>))
27import Control.Applicative ((<|>))
@@ -41,9 +42,9 @@ import qualified Data.Time.LocalTime as Local
41day :: Parser Day
42day :: Parser Day
42day = do
43day = do
43 absOrNeg <- negate <$ char '-' <|> id <$ char '+' <|> pure id
44 absOrNeg <- negate <$ char '-' <|> id <$ char '+' <|> pure id
44− y <- (decimal <* char '-') <|> fail "date must be of form [+,-]YYYY-MM-DD"
45+ y <- decimal <* char '-'
45− m <- (twoDigits <* char '-') <|> fail "date must be of form [+,-]YYYY-MM-DD"
46+ m <- twoDigits <* char '-'
46− d <- twoDigits <|> fail "date must be of form [+,-]YYYY-MM-DD"
47+ d <- twoDigits
47 maybe (fail "invalid date") return (fromGregorianValid (absOrNeg y) m d)
48 maybe (fail "invalid date") return (fromGregorianValid (absOrNeg y) m d)
48
49
49-- | Parse a two-digit integer (e.g. day of month, hour).
50-- | Parse a two-digit integer (e.g. day of month, hour).
attoparsec-iso8601/Data/Attoparsec/Time/Internal.hs+18−21
@@ -1,5 +1,5 @@
1{-# LANGUAGE CPP #-}
1{-# LANGUAGE CPP #-}
2−{-# LANGUAGE NoImplicitPrelude #-}
2+
3-- |
3-- |
4-- Module: Data.Aeson.Internal.Time
4-- Module: Data.Aeson.Internal.Time
5-- Copyright: (c) 2015-2016 Bryan O'Sullivan
5-- Copyright: (c) 2015-2016 Bryan O'Sullivan
@@ -17,48 +17,45 @@ module Data.Attoparsec.Time.Internal
17 , toTimeOfDay64
17 , toTimeOfDay64
18 ) where
18 ) where
19
19
20+import Prelude ()
20import Prelude.Compat
21import Prelude.Compat
21
22
22import Data.Int (Int64)
23import Data.Int (Int64)
23import Data.Time
24import Data.Time
24import Unsafe.Coerce (unsafeCoerce)
25import Unsafe.Coerce (unsafeCoerce)
25
26
26−#if MIN_VERSION_time(1,6,0)
27+#if MIN_VERSION_base(4,7,0)
27−
28−import Data.Time.Clock (diffTimeToPicoseconds)
29−
30−#endif
31
28
32import Data.Fixed (Pico, Fixed(MkFixed))
29import Data.Fixed (Pico, Fixed(MkFixed))
33
30
34−#if !MIN_VERSION_time(1,6,0)
31+toPico :: Integer -> Pico
32+toPico = MkFixed
35
33
36−diffTimeToPicoseconds :: DiffTime -> Integer
34+fromPico :: Pico -> Integer
37−diffTimeToPicoseconds = unsafeCoerce
35+fromPico (MkFixed i) = i
38
36
39−#endif
37+#else
38+
39+import Data.Fixed (Pico)
40
40
41toPico :: Integer -> Pico
41toPico :: Integer -> Pico
42−toPico = MkFixed
42+toPico = unsafeCoerce
43
43
44fromPico :: Pico -> Integer
44fromPico :: Pico -> Integer
45−fromPico (MkFixed i) = i
45+fromPico = unsafeCoerce
46+
47+#endif
46
48
47-- | Like TimeOfDay, but using a fixed-width integer for seconds.
49-- | Like TimeOfDay, but using a fixed-width integer for seconds.
48data TimeOfDay64 = TOD {-# UNPACK #-} !Int
50data TimeOfDay64 = TOD {-# UNPACK #-} !Int
49 {-# UNPACK #-} !Int
51 {-# UNPACK #-} !Int
50 {-# UNPACK #-} !Int64
52 {-# UNPACK #-} !Int64
51
53
52−posixDayLength :: DiffTime
53−posixDayLength = 86400
54−
55diffTimeOfDay64 :: DiffTime -> TimeOfDay64
54diffTimeOfDay64 :: DiffTime -> TimeOfDay64
56−diffTimeOfDay64 t
57− | t >= posixDayLength = TOD 23 59 (60000000000000 + pico (t - posixDayLength))
58− | otherwise = TOD (fromIntegral h) (fromIntegral m) s
55+diffTimeOfDay64 t = TOD (fromIntegral h) (fromIntegral m) s
59− where (h,mp) = pico t `quotRem` 3600000000000000
56+ where (h,mp) = fromIntegral pico `quotRem` 3600000000000000
60− (m,s) = mp `quotRem` 60000000000000
57+ (m,s) = mp `quotRem` 60000000000000
61− pico = fromIntegral . diffTimeToPicoseconds
58+ pico = unsafeCoerce t :: Integer
62
59
63toTimeOfDay64 :: TimeOfDay -> TimeOfDay64
60toTimeOfDay64 :: TimeOfDay -> TimeOfDay64
64toTimeOfDay64 (TimeOfDay h m s) = TOD h m (fromIntegral (fromPico s))
61toTimeOfDay64 (TimeOfDay h m s) = TOD h m (fromIntegral (fromPico s))
attoparsec-iso8601/attoparsec-iso8601.cabal+3−3
@@ -1,5 +1,5 @@
1name: attoparsec-iso8601
1name: attoparsec-iso8601
2−version: 1.0.1.0
2+version: 1.0.0.0
3synopsis: Parsing of ISO 8601 dates, originally from aeson.
3synopsis: Parsing of ISO 8601 dates, originally from aeson.
4description: Parsing of ISO 8601 dates, originally from aeson.
4description: Parsing of ISO 8601 dates, originally from aeson.
5license: BSD3
5license: BSD3
@@ -36,8 +36,8 @@ library
36 Data.Attoparsec.Time
36 Data.Attoparsec.Time
37 build-depends:
37 build-depends:
38 attoparsec >= 0.13.0.1,
38 attoparsec >= 0.13.0.1,
39− base >= 4.7 && < 5,
39+ base >= 4.5 && < 5,
40− base-compat >= 0.9.1 && < 0.12,
40+ base-compat >= 0.9.1 && < 0.10,
41 text >= 1.1.1.0,
41 text >= 1.1.1.0,
42 time >= 1.1.1.4
42 time >= 1.1.1.4
43
43
attoparsec-iso8601/changelog.mddeleted−5
@@ -1,5 +0,0 @@
1−For the latest version of this document, please see [https://github.com/bos/aeson/blob/master/attoparsec-iso8601/changelog.md](https://github.com/bos/aeson/blob/master/attoparsec-iso8601/changelog.md).
2−
3−### 1.0.1.0
4−
5−* Fixes handling of `UTCTime` wrt. leap seconds , thanks to Adam Schønemann
benchmarks/AesonCompareAutoInstances.hsnew file+282
@@ -0,0 +1,282 @@
1+{-# LANGUAGE DeriveGeneric #-}
2+{-# LANGUAGE FlexibleContexts #-}
3+{-# LANGUAGE TemplateHaskell #-}
4+
5+module Main (main) where
6+
7+import Prelude ()
8+import Prelude.Compat
9+
10+import Control.Monad
11+import Control.DeepSeq (NFData, rnf, deepseq)
12+import Criterion.Main hiding (defaultOptions)
13+import Data.Aeson
14+import Data.Aeson.Encoding
15+import Data.Aeson.TH
16+import Data.Aeson.Types
17+import Data.ByteString.Lazy (ByteString)
18+import Data.Data (Data)
19+import Data.Typeable (Typeable)
20+import GHC.Generics (Generic, Rep)
21+import Options
22+
23+toBS :: Encoding -> ByteString
24+toBS = encodingToLazyByteString
25+
26+gEncode :: (Generic a, GToEncoding Zero (Rep a)) => a -> ByteString
27+gEncode = toBS . genericToEncoding opts
28+
29+--------------------------------------------------------------------------------
30+
31+data D a = Nullary
32+ | Unary Int
33+ | Product String Char a
34+ | Record { testOne :: Double
35+ , testTwo :: Bool
36+ , testThree :: D a
37+ }
38+ deriving (Show, Eq)
39+
40+deriveJSON opts ''D
41+
42+instance NFData a => NFData (D a) where
43+ rnf Nullary = ()
44+ rnf (Unary n) = rnf n
45+ rnf (Product s c x) = s `deepseq` c `deepseq` rnf x
46+ rnf (Record d b y) = d `deepseq` b `deepseq` rnf y
47+
48+type T = D (D (D ()))
49+
50+d :: T
51+d = Record
52+ { testOne = 1234.56789
53+ , testTwo = True
54+ , testThree = Product "Hello World!" 'a'
55+ Record
56+ { testOne = 9876.54321
57+ , testTwo = False
58+ , testThree = Product "Yeehaa!!!" '\n' Nullary
59+ }
60+ }
61+
62+--------------------------------------------------------------------------------
63+
64+data D' a = Nullary'
65+ | Unary' Int
66+ | Product' String Char a
67+ | Record' { testOne' :: Double
68+ , testTwo' :: Bool
69+ , testThree' :: D' a
70+ }
71+ deriving (Show, Eq, Generic)
72+
73+instance ToJSON a => ToJSON (D' a) where
74+ toJSON = genericToJSON opts
75+
76+instance FromJSON a => FromJSON (D' a) where
77+ parseJSON = genericParseJSON opts
78+
79+instance NFData a => NFData (D' a) where
80+ rnf Nullary' = ()
81+ rnf (Unary' n) = rnf n
82+ rnf (Product' s c x) = s `deepseq` c `deepseq` rnf x
83+ rnf (Record' d b y) = d `deepseq` b `deepseq` rnf y
84+
85+type T' = D' (D' (D' ()))
86+
87+d' :: T'
88+d' = Record'
89+ { testOne' = 1234.56789
90+ , testTwo' = True
91+ , testThree' = Product' "Hello World!" 'a'
92+ Record'
93+ { testOne' = 9876.54321
94+ , testTwo' = False
95+ , testThree' = Product' "Yeehaa!!!" '\n' Nullary'
96+ }
97+ }
98+
99+--------------------------------------------------------------------------------
100+
101+data BigRecord = BigRecord
102+ { field01 :: !Int, field02 :: !Int, field03 :: !Int, field04 :: !Int, field05 :: !Int
103+ , field06 :: !Int, field07 :: !Int, field08 :: !Int, field09 :: !Int, field10 :: !Int
104+ , field11 :: !Int, field12 :: !Int, field13 :: !Int, field14 :: !Int, field15 :: !Int
105+ , field16 :: !Int, field17 :: !Int, field18 :: !Int, field19 :: !Int, field20 :: !Int
106+ , field21 :: !Int, field22 :: !Int, field23 :: !Int, field24 :: !Int, field25 :: !Int
107+ } deriving (Show, Eq, Generic)
108+
109+instance NFData BigRecord
110+
111+bigRecord = BigRecord 1 2 3 4 5
112+ 6 7 8 9 10
113+ 11 12 13 14 15
114+ 16 17 18 19 20
115+ 21 22 23 24 25
116+
117+return []
118+
119+gBigRecordToJSON :: BigRecord -> Value
120+gBigRecordToJSON = genericToJSON opts
121+
122+gBigRecordEncode :: BigRecord -> ByteString
123+gBigRecordEncode = gEncode
124+
125+gBigRecordFromJSON :: Value -> Result BigRecord
126+gBigRecordFromJSON = parse $ genericParseJSON opts
127+
128+thBigRecordToJSON :: BigRecord -> Value
129+thBigRecordToJSON = $(mkToJSON opts ''BigRecord)
130+
131+thBigRecordEncode :: BigRecord -> ByteString
132+thBigRecordEncode = toBS . $(mkToEncoding opts ''BigRecord)
133+
134+thBigRecordFromJSON :: Value -> Result BigRecord
135+thBigRecordFromJSON = parse $(mkParseJSON opts ''BigRecord)
136+
137+--------------------------------------------------------------------------------
138+
139+data BigProduct = BigProduct
140+ !Int !Int !Int !Int !Int
141+ !Int !Int !Int !Int !Int
142+ !Int !Int !Int !Int !Int
143+ !Int !Int !Int !Int !Int
144+ !Int !Int !Int !Int !Int
145+ deriving (Show, Eq, Generic)
146+
147+instance NFData BigProduct
148+
149+bigProduct = BigProduct 1 2 3 4 5
150+ 6 7 8 9 10
151+ 11 12 13 14 15
152+ 16 17 18 19 20
153+ 21 22 23 24 25
154+
155+return []
156+
157+gBigProductToJSON :: BigProduct -> Value
158+gBigProductToJSON = genericToJSON opts
159+
160+gBigProductEncode :: BigProduct -> ByteString
161+gBigProductEncode = gEncode
162+
163+gBigProductFromJSON :: Value -> Result BigProduct
164+gBigProductFromJSON = parse $ genericParseJSON opts
165+
166+thBigProductToJSON :: BigProduct -> Value
167+thBigProductToJSON = $(mkToJSON opts ''BigProduct)
168+
169+thBigProductEncode :: BigProduct -> ByteString
170+thBigProductEncode = toBS . $(mkToEncoding opts ''BigProduct)
171+
172+thBigProductFromJSON :: Value -> Result BigProduct
173+thBigProductFromJSON = parse $(mkParseJSON opts ''BigProduct)
174+
175+--------------------------------------------------------------------------------
176+
177+data BigSum = F01 | F02 | F03 | F04 | F05
178+ | F06 | F07 | F08 | F09 | F10
179+ | F11 | F12 | F13 | F14 | F15
180+ | F16 | F17 | F18 | F19 | F20
181+ | F21 | F22 | F23 | F24 | F25
182+ deriving (Show, Eq, Generic)
183+
184+instance NFData BigSum
185+
186+bigSum = F25
187+
188+return []
189+
190+gBigSumToJSON :: BigSum -> Value
191+gBigSumToJSON = genericToJSON opts
192+
193+gBigSumEncode :: BigSum -> ByteString
194+gBigSumEncode = gEncode
195+
196+gBigSumFromJSON :: Value -> Result BigSum
197+gBigSumFromJSON = parse $ genericParseJSON opts
198+
199+thBigSumToJSON :: BigSum -> Value
200+thBigSumToJSON = $(mkToJSON opts ''BigSum)
201+
202+thBigSumEncode :: BigSum -> ByteString
203+thBigSumEncode = toBS . $(mkToEncoding opts ''BigSum)
204+
205+thBigSumFromJSON :: Value -> Result BigSum
206+thBigSumFromJSON = parse $(mkParseJSON opts ''BigSum)
207+
208+--------------------------------------------------------------------------------
209+
210+type FJ a = Value -> Result a
211+
212+runBench :: IO ()
213+runBench = defaultMain
214+ [ let v = toJSON d
215+ in (d, d', v) `deepseq`
216+ bgroup "D"
217+ [ group "toJSON" (nf toJSON d)
218+ (nf toJSON d')
219+ , group "encode" (nf encode d)
220+ (nf encode d')
221+ , group "fromJSON" (nf ( fromJSON :: FJ T ) v)
222+ (nf ( fromJSON :: FJ T') v)
223+ ]
224+ , let v = thBigRecordToJSON bigRecord
225+ in bigRecord `deepseq` v `deepseq`
226+ bgroup "BigRecord"
227+ [ group "toJSON" (nf thBigRecordToJSON bigRecord)
228+ (nf gBigRecordToJSON bigRecord)
229+ , group "encode" (nf thBigRecordEncode bigRecord)
230+ (nf gBigRecordEncode bigRecord)
231+ , group "fromJSON" (nf (thBigRecordFromJSON :: FJ BigRecord) v)
232+ (nf ( gBigRecordFromJSON :: FJ BigRecord) v)
233+ ]
234+ , let v = thBigProductToJSON bigProduct
235+ in bigProduct `deepseq` v `deepseq`
236+ bgroup "BigProduct"
237+ [ group "toJSON" (nf thBigProductToJSON bigProduct)
238+ (nf gBigProductToJSON bigProduct)
239+ , group "encode" (nf thBigProductEncode bigProduct)
240+ (nf gBigProductEncode bigProduct)
241+ , group "fromJSON" (nf (thBigProductFromJSON :: FJ BigProduct) v)
242+ (nf (gBigProductFromJSON :: FJ BigProduct) v)
243+ ]
244+ , let v = thBigSumToJSON bigSum
245+ in bigSum `deepseq` v `deepseq`
246+ bgroup "BigSum"
247+ [ group "toJSON" (nf thBigSumToJSON bigSum)
248+ (nf gBigSumToJSON bigSum)
249+ , group "encode" (nf thBigSumEncode bigSum)
250+ (nf gBigSumEncode bigSum)
251+ , group "fromJSON" (nf (thBigSumFromJSON :: FJ BigSum) v)
252+ (nf (gBigSumFromJSON :: FJ BigSum) v)
253+ ]
254+ ]
255+
256+group n th gen = bgroup n [ bench "th" th
257+ , bench "generic" gen
258+ ]
259+
260+sanityCheck = do
261+ check d toJSON fromJSON encode
262+ check d' toJSON fromJSON encode
263+ check bigRecord thBigRecordToJSON thBigRecordFromJSON thBigRecordEncode
264+ check bigRecord gBigRecordToJSON gBigRecordFromJSON gBigRecordEncode
265+ check bigProduct thBigProductToJSON thBigProductFromJSON thBigProductEncode
266+ check bigProduct gBigProductToJSON gBigProductFromJSON gBigProductEncode
267+ check bigSum thBigSumToJSON thBigSumFromJSON thBigSumEncode
268+ check bigSum gBigSumToJSON gBigSumFromJSON gBigSumEncode
269+
270+check :: (Show a, Eq a)
271+ => a -> (a -> Value) -> (Value -> Result a) -> (a -> ByteString) -> IO ()
272+check x toJSON fromJSON encode = do
273+ unless (Success x == (fromJSON . toJSON) x) $ fail $ "toJSON: " ++ show x
274+ unless (Success x == (decode' . encode) x) $ fail $ "encode: " ++ show x
275+ where
276+ decode' s = case decode s of
277+ Just v -> fromJSON v
278+ Nothing -> fail ""
279+
280+main = do
281+ sanityCheck
282+ runBench
benchmarks/AesonEncode.hs+2−2
@@ -1,13 +1,13 @@
1{-# LANGUAGE BangPatterns #-}
1{-# LANGUAGE BangPatterns #-}
2−{-# LANGUAGE NoImplicitPrelude #-}
3{-# LANGUAGE OverloadedStrings #-}
2{-# LANGUAGE OverloadedStrings #-}
4
3
5module Main (main) where
4module Main (main) where
6
5
6+import Prelude ()
7import Prelude.Compat
7import Prelude.Compat
8
8
9import Control.DeepSeq
9import Control.DeepSeq
10−import Control.Monad (forM_)
10+import Control.Monad
11import Data.Aeson
11import Data.Aeson
12import Data.Attoparsec.ByteString (IResult(..), parseWith)
12import Data.Attoparsec.ByteString (IResult(..), parseWith)
13import Data.Char (isDigit)
13import Data.Char (isDigit)
benchmarks/AesonFoldable.hs+42−14
@@ -1,14 +1,16 @@
1−{-# LANGUAGE NoImplicitPrelude #-}
2{-# LANGUAGE OverloadedStrings #-}
1{-# LANGUAGE OverloadedStrings #-}
2+{-# LANGUAGE PackageImports #-}
3
3
4module Main (main) where
4module Main (main) where
5
5
6import Criterion.Main
6import Criterion.Main
7
7
8+import Prelude ()
8import Prelude.Compat
9import Prelude.Compat
9
10
10import Data.Foldable (toList)
11import Data.Foldable (toList)
11−import qualified Data.Aeson as A
12+import qualified "aeson" Data.Aeson as A
13+import qualified "aeson-benchmarks" Data.Aeson as B
12import qualified Data.Sequence as S
14import qualified Data.Sequence as S
13import qualified Data.Vector as V
15import qualified Data.Vector as V
14import qualified Data.Vector.Unboxed as U
16import qualified Data.Vector.Unboxed as U
@@ -19,6 +21,10 @@ import qualified Data.Vector.Unboxed as U
19
21
20newtype L f = L { getL :: f Int }
22newtype L f = L { getL :: f Int }
21
23
24+instance Foldable f => B.ToJSON (L f) where
25+ toJSON = error "do not use this"
26+ toEncoding = B.toEncoding . toList . getL
27+
22instance Foldable f => A.ToJSON (L f) where
28instance Foldable f => A.ToJSON (L f) where
23 toJSON = error "do not use this"
29 toJSON = error "do not use this"
24 toEncoding = A.toEncoding . toList . getL
30 toEncoding = A.toEncoding . toList . getL
@@ -29,6 +35,10 @@ instance Foldable f => A.ToJSON (L f) where
29
35
30newtype F f = F { getF :: f Int }
36newtype F f = F { getF :: f Int }
31
37
38+instance Foldable f => B.ToJSON (F f) where
39+ toJSON = error "do not use this"
40+ toEncoding = B.foldable . getF
41+
32instance Foldable f => A.ToJSON (F f) where
42instance Foldable f => A.ToJSON (F f) where
33 toJSON = error "do not use this"
43 toJSON = error "do not use this"
34 toEncoding = A.foldable . getF
44 toEncoding = A.foldable . getF
@@ -53,34 +63,52 @@ valueUVector = U.fromList valueList
53-- Main
63-- Main
54-------------------------------------------------------------------------------
64-------------------------------------------------------------------------------
55
65
56−benchEncode
66+benchEncodeA
57 :: A.ToJSON a
67 :: A.ToJSON a
58 => String
68 => String
59 -> a
69 -> a
60 -> Benchmark
70 -> Benchmark
61−benchEncode name val
71+benchEncodeA name val
62 = bench ("A " ++ name) $ nf A.encode val
72 = bench ("A " ++ name) $ nf A.encode val
63
73
74+benchEncodeB
75+ :: B.ToJSON a
76+ => String
77+ -> a
78+ -> Benchmark
79+benchEncodeB name val
80+ = bench ("B " ++ name) $ nf B.encode val
81+
64main :: IO ()
82main :: IO ()
65main = defaultMain
83main = defaultMain
66 [ bgroup "encode"
84 [ bgroup "encode"
67 [ bgroup "List"
85 [ bgroup "List"
68− [ benchEncode "-" valueList
86+ [ benchEncodeB "-" valueList
69− , benchEncode "L" $ L valueList
87+ , benchEncodeB "L" $ L valueList
88+ , benchEncodeB "F" $ F valueList
89+ , benchEncodeA "-" valueList
90+ , benchEncodeA "L" $ L valueList
70− , benchEncode "F" $ F valueList
91+ , benchEncodeA "F" $ F valueList
71 ]
92 ]
72 , bgroup "Seq"
93 , bgroup "Seq"
73− [ benchEncode "-" valueSeq
94+ [ benchEncodeB "-" valueSeq
74− , benchEncode "L" $ L valueSeq
95+ , benchEncodeB "L" $ L valueSeq
96+ , benchEncodeB "F" $ F valueSeq
97+ , benchEncodeA "-" valueSeq
98+ , benchEncodeA "L" $ L valueSeq
75− , benchEncode "F" $ F valueSeq
99+ , benchEncodeA "F" $ F valueSeq
76 ]
100 ]
77 , bgroup "Vector"
101 , bgroup "Vector"
78− [ benchEncode "-" valueVector
102+ [ benchEncodeB "-" valueVector
79− , benchEncode "L" $ L valueVector
103+ , benchEncodeB "L" $ L valueVector
104+ , benchEncodeB "F" $ F valueVector
105+ , benchEncodeA "-" valueVector
106+ , benchEncodeA "L" $ L valueVector
80− , benchEncode "F" $ F valueVector
107+ , benchEncodeA "F" $ F valueVector
81 ]
108 ]
82 , bgroup "Vector.Unboxed"
109 , bgroup "Vector.Unboxed"
110+ [ benchEncodeB "-" valueUVector
83− [ benchEncode "-" valueUVector
111+ , benchEncodeA "-" valueUVector
84 ]
112 ]
85 ]
113 ]
86 ]
114 ]
benchmarks/AesonMap.hs+56−39
@@ -1,11 +1,13 @@
1{-# LANGUAGE FlexibleContexts #-}
1{-# LANGUAGE FlexibleContexts #-}
2−{-# LANGUAGE NoImplicitPrelude #-}
2+{-# LANGUAGE OverloadedStrings #-}
3+{-# LANGUAGE PackageImports #-}
3{-# LANGUAGE RankNTypes #-}
4{-# LANGUAGE RankNTypes #-}
4
5
5{-# OPTIONS_GHC -fno-warn-orphans #-}
6{-# OPTIONS_GHC -fno-warn-orphans #-}
6
7
7module Main (main) where
8module Main (main) where
8
9
10+import Prelude ()
9import Prelude.Compat
11import Prelude.Compat
10
12
11import Control.DeepSeq
13import Control.DeepSeq
@@ -13,8 +15,9 @@ import Criterion.Main
13import Data.Hashable
15import Data.Hashable
14import Data.Proxy (Proxy (..))
16import Data.Proxy (Proxy (..))
15import Data.Tagged (Tagged (..))
17import Data.Tagged (Tagged (..))
16−import Data.Aeson
18+import qualified "aeson" Data.Aeson as A
19+import qualified "aeson-benchmarks" Data.Aeson as B
17−import Data.Aeson.Types (fromJSONKeyCoerce)
20+import qualified "aeson-benchmarks" Data.Aeson.Types as B (fromJSONKeyCoerce)
18import qualified Data.ByteString.Lazy as LBS
21import qualified Data.ByteString.Lazy as LBS
19import qualified Data.HashMap.Strict as HM
22import qualified Data.HashMap.Strict as HM
20import qualified Data.Map as M
23import qualified Data.Map as M
@@ -44,10 +47,10 @@ instance NFData T1 where
44instance Hashable T1 where
47instance Hashable T1 where
45 hashWithSalt salt (T1 t) = hashWithSalt salt t
48 hashWithSalt salt (T1 t) = hashWithSalt salt t
46
49
47−instance FromJSON T1 where
50+instance B.FromJSON T1 where
48− parseJSON = withText "T1" $ pure . T1
51+ parseJSON = B.withText "T1" $ pure . T1
49−instance FromJSONKey T1 where
52+instance B.FromJSONKey T1 where
50− fromJSONKey = FromJSONKeyText T1
53+ fromJSONKey = B.FromJSONKeyText T1
51
54
52-------------------------------------------------------------------------------
55-------------------------------------------------------------------------------
53-- Coerce
56-- Coerce
@@ -61,10 +64,10 @@ instance NFData T2 where
61instance Hashable T2 where
64instance Hashable T2 where
62 hashWithSalt salt (T2 t) = hashWithSalt salt t
65 hashWithSalt salt (T2 t) = hashWithSalt salt t
63
66
64−instance FromJSON T2 where
67+instance B.FromJSON T2 where
65− parseJSON = withText "T2" $ pure . T2
68+ parseJSON = B.withText "T2" $ pure . T2
66−instance FromJSONKey T2 where
69+instance B.FromJSONKey T2 where
67− fromJSONKey = fromJSONKeyCoerce
70+ fromJSONKey = B.fromJSONKeyCoerce
68
71
69-------------------------------------------------------------------------------
72-------------------------------------------------------------------------------
70-- TextParser
73-- TextParser
@@ -78,10 +81,10 @@ instance NFData T3 where
78instance Hashable T3 where
81instance Hashable T3 where
79 hashWithSalt salt (T3 t) = hashWithSalt salt t
82 hashWithSalt salt (T3 t) = hashWithSalt salt t
80
83
81−instance FromJSON T3 where
84+instance B.FromJSON T3 where
82− parseJSON = withText "T3" $ pure . T3
85+ parseJSON = B.withText "T3" $ pure . T3
83−instance FromJSONKey T3 where
86+instance B.FromJSONKey T3 where
84− fromJSONKey = FromJSONKeyTextParser (pure . T3)
87+ fromJSONKey = B.FromJSONKeyTextParser (pure . T3)
85
88
86-------------------------------------------------------------------------------
89-------------------------------------------------------------------------------
87-- Values
90-- Values
@@ -94,30 +97,40 @@ value1000 = value 1000
94value10000 = value 10000
97value10000 = value 10000
95
98
96encodedValue10 :: LBS.ByteString
99encodedValue10 :: LBS.ByteString
97−encodedValue10 = encode value10
100+encodedValue10 = B.encode value10
98
101
99encodedValue100 :: LBS.ByteString
102encodedValue100 :: LBS.ByteString
100−encodedValue100 = encode value100
103+encodedValue100 = B.encode value100
101
104
102encodedValue1000 :: LBS.ByteString
105encodedValue1000 :: LBS.ByteString
103−encodedValue1000 = encode value1000
106+encodedValue1000 = B.encode value1000
104
107
105encodedValue10000 :: LBS.ByteString
108encodedValue10000 :: LBS.ByteString
106−encodedValue10000 = encode value10000
109+encodedValue10000 = B.encode value10000
107
110
108-------------------------------------------------------------------------------
111-------------------------------------------------------------------------------
109-- Helpers
112-- Helpers
110-------------------------------------------------------------------------------
113-------------------------------------------------------------------------------
111
114
112−decodeHM
115+decodeHMB
113− :: (FromJSON (HM.HashMap k T.Text), Eq k, Hashable k)
116+ :: (B.FromJSONKey k, Eq k, Hashable k)
114 => Proxy k -> LBS.ByteString -> Maybe (HM.HashMap k T.Text)
117 => Proxy k -> LBS.ByteString -> Maybe (HM.HashMap k T.Text)
115−decodeHM _ = decode
118+decodeHMB _ = B.decode
116
119
117−decodeMap
120+decodeHMA
121+ :: (A.FromJSON (HM.HashMap k T.Text), Eq k, Hashable k)
122+ => Proxy k -> LBS.ByteString -> Maybe (HM.HashMap k T.Text)
123+decodeHMA _ = A.decode
124+
125+decodeMapB
126+ :: (B.FromJSONKey k, Ord k)
127+ => Proxy k -> LBS.ByteString -> Maybe (M.Map k T.Text)
128+decodeMapB _ = B.decode
129+
130+decodeMapA
118− :: (FromJSON (M.Map k T.Text), Ord k)
131+ :: (A.FromJSON (M.Map k T.Text), Ord k)
119 => Proxy k -> LBS.ByteString -> Maybe (M.Map k T.Text)
132 => Proxy k -> LBS.ByteString -> Maybe (M.Map k T.Text)
120−decodeMap _ = decode
133+decodeMapA _ = A.decode
121
134
122proxyText :: Proxy T.Text
135proxyText :: Proxy T.Text
123proxyText = Proxy
136proxyText = Proxy
@@ -143,14 +156,15 @@ benchDecodeHM
143 -> LBS.ByteString
156 -> LBS.ByteString
144 -> Benchmark
157 -> Benchmark
145benchDecodeHM name val = bgroup name
158benchDecodeHM name val = bgroup name
146− [ bench "Text" $ nf (decodeHM proxyText) val
159+ [ bench "Text" $ nf (decodeHMB proxyText) val
147− , bench "Identity" $ nf (decodeHM proxyT1) val
160+ , bench "Identity" $ nf (decodeHMB proxyT1) val
148− , bench "Coerce" $ nf (decodeHM proxyT2) val
161+ , bench "Coerce" $ nf (decodeHMB proxyT2) val
149− , bench "Parser" $ nf (decodeHM proxyT3) val
162+ , bench "Parser" $ nf (decodeHMB proxyT3) val
163+ , bench "aeson-hackage" $ nf (decodeHMA proxyText) val
150− , bench "Tagged Text" $ nf (decodeHM $ proxyTagged proxyText) val
164+ , bench "Tagged Text" $ nf (decodeHMB $ proxyTagged proxyText) val
151− , bench "Tagged Identity" $ nf (decodeHM $ proxyTagged proxyT1) val
165+ , bench "Tagged Identity" $ nf (decodeHMB $ proxyTagged proxyT1) val
152− , bench "Tagged Coerce" $ nf (decodeHM $ proxyTagged proxyT2) val
166+ , bench "Tagged Coerce" $ nf (decodeHMB $ proxyTagged proxyT2) val
153− , bench "Tagged Parser" $ nf (decodeHM $ proxyTagged proxyT3) val
167+ , bench "Tagged Parser" $ nf (decodeHMB $ proxyTagged proxyT3) val
154 ]
168 ]
155
169
156benchDecodeMap
170benchDecodeMap
@@ -158,10 +172,11 @@ benchDecodeMap
158 -> LBS.ByteString
172 -> LBS.ByteString
159 -> Benchmark
173 -> Benchmark
160benchDecodeMap name val = bgroup name
174benchDecodeMap name val = bgroup name
161− [ bench "Text" $ nf (decodeMap proxyText) val
175+ [ bench "Text" $ nf (decodeMapB proxyText) val
162− , bench "Identity" $ nf (decodeMap proxyT1) val
176+ , bench "Identity" $ nf (decodeMapB proxyT1) val
163− , bench "Coerce" $ nf (decodeMap proxyT2) val
177+ , bench "Coerce" $ nf (decodeMapB proxyT2) val
164− , bench "Parser" $ nf (decodeMap proxyT3) val
178+ , bench "Parser" $ nf (decodeMapB proxyT3) val
179+ , bench "aeson-hackage" $ nf (decodeMapA proxyText) val
165 ]
180 ]
166
181
167benchEncodeHM
182benchEncodeHM
@@ -169,7 +184,8 @@ benchEncodeHM
169 -> HM.HashMap T.Text T.Text
184 -> HM.HashMap T.Text T.Text
170 -> Benchmark
185 -> Benchmark
171benchEncodeHM name val = bgroup name
186benchEncodeHM name val = bgroup name
172− [ bench "Text" $ nf encode val
187+ [ bench "Text" $ nf B.encode val
188+ , bench "aeson-0.11" $ nf A.encode val
173 ]
189 ]
174
190
175benchEncodeMap
191benchEncodeMap
@@ -177,7 +193,8 @@ benchEncodeMap
177 -> HM.HashMap T.Text T.Text
193 -> HM.HashMap T.Text T.Text
178 -> Benchmark
194 -> Benchmark
179benchEncodeMap name val = bgroup name
195benchEncodeMap name val = bgroup name
180− [ bench "Text" $ nf encode val'
196+ [ bench "Text" $ nf B.encode val'
197+ , bench "aeson-0.11" $ nf A.encode val'
181 ]
198 ]
182 where
199 where
183 val' :: M.Map T.Text T.Text
200 val' :: M.Map T.Text T.Text
benchmarks/AesonParse.hs+3−2
@@ -1,12 +1,13 @@
1{-# LANGUAGE BangPatterns #-}
1{-# LANGUAGE BangPatterns #-}
2−{-# LANGUAGE NoImplicitPrelude #-}
3{-# LANGUAGE OverloadedStrings #-}
2{-# LANGUAGE OverloadedStrings #-}
3+{-# LANGUAGE PackageImports #-}
4
4
5module Main (main) where
5module Main (main) where
6
6
7+import Prelude ()
7import Prelude.Compat
8import Prelude.Compat
8
9
9−import Data.Aeson
10+import "aeson-benchmarks" Data.Aeson
10import Control.Monad
11import Control.Monad
11import Data.Attoparsec.ByteString (IResult(..), parseWith)
12import Data.Attoparsec.ByteString (IResult(..), parseWith)
12import Data.Time.Clock
13import Data.Time.Clock
benchmarks/AesonTuples.hs+1−2
@@ -1,7 +1,6 @@
1−{-# LANGUAGE NoImplicitPrelude #-}
2−
3module Main (main) where
1module Main (main) where
4
2
3+import Prelude ()
5import Prelude.Compat
4import Prelude.Compat
6
5
7import Control.DeepSeq (deepseq)
6import Control.DeepSeq (deepseq)
benchmarks/Auto/G/BigProduct.hsdeleted−33
@@ -1,33 +0,0 @@
1−{-# LANGUAGE DeriveGeneric #-}
2−
3−module Auto.G.BigProduct where
4−
5−import Control.DeepSeq
6−import Data.Aeson
7−import GHC.Generics (Generic)
8−import Options
9−
10−data BigProduct = BigProduct
11− !Int !Int !Int !Int !Int
12− !Int !Int !Int !Int !Int
13− !Int !Int !Int !Int !Int
14− !Int !Int !Int !Int !Int
15− !Int !Int !Int !Int !Int
16− deriving (Show, Eq, Generic)
17−
18−instance NFData BigProduct where
19− rnf a = a `seq` ()
20−
21−instance ToJSON BigProduct where
22− toJSON = genericToJSON opts
23− toEncoding = genericToEncoding opts
24−
25−instance FromJSON BigProduct where
26− parseJSON = genericParseJSON opts
27−
28−bigProduct :: BigProduct
29−bigProduct = BigProduct 1 2 3 4 5
30− 6 7 8 9 10
31− 11 12 13 14 15
32− 16 17 18 19 20
33− 21 22 23 24 25
benchmarks/Auto/G/BigRecord.hsdeleted−33
@@ -1,33 +0,0 @@
1−{-# LANGUAGE DeriveGeneric #-}
2−
3−module Auto.G.BigRecord where
4−
5−import Control.DeepSeq
6−import Data.Aeson
7−import GHC.Generics (Generic)
8−import Options
9−
10−data BigRecord = BigRecord
11− { field01 :: !Int, field02 :: !Int, field03 :: !Int, field04 :: !Int, field05 :: !Int
12− , field06 :: !Int, field07 :: !Int, field08 :: !Int, field09 :: !Int, field10 :: !Int
13− , field11 :: !Int, field12 :: !Int, field13 :: !Int, field14 :: !Int, field15 :: !Int
14− , field16 :: !Int, field17 :: !Int, field18 :: !Int, field19 :: !Int, field20 :: !Int
15− , field21 :: !Int, field22 :: !Int, field23 :: !Int, field24 :: !Int, field25 :: !Int
16− } deriving (Show, Eq, Generic)
17−
18−instance NFData BigRecord where
19− rnf a = a `seq` ()
20−
21−instance ToJSON BigRecord where
22− toJSON = genericToJSON opts
23− toEncoding = genericToEncoding opts
24−
25−instance FromJSON BigRecord where
26− parseJSON = genericParseJSON opts
27−
28−bigRecord :: BigRecord
29−bigRecord = BigRecord 1 2 3 4 5
30− 6 7 8 9 10
31− 11 12 13 14 15
32− 16 17 18 19 20
33− 21 22 23 24 25
benchmarks/Auto/G/BigSum.hsdeleted−28
@@ -1,28 +0,0 @@
1−{-# LANGUAGE DeriveGeneric #-}
2−
3−module Auto.G.BigSum where
4−
5−import Control.DeepSeq
6−import Data.Aeson
7−import GHC.Generics (Generic)
8−import Options
9−
10−data BigSum = F01 | F02 | F03 | F04 | F05
11− | F06 | F07 | F08 | F09 | F10
12− | F11 | F12 | F13 | F14 | F15
13− | F16 | F17 | F18 | F19 | F20
14− | F21 | F22 | F23 | F24 | F25
15− deriving (Show, Eq, Generic)
16−
17−instance NFData BigSum where
18− rnf a = a `seq` ()
19−
20−instance ToJSON BigSum where
21− toJSON = genericToJSON opts
22− toEncoding = genericToEncoding opts
23−
24−instance FromJSON BigSum where
25− parseJSON = genericParseJSON opts
26−
27−bigSum :: BigSum
28−bigSum = F25
benchmarks/Auto/G/D.hsdeleted−44
@@ -1,44 +0,0 @@
1−{-# LANGUAGE DeriveGeneric #-}
2−
3−module Auto.G.D where
4−
5−import Control.DeepSeq
6−import Data.Aeson
7−import GHC.Generics (Generic)
8−import Options
9−
10−data D a = Nullary
11− | Unary Int
12− | Product String Char a
13− | Record { testOne :: Double
14− , testTwo :: Bool
15− , testThree :: D a
16− }
17− deriving (Show, Eq, Generic)
18−
19−instance NFData a => NFData (D a) where
20− rnf Nullary = ()
21− rnf (Unary n) = rnf n
22− rnf (Product s c x) = s `deepseq` c `deepseq` rnf x
23− rnf (Record a b y) = a `deepseq` b `deepseq` rnf y
24−
25−instance ToJSON a => ToJSON (D a) where
26− toJSON = genericToJSON opts
27− toEncoding = genericToEncoding opts
28−
29−instance FromJSON a => FromJSON (D a) where
30− parseJSON = genericParseJSON opts
31−
32−type T = D (D (D ()))
33−
34−d :: T
35−d = Record
36− { testOne = 1234.56789
37− , testTwo = True
38− , testThree = Product "Hello World!" 'a'
39− Record
40− { testOne = 9876.54321
41− , testTwo = False
42− , testThree = Product "Yeehaa!!!" '\n' Nullary
43− }
44− }
benchmarks/Auto/T/BigProduct.hsdeleted−27
@@ -1,27 +0,0 @@
1−{-# LANGUAGE TemplateHaskell #-}
2−
3−module Auto.T.BigProduct where
4−
5−import Control.DeepSeq
6−import Data.Aeson.TH
7−import Options
8−
9−data BigProduct = BigProduct
10− !Int !Int !Int !Int !Int
11− !Int !Int !Int !Int !Int
12− !Int !Int !Int !Int !Int
13− !Int !Int !Int !Int !Int
14− !Int !Int !Int !Int !Int
15− deriving (Show, Eq)
16−
17−instance NFData BigProduct where
18− rnf a = a `seq` ()
19−
20−deriveJSON opts ''BigProduct
21−
22−bigProduct :: BigProduct
23−bigProduct = BigProduct 1 2 3 4 5
24− 6 7 8 9 10
25− 11 12 13 14 15
26− 16 17 18 19 20
27− 21 22 23 24 25
benchmarks/Auto/T/BigRecord.hsdeleted−27
@@ -1,27 +0,0 @@
1−{-# LANGUAGE TemplateHaskell #-}
2−
3−module Auto.T.BigRecord where
4−
5−import Control.DeepSeq
6−import Data.Aeson.TH
7−import Options
8−
9−data BigRecord = BigRecord
10− { field01 :: !Int, field02 :: !Int, field03 :: !Int, field04 :: !Int, field05 :: !Int
11− , field06 :: !Int, field07 :: !Int, field08 :: !Int, field09 :: !Int, field10 :: !Int
12− , field11 :: !Int, field12 :: !Int, field13 :: !Int, field14 :: !Int, field15 :: !Int
13− , field16 :: !Int, field17 :: !Int, field18 :: !Int, field19 :: !Int, field20 :: !Int
14− , field21 :: !Int, field22 :: !Int, field23 :: !Int, field24 :: !Int, field25 :: !Int
15− } deriving (Show, Eq)
16−
17−instance NFData BigRecord where
18− rnf a = a `seq` ()
19−
20−deriveJSON opts ''BigRecord
21−
22−bigRecord :: BigRecord
23−bigRecord = BigRecord 1 2 3 4 5
24− 6 7 8 9 10
25− 11 12 13 14 15
26− 16 17 18 19 20
27− 21 22 23 24 25
benchmarks/Auto/T/BigSum.hsdeleted−22
@@ -1,22 +0,0 @@
1−{-# LANGUAGE TemplateHaskell #-}
2−
3−module Auto.T.BigSum where
4−
5−import Control.DeepSeq
6−import Data.Aeson.TH
7−import Options
8−
9−data BigSum = F01 | F02 | F03 | F04 | F05
10− | F06 | F07 | F08 | F09 | F10
11− | F11 | F12 | F13 | F14 | F15
12− | F16 | F17 | F18 | F19 | F20
13− | F21 | F22 | F23 | F24 | F25
14− deriving (Show, Eq)
15−
16−instance NFData BigSum where
17− rnf a = a `seq` ()
18−
19−deriveJSON opts ''BigSum
20−
21−bigSum :: BigSum
22−bigSum = F25
benchmarks/Auto/T/D.hsdeleted−38
@@ -1,38 +0,0 @@
1−{-# LANGUAGE TemplateHaskell #-}
2−
3−module Auto.T.D where
4−
5−import Control.DeepSeq
6−import Data.Aeson.TH
7−import Options
8−
9−data D a = Nullary
10− | Unary Int
11− | Product String Char a
12− | Record { testOne :: Double
13− , testTwo :: Bool
14− , testThree :: D a
15− }
16− deriving (Show, Eq)
17−
18−instance NFData a => NFData (D a) where
19− rnf Nullary = ()
20− rnf (Unary n) = rnf n
21− rnf (Product s c x) = s `deepseq` c `deepseq` rnf x
22− rnf (Record a b y) = a `deepseq` b `deepseq` rnf y
23−
24−deriveJSON opts ''D
25−
26−type T = D (D (D ()))
27−
28−d :: T
29−d = Record
30− { testOne = 1234.56789
31− , testTwo = True
32− , testThree = Product "Hello World!" 'a'
33− Record
34− { testOne = 9876.54321
35− , testTwo = False
36− , testThree = Product "Yeehaa!!!" '\n' Nullary
37− }
38− }
benchmarks/AutoCompare.hsdeleted−73
@@ -1,73 +0,0 @@
1−{-# LANGUAGE RankNTypes #-}
2−{-# LANGUAGE ScopedTypeVariables #-}
3−
4−module Main (main) where
5−
6−import Control.DeepSeq
7−import Control.Monad
8−import Criterion.Main
9−import Data.Aeson
10−
11−import qualified Auto.T.D as T
12−import qualified Auto.T.BigRecord as T
13−import qualified Auto.T.BigProduct as T
14−import qualified Auto.T.BigSum as T
15−import qualified Auto.G.D as G
16−import qualified Auto.G.BigRecord as G
17−import qualified Auto.G.BigProduct as G
18−import qualified Auto.G.BigSum as G
19−
20−--------------------------------------------------------------------------------
21−
22−runBench :: IO ()
23−runBench = defaultMain
24− [ compareBench "D" T.d G.d
25− , compareBench "BigRecord" T.bigRecord G.bigRecord
26− , compareBench "BigProduct" T.bigProduct G.bigProduct
27− , compareBench "BigSum" T.bigSum G.bigSum
28− ]
29−
30−group :: String -> Benchmarkable -> Benchmarkable -> Benchmark
31−group n th gen = bgroup n [ bench "th" th
32− , bench "generic" gen
33− ]
34−
35−compareBench
36− :: forall a b
37− . (ToJSON a, FromJSON a, NFData a, ToJSON b, FromJSON b, NFData b)
38− => String -> a -> b -> Benchmark
39−compareBench name a b = v `deepseq` bgroup name
40− [ group "toJSON" (nf toJSON a)
41− (nf toJSON b)
42− , group "encode" (nf encode a)
43− (nf encode b)
44− , group "fromJSON" (nf (fromJSON :: Value -> Result a) v)
45− (nf (fromJSON :: Value -> Result b) v)
46− ] where
47− v = toJSON a -- == toJSON b
48−
49−sanityCheck :: IO ()
50−sanityCheck = do
51− check T.d
52− check G.d
53− check T.bigRecord
54− check G.bigRecord
55− check T.bigProduct
56− check G.bigProduct
57− check T.bigSum
58− check G.bigSum
59−
60−check :: (Show a, Eq a, FromJSON a, ToJSON a)
61− => a -> IO ()
62−check x = do
63− unless (Success x == (fromJSON . toJSON) x) $ fail $ "toJSON: " ++ show x
64− unless (Success x == (decode_ . encode) x) $ fail $ "encode: " ++ show x
65− where
66− decode_ s = case decode s of
67− Just v -> fromJSON v
68− Nothing -> fail ""
69−
70−main :: IO ()
71−main = do
72− sanityCheck
73− runBench
benchmarks/Compare.hs+5−10
@@ -1,25 +1,22 @@
1−{-# LANGUAGE CPP #-}
1+{-# LANGUAGE PackageImports #-}
2−{-# LANGUAGE NoImplicitPrelude #-}
3{-# LANGUAGE ScopedTypeVariables #-}
2{-# LANGUAGE ScopedTypeVariables #-}
4
3
5module Main (main) where
4module Main (main) where
6
5
6+import Prelude ()
7import Prelude.Compat
7import Prelude.Compat
8
8
9import Compare.BufferBuilder ()
9import Compare.BufferBuilder ()
10+import Compare.JsonBuilder ()
10import Criterion.Main
11import Criterion.Main
11import Data.BufferBuilder.Json
12import Data.BufferBuilder.Json
13+import Data.Json.Builder
12import Twitter
14import Twitter
13import Twitter.Manual ()
15import Twitter.Manual ()
14import Typed.Common
16import Typed.Common
15−import qualified Data.Aeson as Aeson
17+import qualified "aeson-benchmarks" Data.Aeson as Aeson
16import qualified Compare.JsonBench as JsonBench
18import qualified Compare.JsonBench as JsonBench
17
19
18−#ifdef MIN_VERSION_json_builder
19−import Data.Json.Builder
20−import Compare.JsonBuilder ()
21−#endif
22−
23main :: IO ()
20main :: IO ()
24main =
21main =
25 defaultMain [
22 defaultMain [
@@ -27,9 +24,7 @@ main =
27 bgroup "twitter" [
24 bgroup "twitter" [
28 bench "aeson" $ nf Aeson.encode twtr
25 bench "aeson" $ nf Aeson.encode twtr
29 , bench "buffer-builder" $ nf encodeJson twtr
26 , bench "buffer-builder" $ nf encodeJson twtr
30−#ifdef MIN_VERSION_json_builder
31 , bench "json-builder" $ nf toJsonLBS twtr
27 , bench "json-builder" $ nf toJsonLBS twtr
32−#endif
33 ]
28 ]
34 , JsonBench.benchmarks
29 , JsonBench.benchmarks
35 ]
30 ]
benchmarks/Compare/BufferBuilder.hs+3−2
@@ -1,4 +1,3 @@
1−{-# LANGUAGE NoImplicitPrelude #-}
2{-# LANGUAGE OverloadedStrings #-}
1{-# LANGUAGE OverloadedStrings #-}
3{-# LANGUAGE RecordWildCards #-}
2{-# LANGUAGE RecordWildCards #-}
4
3
@@ -7,11 +6,13 @@
7
6
8module Compare.BufferBuilder () where
7module Compare.BufferBuilder () where
9
8
10−import Prelude.Compat hiding ((<>))
9+import Prelude ()
10+import Prelude.Compat
11
11
12import Data.BufferBuilder.Json
12import Data.BufferBuilder.Json
13import Data.Int (Int64)
13import Data.Int (Int64)
14import Data.Monoid ((<>))
14import Data.Monoid ((<>))
15+import Prelude hiding (id)
15import Twitter
16import Twitter
16import qualified Data.BufferBuilder.Utf8 as UB
17import qualified Data.BufferBuilder.Utf8 as UB
17
18
benchmarks/Compare/JsonBench.hs+9−18
@@ -1,18 +1,17 @@
1−{-# LANGUAGE CPP #-}
1+-- Adapted from a buffer-builder benchmark:
2+--
3+-- https://github.com/chadaustin/buffer-builder/blob/master/test.json
4+
2{-# LANGUAGE BangPatterns #-}
5{-# LANGUAGE BangPatterns #-}
3{-# LANGUAGE MagicHash #-}
6{-# LANGUAGE MagicHash #-}
4−{-# LANGUAGE NoImplicitPrelude #-}
5{-# LANGUAGE OverloadedStrings #-}
7{-# LANGUAGE OverloadedStrings #-}
6{-# LANGUAGE RecordWildCards #-}
8{-# LANGUAGE RecordWildCards #-}
7{-# LANGUAGE ScopedTypeVariables #-}
9{-# LANGUAGE ScopedTypeVariables #-}
8
10
9−-- Adapted from a buffer-builder benchmark:
10−--
11−-- https://github.com/chadaustin/buffer-builder/blob/master/test.json
12−
13module Compare.JsonBench (benchmarks) where
11module Compare.JsonBench (benchmarks) where
14
12
15−import Prelude.Compat hiding ((<>))
13+import Prelude ()
14+import Prelude.Compat
16
15
17import Control.DeepSeq (NFData(..))
16import Control.DeepSeq (NFData(..))
18import Criterion
17import Criterion
@@ -20,13 +19,9 @@ import Data.Aeson ((.:))
20import Data.Monoid ((<>))
19import Data.Monoid ((<>))
21import Data.Text (Text)
20import Data.Text (Text)
22import Typed.Common (load)
21import Typed.Common (load)
23−import qualified Control.Monad.Fail as Fail
24import qualified Data.Aeson as Aeson
22import qualified Data.Aeson as Aeson
25import qualified Data.BufferBuilder.Json as Json
23import qualified Data.BufferBuilder.Json as Json
26−
27−#ifdef MIN_VERSION_json_builder
28import qualified Data.Json.Builder as JB
24import qualified Data.Json.Builder as JB
29−#endif
30
25
31data EyeColor = Green | Blue | Brown
26data EyeColor = Green | Blue | Brown
32 deriving (Eq, Show)
27 deriving (Eq, Show)
@@ -69,10 +64,10 @@ instance NFData Gender where rnf !_ = ()
69instance NFData Fruit where rnf !_ = ()
64instance NFData Fruit where rnf !_ = ()
70
65
71instance NFData Friend where
66instance NFData Friend where
72− rnf Friend {..} = rnf fId `seq` rnf fName
67+ rnf Friend {..} = rnf fId `seq` rnf fName `seq` ()
73
68
74instance NFData User where
69instance NFData User where
75− rnf User {..} = rnf uId `seq` rnf uIndex `seq` rnf uGuid `seq` rnf uIsActive `seq` rnf uBalance `seq` rnf uPicture `seq` rnf uAge `seq` rnf uEyeColor `seq` rnf uName `seq` rnf uGender `seq` rnf uCompany `seq` rnf uEmail `seq` rnf uPhone `seq` rnf uAddress `seq` rnf uAbout `seq` rnf uRegistered `seq` rnf uLatitude `seq` rnf uLongitude `seq` rnf uTags `seq` rnf uFriends `seq` rnf uGreeting `seq` rnf uFavouriteFruit
70+ rnf User {..} = rnf uId `seq` rnf uIndex `seq` rnf uGuid `seq` rnf uIsActive `seq` rnf uBalance `seq` rnf uPicture `seq` rnf uAge `seq` rnf uEyeColor `seq` rnf uName `seq` rnf uGender `seq` rnf uCompany `seq` rnf uEmail `seq` rnf uPhone `seq` rnf uAddress `seq` rnf uAbout `seq` rnf uRegistered `seq` rnf uLatitude `seq` rnf uLongitude `seq` rnf uTags `seq` rnf uFriends `seq` rnf uGreeting `seq` rnf uFavouriteFruit `seq` ()
76
71
77eyeColorTable :: [(Text, EyeColor)]
72eyeColorTable :: [(Text, EyeColor)]
78eyeColorTable = [("brown", Brown), ("green", Green), ("blue", Blue)]
73eyeColorTable = [("brown", Brown), ("green", Green), ("blue", Blue)]
@@ -83,7 +78,7 @@ genderTable = [("male", Male), ("female", Female)]
83fruitTable :: [(Text, Fruit)]
78fruitTable :: [(Text, Fruit)]
84fruitTable = [("apple", Apple), ("strawberry", Strawberry), ("banana", Banana)]
79fruitTable = [("apple", Apple), ("strawberry", Strawberry), ("banana", Banana)]
85
80
86−enumFromJson :: Fail.MonadFail m => String -> [(Text, enum)] -> (json -> m Text) -> json -> m enum
81+enumFromJson :: Monad m => String -> [(Text, enum)] -> (json -> m Text) -> json -> m enum
87enumFromJson enumName table extract v = do
82enumFromJson enumName table extract v = do
88 s <- extract v
83 s <- extract v
89 case lookup s table of
84 case lookup s table of
@@ -273,7 +268,6 @@ instance Json.ToJson User where
273 <> "greeting"# Json..=# uGreeting
268 <> "greeting"# Json..=# uGreeting
274 <> "favoriteFruit"# Json..=# uFavouriteFruit
269 <> "favoriteFruit"# Json..=# uFavouriteFruit
275
270
276−#ifdef MIN_VERSION_json_builder
277---- json-builder instances ----
271---- json-builder instances ----
278
272
279instance JB.Value EyeColor where
273instance JB.Value EyeColor where
@@ -325,7 +319,6 @@ instance JB.Value User where
325 <> t "friends" `JB.row` uFriends
319 <> t "friends" `JB.row` uFriends
326 <> t "greeting" `JB.row` uGreeting
320 <> t "greeting" `JB.row` uGreeting
327 <> t "favoriteFruit" `JB.row` uFavouriteFruit
321 <> t "favoriteFruit" `JB.row` uFavouriteFruit
328−#endif
329
322
330benchmarks :: Benchmark
323benchmarks :: Benchmark
331benchmarks = env (load "json-data/buffer-builder.json") $
324benchmarks = env (load "json-data/buffer-builder.json") $
@@ -333,7 +326,5 @@ benchmarks = env (load "json-data/buffer-builder.json") $
333 bgroup "json-bench" [
326 bgroup "json-bench" [
334 bench "aeson" $ nf Aeson.encode parsedUserList
327 bench "aeson" $ nf Aeson.encode parsedUserList
335 , bench "buffer-builder" $ nf Json.encodeJson parsedUserList
328 , bench "buffer-builder" $ nf Json.encodeJson parsedUserList
336−#ifdef MIN_VERSION_json_builder
337 , bench "json-builder" $ nf JB.toJsonLBS parsedUserList
329 , bench "json-builder" $ nf JB.toJsonLBS parsedUserList
338−#endif
339 ]
330 ]
benchmarks/Compare/JsonBuilder.hs+3−2
@@ -1,4 +1,3 @@
1−{-# LANGUAGE NoImplicitPrelude #-}
2{-# LANGUAGE RecordWildCards #-}
1{-# LANGUAGE RecordWildCards #-}
3
2
4{-# OPTIONS_GHC -fno-warn-name-shadowing #-}
3{-# OPTIONS_GHC -fno-warn-name-shadowing #-}
@@ -6,10 +5,12 @@
6
5
7module Compare.JsonBuilder () where
6module Compare.JsonBuilder () where
8
7
9−import Prelude.Compat hiding ((<>))
8+import Prelude ()
9+import Prelude.Compat
10
10
11import Data.Json.Builder
11import Data.Json.Builder
12import Data.Monoid ((<>))
12import Data.Monoid ((<>))
13+import Prelude hiding (id)
13import Twitter
14import Twitter
14
15
15instance JsObject Metadata where
16instance JsObject Metadata where
benchmarks/CompareWithJSON.hs+33−23
@@ -1,9 +1,9 @@
1−{-# LANGUAGE NoImplicitPrelude #-}
1+{-# LANGUAGE PackageImports #-}
2−
3{-# OPTIONS_GHC -fno-warn-orphans #-}
2{-# OPTIONS_GHC -fno-warn-orphans #-}
4
3
5module Main (main) where
4module Main (main) where
6
5
6+import Prelude ()
7import Prelude.Compat
7import Prelude.Compat
8
8
9import Blaze.ByteString.Builder (toLazyByteString)
9import Blaze.ByteString.Builder (toLazyByteString)
@@ -11,9 +11,10 @@ import Blaze.ByteString.Builder.Char.Utf8 (fromString)
11import Control.DeepSeq (NFData(rnf))
11import Control.DeepSeq (NFData(rnf))
12import Criterion.Main
12import Criterion.Main
13import Data.Maybe (fromMaybe)
13import Data.Maybe (fromMaybe)
14−import qualified Data.Aeson as A
14+import qualified "aeson-benchmarks" Data.Aeson as A
15−import qualified Data.Aeson.Text as A
15+import qualified "aeson-benchmarks" Data.Aeson.Text as A
16−import qualified Data.Aeson.Parser.Internal as I
16+import qualified "aeson-benchmarks" Data.Aeson.Parser.Internal as I
17+import qualified "aeson" Data.Aeson as B
17import qualified Data.ByteString as BS
18import qualified Data.ByteString as BS
18import qualified Data.ByteString.Lazy as BL
19import qualified Data.ByteString.Lazy as BL
19import qualified Data.Text.Lazy as TL
20import qualified Data.Text.Lazy as TL
@@ -27,7 +28,7 @@ instance (NFData v) => NFData (J.JSObject v) where
27instance NFData J.JSValue where
28instance NFData J.JSValue where
28 rnf J.JSNull = ()
29 rnf J.JSNull = ()
29 rnf (J.JSBool b) = rnf b
30 rnf (J.JSBool b) = rnf b
30− rnf (J.JSRational a b) = rnf a `seq` rnf b
31+ rnf (J.JSRational a b) = rnf a `seq` rnf b `seq` ()
31 rnf (J.JSString s) = rnf (J.fromJSString s)
32 rnf (J.JSString s) = rnf (J.fromJSString s)
32 rnf (J.JSArray lst) = rnf lst
33 rnf (J.JSArray lst) = rnf lst
33 rnf (J.JSObject o) = rnf o
34 rnf (J.JSObject o) = rnf o
@@ -38,14 +39,20 @@ decodeJ s =
38 J.Ok v -> v
39 J.Ok v -> v
39 J.Error _ -> error "fail to parse via JSON"
40 J.Error _ -> error "fail to parse via JSON"
40
41
42+decodeA :: BL.ByteString -> A.Value
43+decodeA s = fromMaybe (error "fail to parse via Aeson") $ A.decode s
44+
41−decode :: BL.ByteString -> A.Value
45+decodeA' :: BL.ByteString -> A.Value
46+decodeA' s = fromMaybe (error "fail to parse via Aeson") $ A.decode' s
47+
48+decodeAS :: BS.ByteString -> A.Value
42−decode s = fromMaybe (error "fail to parse via Aeson") $ A.decode s
49+decodeAS s = fromMaybe (error "fail to parse via Aeson") $ A.decodeStrict' s
43
50
44−decode' :: BL.ByteString -> A.Value
51+decodeB :: BL.ByteString -> B.Value
45−decode' s = fromMaybe (error "fail to parse via Aeson") $ A.decode' s
52+decodeB s = fromMaybe (error "fail to parse via Aeson") $ B.decode s
46
53
47−decodeS :: BS.ByteString -> A.Value
54+decodeBS :: BS.ByteString -> B.Value
48−decodeS s = fromMaybe (error "fail to parse via Aeson") $ A.decodeStrict' s
55+decodeBS s = fromMaybe (error "fail to parse via Aeson") $ B.decodeStrict' s
49
56
50decodeIP :: BL.ByteString -> A.Value
57decodeIP :: BL.ByteString -> A.Value
51decodeIP s = fromMaybe (error "fail to parse via Parser.decodeWith") $
58decodeIP s = fromMaybe (error "fail to parse via Parser.decodeWith") $
@@ -73,29 +80,32 @@ main = do
73 defaultMain [
80 defaultMain [
74 bgroup "decode" [
81 bgroup "decode" [
75 bgroup "en" [
82 bgroup "en" [
76− bench "aeson/lazy" $ nf decode enA
83+ bench "aeson/lazy" $ nf decodeA enA
77− , bench "aeson/strict" $ nf decode' enA
84+ , bench "aeson/strict" $ nf decodeA' enA
78− , bench "aeson/stricter" $ nf decodeS enS
85+ , bench "aeson/stricter" $ nf decodeAS enS
86+ , bench "aeson/hackage" $ nf decodeB enA
87+ , bench "aeson/hackage'" $ nf decodeBS enS
79 , bench "aeson/parser" $ nf decodeIP enA
88 , bench "aeson/parser" $ nf decodeIP enA
80 , bench "json" $ nf decodeJ enJ
89 , bench "json" $ nf decodeJ enJ
81 ]
90 ]
82 , bgroup "jp" [
91 , bgroup "jp" [
83− bench "aeson" $ nf decode jpA
92+ bench "aeson" $ nf decodeA jpA
84− , bench "aeson/stricter" $ nf decodeS jpS
93+ , bench "aeson/stricter" $ nf decodeAS jpS
94+ , bench "aeson/hackage" $ nf decodeB jpA
85 , bench "json" $ nf decodeJ jpJ
95 , bench "json" $ nf decodeJ jpJ
86 ]
96 ]
87 ]
97 ]
88 , bgroup "encode" [
98 , bgroup "encode" [
89 bgroup "en" [
99 bgroup "en" [
90− bench "aeson-to-bytestring" $ nf A.encode (decode enA)
100+ bench "aeson-to-bytestring" $ nf A.encode (decodeA enA)
91− , bench "aeson-via-text-to-bytestring" $ nf encodeViaText (decode enA)
101+ , bench "aeson-via-text-to-bytestring" $ nf encodeViaText (decodeA enA)
92− , bench "aeson-to-text" $ nf encodeToText (decode enA)
102+ , bench "aeson-to-text" $ nf encodeToText (decodeA enA)
93 , bench "json" $ nf encodeJ (decodeJ enJ)
103 , bench "json" $ nf encodeJ (decodeJ enJ)
94 ]
104 ]
95 , bgroup "jp" [
105 , bgroup "jp" [
96− bench "aeson-to-bytestring" $ nf A.encode (decode jpA)
106+ bench "aeson-to-bytestring" $ nf A.encode (decodeA jpA)
97− , bench "aeson-via-text-to-bytestring" $ nf encodeViaText (decode jpA)
107+ , bench "aeson-via-text-to-bytestring" $ nf encodeViaText (decodeA jpA)
98− , bench "aeson-to-text" $ nf encodeToText (decode jpA)
108+ , bench "aeson-to-text" $ nf encodeToText (decodeA jpA)
99 , bench "json" $ nf encodeJ (decodeJ jpJ)
109 , bench "json" $ nf encodeJ (decodeJ jpJ)
100 ]
110 ]
101 ]
111 ]
benchmarks/Dates.hs+2−2
@@ -1,7 +1,7 @@
1−{-# LANGUAGE NoImplicitPrelude #-}
1+{-# OPTIONS_GHC -fsimpl-tick-factor=0 #-}
2−
3module Main (main) where
2module Main (main) where
4
3
4+import Prelude ()
5import Prelude.Compat
5import Prelude.Compat
6
6
7import Criterion.Main
7import Criterion.Main
benchmarks/Escape.hsdeleted−32
@@ -1,32 +0,0 @@
1−{-# LANGUAGE NoImplicitPrelude #-}
2−{-# LANGUAGE ScopedTypeVariables #-}
3−
4−{-# OPTIONS_GHC -fno-warn-name-shadowing #-}
5−
6−module Main (main) where
7−
8−import Prelude.Compat
9−
10−import Criterion.Main
11−import qualified Data.Aeson.Parser.UnescapeFFI as FFI
12−import qualified Data.Aeson.Parser.UnescapePure as Pure
13−
14−import qualified Data.ByteString.Char8 as BS
15−import System.Environment (getArgs, withArgs)
16−
17−main :: IO ()
18−main = do
19− args_ <- getArgs
20− let (args, p, n) =
21− case args_ of
22− "--pattern" : p : args_ -> k p args_
23− _ -> k "\\\"" args_
24− k p args_ =
25− case args_ of
26− "--repeat" : n : args_ -> (args_, p, read n)
27− args_ -> (args_, p, 10000)
28− input = BS.concat $ replicate n $ BS.pack p
29− withArgs args $ defaultMain
30− [ bench "ffi" $ whnf FFI.unescapeText input
31− , bench "pure" $ whnf Pure.unescapeText input
32− ]
benchmarks/Issue673.hsdeleted−165
@@ -1,165 +0,0 @@
1−{-# LANGUAGE NoImplicitPrelude #-}
2−{-# LANGUAGE OverloadedStrings #-}
3−
4−module Main (
5− main,
6− input17,
7− input32,
8− input64,
9− input128,
10− input256,
11− input2048,
12− input4096,
13− input8192,
14− input16384,
15− ) where
16−
17−import Criterion.Main
18−import Prelude.Compat
19−import Data.Int (Int64)
20−import Data.Scientific (Scientific)
21−import Data.Semigroup ((<>))
22−import Data.Aeson.Parser (scientific)
23−
24−import qualified Data.Attoparsec.ByteString.Lazy as AttoL
25−import qualified Data.Attoparsec.ByteString.Char8 as Atto8
26−import qualified Data.Aeson as A
27−import qualified Data.ByteString as BS
28−import qualified Data.ByteString.Lazy as LBS
29−import qualified Data.ByteString.Lazy.Char8 as LBS8
30−
31−decodeInt :: LBS.ByteString -> Maybe Int
32−decodeInt = A.decode
33−
34−decodeString :: LBS.ByteString -> Maybe String
35−decodeString = A.decode
36−
37−decodeScientific :: LBS.ByteString -> Maybe Scientific
38−decodeScientific = A.decode
39−
40−decodeViaRead :: LBS.ByteString -> Integer
41−decodeViaRead = read . LBS8.unpack
42−
43−decodeAtto :: LBS.ByteString -> Maybe Scientific
44−decodeAtto
45− = parseOnly (scientific <* AttoL.endOfInput)
46− where
47− parseOnly p lbs = case AttoL.parse p lbs of
48− AttoL.Done _ r -> Just r
49− AttoL.Fail {} -> Nothing
50−
51−decodeAtto8 :: LBS.ByteString -> Maybe Scientific
52−decodeAtto8
53− = parseOnly (Atto8.scientific <* AttoL.endOfInput)
54− where
55− parseOnly p lbs = case AttoL.parse p lbs of
56− AttoL.Done _ r -> Just r
57− AttoL.Fail {} -> Nothing
58−
59−generate :: Int64 -> LBS.ByteString
60−generate n = LBS8.replicate n '1'
61−
62−input17 :: LBS.ByteString
63−input17 = generate 17
64−
65−input32 :: LBS.ByteString
66−input32 = generate 32
67−
68−input64 :: LBS.ByteString
69−input64 = generate 64
70−
71−input128 :: LBS.ByteString
72−input128 = generate 128
73−
74−input256 :: LBS.ByteString
75−input256 = generate 256
76−
77−input2048 :: LBS.ByteString
78−input2048 = generate 2048
79−
80−input4096 :: LBS.ByteString
81−input4096 = generate 4096
82−
83−input8192 :: LBS.ByteString
84−input8192 = generate 8192
85−
86−input16384 :: LBS.ByteString
87−input16384 = generate 16384
88−
89−
90−main :: IO ()
91−main = defaultMain
92− -- works on 64bit
93− [ benchPair "17" input17
94− -- , benchPair "32" input32
95− -- , benchPair "64" input64
96− -- , benchPair "128" input128
97− -- , benchPair "256" input256
98− , benchPair "2048" input2048
99− , benchPair "4096" input4096
100− , benchPair "8192" input8192
101− , benchPair "16384" input16384
102− ]
103− where
104− benchPair name input = bgroup name
105− [ bench "Int" $ whnf decodeInt input
106− , bench "Simple" $ whnf bsToIntegerSimple (LBS.toStrict input)
107− , bench "Optim" $ whnf bsToInteger (LBS.toStrict input)
108− , bench "Read" $ whnf decodeViaRead input
109− , bench "Scientific" $ whnf decodeScientific input
110− , bench "parserA" $ whnf decodeAtto input
111− , bench "parserS" $ whnf decodeAtto8 input
112− , bench "String" $ whnf decodeString $ "\"" <> input <> "\""
113− ]
114−
115−-------------------------------------------------------------------------------
116−-- better fromInteger
117−-------------------------------------------------------------------------------
118−
119−bsToInteger :: BS.ByteString -> Integer
120−bsToInteger bs
121− | l > 40 = valInteger 10 l [ fromIntegral (w - 48) | w <- BS.unpack bs ]
122− | otherwise = bsToIntegerSimple bs
123− where
124− l = BS.length bs
125−
126−bsToIntegerSimple :: BS.ByteString -> Integer
127−bsToIntegerSimple = BS.foldl' step 0 where
128− step a b = a * 10 + fromIntegral (b - 48) -- 48 = '0'
129−
130−-- A sub-quadratic algorithm for Integer. Pairs of adjacent radix b
131−-- digits are combined into a single radix b^2 digit. This process is
132−-- repeated until we are left with a single digit. This algorithm
133−-- performs well only on large inputs, so we use the simple algorithm
134−-- for smaller inputs.
135−valInteger :: Integer -> Int -> [Integer] -> Integer
136−valInteger = go
137− where
138− go :: Integer -> Int -> [Integer] -> Integer
139− go _ _ [] = 0
140− go _ _ [d] = d
141− go b l ds
142− | l > 40 = b' `seq` go b' l' (combine b ds')
143− | otherwise = valSimple b ds
144− where
145− -- ensure that we have an even number of digits
146− -- before we call combine:
147− ds' = if even l then ds else 0 : ds
148− b' = b * b
149− l' = (l + 1) `quot` 2
150−
151− combine b (d1 : d2 : ds) = d `seq` (d : combine b ds)
152− where
153− d = d1 * b + d2
154− combine _ [] = []
155− combine _ [_] = errorWithoutStackTrace "this should not happen"
156−
157−-- The following algorithm is only linear for types whose Num operations
158−-- are in constant time.
159−valSimple :: Integer -> [Integer] -> Integer
160−valSimple base = go 0
161− where
162− go r [] = r
163− go r (d : ds) = r' `seq` go r' ds
164− where
165− r' = r * base + fromIntegral d
benchmarks/JsonParse.hs+2−3
@@ -1,11 +1,10 @@
1{-# LANGUAGE BangPatterns #-}
1{-# LANGUAGE BangPatterns #-}
2−{-# LANGUAGE NoImplicitPrelude #-}
3{-# LANGUAGE ScopedTypeVariables #-}
2{-# LANGUAGE ScopedTypeVariables #-}
4−
5{-# OPTIONS_GHC -fno-warn-orphans #-}
3{-# OPTIONS_GHC -fno-warn-orphans #-}
6
4
7module Main (main) where
5module Main (main) where
8
6
7+import Prelude ()
9import Prelude.Compat
8import Prelude.Compat
10
9
11import Control.DeepSeq
10import Control.DeepSeq
@@ -17,7 +16,7 @@ import Text.JSON
17instance NFData JSValue where
16instance NFData JSValue where
18 rnf JSNull = ()
17 rnf JSNull = ()
19 rnf (JSBool b) = rnf b
18 rnf (JSBool b) = rnf b
20− rnf (JSRational b r) = rnf b `seq` rnf r
19+ rnf (JSRational b r) = rnf b `seq` rnf r `seq` ()
21 rnf (JSString s) = rnf (fromJSString s)
20 rnf (JSString s) = rnf (fromJSString s)
22 rnf (JSArray vs) = rnf vs
21 rnf (JSArray vs) = rnf vs
23 rnf (JSObject kvs) = rnf (fromJSObject kvs)
22 rnf (JSObject kvs) = rnf (fromJSObject kvs)
benchmarks/Micro.hs+6−8
@@ -1,13 +1,11 @@
1−{-# LANGUAGE NoImplicitPrelude #-}
2−
3module Main (main) where
1module Main (main) where
4
2
3+import Prelude ()
5import Prelude.Compat
4import Prelude.Compat
6
5
7import Criterion.Main
6import Criterion.Main
8−
9−-- Encoding is a newtype wrapper around Builder
10−import Data.Aeson.Encoding (text, string, encodingToLazyByteString)
7+import qualified Data.Aeson.Encoding.Builder as AB
8+import qualified Data.ByteString.Builder as B
11import qualified Data.Text as T
9import qualified Data.Text as T
12
10
13main :: IO ()
11main :: IO ()
@@ -15,8 +13,8 @@ main = do
15 let txt = "append (append b (primBounded w1 x1)) (primBounded w2 x2)"
13 let txt = "append (append b (primBounded w1 x1)) (primBounded w2 x2)"
16 defaultMain [
14 defaultMain [
17 bgroup "string" [
15 bgroup "string" [
18− bench "text" $ nf (encodingToLazyByteString . text) (T.pack txt)
16+ bench "text" $ nf (B.toLazyByteString . AB.text) (T.pack txt)
19− , bench "string direct" $ nf (encodingToLazyByteString . string) txt
17+ , bench "string direct" $ nf (B.toLazyByteString . AB.string) txt
20− , bench "string via text" $ nf (encodingToLazyByteString . text . T.pack) txt
18+ , bench "string via text" $ nf (B.toLazyByteString . AB.text . T.pack) txt
21 ]
19 ]
22 ]
20 ]
benchmarks/Options.hs+3
@@ -1,5 +1,8 @@
1module Options (opts) where
1module Options (opts) where
2
2
3+import Prelude ()
4+import Prelude.Compat
5+
3import Data.Aeson.Types
6import Data.Aeson.Types
4
7
5opts :: Options
8opts :: Options
benchmarks/ReadFile.hs+1−1
@@ -1,9 +1,9 @@
1{-# LANGUAGE BangPatterns #-}
1{-# LANGUAGE BangPatterns #-}
2−{-# LANGUAGE NoImplicitPrelude #-}
3{-# LANGUAGE OverloadedStrings #-}
2{-# LANGUAGE OverloadedStrings #-}
4
3
5module Main (main) where
4module Main (main) where
6
5
6+import Prelude ()
7import Prelude.Compat
7import Prelude.Compat
8
8
9import Control.DeepSeq
9import Control.DeepSeq
benchmarks/Typed.hs+1−2
@@ -1,7 +1,6 @@
1−{-# LANGUAGE NoImplicitPrelude #-}
2−
3module Main (main) where
1module Main (main) where
4
2
3+import Prelude ()
5import Prelude.Compat
4import Prelude.Compat
6
5
7import Criterion.Main
6import Criterion.Main
benchmarks/Typed/Common.hs+10−1
@@ -1,16 +1,25 @@
1−{-# LANGUAGE NoImplicitPrelude #-}
1+{-# LANGUAGE CPP #-}
2+#ifdef HAS_BOTH_AESON_AND_BENCHMARKS
3+{-# LANGUAGE PackageImports #-}
4+#endif
2
5
3{-# OPTIONS_GHC -fno-warn-unused-imports #-}
6{-# OPTIONS_GHC -fno-warn-unused-imports #-}
4
7
5module Typed.Common (load) where
8module Typed.Common (load) where
6
9
10+import Prelude ()
7import Prelude.Compat
11import Prelude.Compat
8
12
9import Data.ByteString.Lazy as L
13import Data.ByteString.Lazy as L
10import System.Exit
14import System.Exit
11import System.IO
15import System.IO
12
16
17+#ifndef HAS_BOTH_AESON_AND_BENCHMARKS
13import Data.Aeson hiding (Result)
18import Data.Aeson hiding (Result)
19+#else
20+import "aeson" Data.Aeson hiding (Result)
21+import qualified "aeson-benchmarks" Data.Aeson as B
22+#endif
14
23
15load :: FromJSON a => FilePath -> IO a
24load :: FromJSON a => FilePath -> IO a
16load fileName = do
25load fileName = do
benchmarks/Typed/Generic.hs+31−15
@@ -1,44 +1,60 @@
1−{-# LANGUAGE NoImplicitPrelude #-}
1+{-# LANGUAGE PackageImports #-}
2−
3module Typed.Generic (benchmarks, decodeBenchmarks) where
2module Typed.Generic (benchmarks, decodeBenchmarks) where
4
3
4+import Prelude ()
5import Prelude.Compat
5import Prelude.Compat
6
6
7−import Data.Aeson hiding (Result)
7+import "aeson" Data.Aeson hiding (Result)
8import Criterion
8import Criterion
9import Data.ByteString.Lazy as L
9import Data.ByteString.Lazy as L
10import Twitter.Generic
10import Twitter.Generic
11import Typed.Common
11import Typed.Common
12+import qualified "aeson-benchmarks" Data.Aeson as B
13+
14+encodeDirectA :: Result -> L.ByteString
15+encodeDirectA = encode
12
16
13−encodeDirect :: Result -> L.ByteString
17+encodeViaValueA :: Result -> L.ByteString
14−encodeDirect = encode
18+encodeViaValueA = encode . toJSON
15
19
16−encodeViaValue :: Result -> L.ByteString
20+encodeDirectB :: Result -> L.ByteString
17−encodeViaValue = encode . toJSON
21+encodeDirectB = B.encode
22+
23+encodeViaValueB :: Result -> L.ByteString
24+encodeViaValueB = B.encode . B.toJSON
18
25
19benchmarks :: Benchmark
26benchmarks :: Benchmark
20benchmarks =
27benchmarks =
21 env ((,) <$> load "json-data/twitter100.json" <*> load "json-data/jp100.json") $ \ ~(twitter100, jp100) ->
28 env ((,) <$> load "json-data/twitter100.json" <*> load "json-data/jp100.json") $ \ ~(twitter100, jp100) ->
22 bgroup "encodeGeneric" [
29 bgroup "encodeGeneric" [
23 bgroup "direct" [
30 bgroup "direct" [
24− bench "twitter100" $ nf encodeDirect twitter100
31+ bench "twitter100" $ nf encodeDirectB twitter100
32+ , bench "jp100" $ nf encodeDirectB jp100
33+ , bench "twitter100 baseline" $ nf encodeDirectA twitter100
25− , bench "jp100" $ nf encodeDirect jp100
34+ , bench "jp100 baseline" $ nf encodeDirectA jp100
26 ]
35 ]
27 , bgroup "viaValue" [
36 , bgroup "viaValue" [
28− bench "twitter100" $ nf encodeViaValue twitter100
37+ bench "twitter100" $ nf encodeViaValueB twitter100
38+ , bench "jp100" $ nf encodeViaValueB jp100
39+ , bench "twitter100 baseline" $ nf encodeViaValueA twitter100
29− , bench "jp100" $ nf encodeViaValue jp100
40+ , bench "jp100 baseline" $ nf encodeViaValueA jp100
30 ]
41 ]
31 ]
42 ]
32
43
33−decodeDirect :: L.ByteString -> Maybe Result
44+decodeDirectA :: L.ByteString -> Maybe Result
34−decodeDirect = decode
45+decodeDirectA = decode
46+
47+decodeDirectB :: L.ByteString -> Maybe Result
48+decodeDirectB = B.decode
35
49
36decodeBenchmarks :: Benchmark
50decodeBenchmarks :: Benchmark
37decodeBenchmarks =
51decodeBenchmarks =
38 env ((,) <$> L.readFile "json-data/twitter100.json" <*> L.readFile "json-data/jp100.json") $ \ ~(twitter100, jp100) ->
52 env ((,) <$> L.readFile "json-data/twitter100.json" <*> L.readFile "json-data/jp100.json") $ \ ~(twitter100, jp100) ->
39 bgroup "decodeGeneric"
53 bgroup "decodeGeneric"
40 [ bgroup "direct"
54 [ bgroup "direct"
41− [ bench "twitter100" $ nf decodeDirect twitter100
55+ [ bench "twitter100" $ nf decodeDirectB twitter100
56+ , bench "jp100" $ nf decodeDirectB jp100
57+ , bench "twitter100 baseline" $ nf decodeDirectA twitter100
42− , bench "jp100" $ nf decodeDirect jp100
58+ , bench "jp100 baseline" $ nf decodeDirectA jp100
43 ]
59 ]
44 ]
60 ]
benchmarks/Typed/Manual.hs+31−15
@@ -1,44 +1,60 @@
1−{-# LANGUAGE NoImplicitPrelude #-}
1+{-# LANGUAGE PackageImports #-}
2−
3module Typed.Manual (benchmarks, decodeBenchmarks) where
2module Typed.Manual (benchmarks, decodeBenchmarks) where
4
3
4+import Prelude ()
5import Prelude.Compat
5import Prelude.Compat
6
6
7−import Data.Aeson hiding (Result)
7+import "aeson" Data.Aeson hiding (Result)
8import Criterion
8import Criterion
9import Data.ByteString.Lazy as L
9import Data.ByteString.Lazy as L
10import Twitter.Manual
10import Twitter.Manual
11import Typed.Common
11import Typed.Common
12+import qualified "aeson-benchmarks" Data.Aeson as B
13+
14+encodeDirectA :: Result -> L.ByteString
15+encodeDirectA = encode
12
16
13−encodeDirect :: Result -> L.ByteString
17+encodeViaValueA :: Result -> L.ByteString
14−encodeDirect = encode
18+encodeViaValueA = encode . toJSON
15
19
16−encodeViaValue :: Result -> L.ByteString
20+encodeDirectB :: Result -> L.ByteString
17−encodeViaValue = encode . toJSON
21+encodeDirectB = B.encode
22+
23+encodeViaValueB :: Result -> L.ByteString
24+encodeViaValueB = B.encode . B.toJSON
18
25
19benchmarks :: Benchmark
26benchmarks :: Benchmark
20benchmarks =
27benchmarks =
21 env ((,) <$> load "json-data/twitter100.json" <*> load "json-data/jp100.json") $ \ ~(twitter100, jp100) ->
28 env ((,) <$> load "json-data/twitter100.json" <*> load "json-data/jp100.json") $ \ ~(twitter100, jp100) ->
22 bgroup "encodeManual" [
29 bgroup "encodeManual" [
23 bgroup "direct" [
30 bgroup "direct" [
24− bench "twitter100" $ nf encodeDirect twitter100
31+ bench "twitter100" $ nf encodeDirectB twitter100
32+ , bench "jp100" $ nf encodeDirectB jp100
33+ , bench "twitter100 baseline" $ nf encodeDirectA twitter100
25− , bench "jp100" $ nf encodeDirect jp100
34+ , bench "jp100 baseline" $ nf encodeDirectA jp100
26 ]
35 ]
27 , bgroup "viaValue" [
36 , bgroup "viaValue" [
28− bench "twitter100" $ nf encodeViaValue twitter100
37+ bench "twitter100" $ nf encodeViaValueB twitter100
38+ , bench "jp100" $ nf encodeViaValueB jp100
39+ , bench "twitter100 baseline" $ nf encodeViaValueA twitter100
29− , bench "jp100" $ nf encodeViaValue jp100
40+ , bench "jp100 baseline" $ nf encodeViaValueA jp100
30 ]
41 ]
31 ]
42 ]
32
43
33−decodeDirect :: L.ByteString -> Maybe Result
44+decodeDirectA :: L.ByteString -> Maybe Result
34−decodeDirect = decode
45+decodeDirectA = decode
46+
47+decodeDirectB :: L.ByteString -> Maybe Result
48+decodeDirectB = B.decode
35
49
36decodeBenchmarks :: Benchmark
50decodeBenchmarks :: Benchmark
37decodeBenchmarks =
51decodeBenchmarks =
38 env ((,) <$> L.readFile "json-data/twitter100.json" <*> L.readFile "json-data/jp100.json") $ \ ~(twitter100, jp100) ->
52 env ((,) <$> L.readFile "json-data/twitter100.json" <*> L.readFile "json-data/jp100.json") $ \ ~(twitter100, jp100) ->
39 bgroup "decodeManual"
53 bgroup "decodeManual"
40 [ bgroup "direct"
54 [ bgroup "direct"
41− [ bench "twitter100" $ nf decodeDirect twitter100
55+ [ bench "twitter100" $ nf decodeDirectB twitter100
56+ , bench "jp100" $ nf decodeDirectB jp100
57+ , bench "twitter100 baseline" $ nf decodeDirectA twitter100
42− , bench "jp100" $ nf decodeDirect jp100
58+ , bench "jp100 baseline" $ nf decodeDirectA jp100
43 ]
59 ]
44 ]
60 ]
benchmarks/Typed/TH.hs+31−15
@@ -1,44 +1,60 @@
1−{-# LANGUAGE NoImplicitPrelude #-}
1+{-# LANGUAGE PackageImports #-}
2−
3module Typed.TH (benchmarks, decodeBenchmarks) where
2module Typed.TH (benchmarks, decodeBenchmarks) where
4
3
4+import Prelude ()
5import Prelude.Compat
5import Prelude.Compat
6
6
7−import Data.Aeson hiding (Result)
7+import "aeson" Data.Aeson hiding (Result)
8import Criterion
8import Criterion
9import Data.ByteString.Lazy as L
9import Data.ByteString.Lazy as L
10import Twitter.TH
10import Twitter.TH
11import Typed.Common
11import Typed.Common
12+import qualified "aeson-benchmarks" Data.Aeson as B
13+
14+encodeDirectA :: Result -> L.ByteString
15+encodeDirectA = encode
12
16
13−encodeDirect :: Result -> L.ByteString
17+encodeViaValueA :: Result -> L.ByteString
14−encodeDirect = encode
18+encodeViaValueA = encode . toJSON
15
19
16−encodeViaValue :: Result -> L.ByteString
20+encodeDirectB :: Result -> L.ByteString
17−encodeViaValue = encode . toJSON
21+encodeDirectB = B.encode
22+
23+encodeViaValueB :: Result -> L.ByteString
24+encodeViaValueB = B.encode . B.toJSON
18
25
19benchmarks :: Benchmark
26benchmarks :: Benchmark
20benchmarks =
27benchmarks =
21 env ((,) <$> load "json-data/twitter100.json" <*> load "json-data/jp100.json") $ \ ~(twitter100, jp100) ->
28 env ((,) <$> load "json-data/twitter100.json" <*> load "json-data/jp100.json") $ \ ~(twitter100, jp100) ->
22 bgroup "encodeTH" [
29 bgroup "encodeTH" [
23 bgroup "direct" [
30 bgroup "direct" [
24− bench "twitter100" $ nf encodeDirect twitter100
31+ bench "twitter100" $ nf encodeDirectB twitter100
32+ , bench "jp100" $ nf encodeDirectB jp100
33+ , bench "twitter100 baseline" $ nf encodeDirectA twitter100
25− , bench "jp100" $ nf encodeDirect jp100
34+ , bench "jp100 baseline" $ nf encodeDirectA jp100
26 ]
35 ]
27 , bgroup "viaValue" [
36 , bgroup "viaValue" [
28− bench "twitter100" $ nf encodeViaValue twitter100
37+ bench "twitter100" $ nf encodeViaValueA twitter100
38+ , bench "jp100" $ nf encodeViaValueA jp100
39+ , bench "twitter100 baseline" $ nf encodeViaValueB twitter100
29− , bench "jp100" $ nf encodeViaValue jp100
40+ , bench "jp100 baseline" $ nf encodeViaValueB jp100
30 ]
41 ]
31 ]
42 ]
32
43
33−decodeDirect :: L.ByteString -> Maybe Result
44+decodeDirectA :: L.ByteString -> Maybe Result
34−decodeDirect = decode
45+decodeDirectA = decode
46+
47+decodeDirectB :: L.ByteString -> Maybe Result
48+decodeDirectB = B.decode
35
49
36decodeBenchmarks :: Benchmark
50decodeBenchmarks :: Benchmark
37decodeBenchmarks =
51decodeBenchmarks =
38 env ((,) <$> L.readFile "json-data/twitter100.json" <*> L.readFile "json-data/jp100.json") $ \ ~(twitter100, jp100) ->
52 env ((,) <$> L.readFile "json-data/twitter100.json" <*> L.readFile "json-data/jp100.json") $ \ ~(twitter100, jp100) ->
39 bgroup "decodeTH"
53 bgroup "decodeTH"
40 [ bgroup "direct"
54 [ bgroup "direct"
41− [ bench "twitter100" $ nf decodeDirect twitter100
55+ [ bench "twitter100" $ nf decodeDirectB twitter100
56+ , bench "jp100" $ nf decodeDirectB jp100
57+ , bench "twitter100 baseline" $ nf decodeDirectA twitter100
42− , bench "jp100" $ nf decodeDirect jp100
58+ , bench "jp100 baseline" $ nf decodeDirectA jp100
43 ]
59 ]
44 ]
60 ]
benchmarks/aeson-benchmarks.cabal+70−167
@@ -1,21 +1,65 @@
1name: aeson-benchmarks
1name: aeson-benchmarks
2version: 0
2version: 0
3build-type: Simple
3build-type: Simple
4+
4−cabal-version: >=1.10
5+cabal-version: >=1.8
5
6
6flag bytestring-builder
7flag bytestring-builder
7 description: Depend on the bytestring-builder package for backwards compatibility.
8 description: Depend on the bytestring-builder package for backwards compatibility.
8 default: False
9 default: False
9 manual: False
10 manual: False
10
11
11−flag local-aeson
12− description: Build the local version of aeson, to avoid rebuilding aeson's
13− reverse dependencies for benchmarking (statistics, criterion).
14− default: True
15− manual: True
16−
17library
12library
18− default-language: Haskell2010
13+ hs-source-dirs: .. . ../ffi ../pure ../attoparsec-iso8601
14+ c-sources: ../cbits/unescape_string.c
15+ exposed-modules:
16+ Data.Aeson
17+ Data.Aeson.Encoding
18+ Data.Aeson.Encoding.Builder
19+ Data.Aeson.Encoding.Internal
20+ Data.Aeson.Internal
21+ Data.Aeson.Internal.Functions
22+ Data.Aeson.Internal.Time
23+ Data.Aeson.Parser
24+ Data.Aeson.Parser.Internal
25+ Data.Aeson.Parser.Time
26+ Data.Aeson.Parser.Unescape
27+ Data.Aeson.Parser.UnescapeFFI
28+ Data.Aeson.Parser.UnescapePure
29+ Data.Aeson.TH
30+ Data.Aeson.Text
31+ Data.Aeson.Types
32+ Data.Aeson.Types.Class
33+ Data.Aeson.Types.FromJSON
34+ Data.Aeson.Types.Generic
35+ Data.Aeson.Types.Internal
36+ Data.Aeson.Types.ToJSON
37+ Data.Attoparsec.Time
38+ Data.Attoparsec.Time.Internal
39+
40+ build-depends:
41+ attoparsec >= 0.13.0.1,
42+ base == 4.*,
43+ base-compat >= 0.9.1 && <0.10,
44+ time-locale-compat >=0.1.1 && <0.2,
45+ containers,
46+ deepseq,
47+ dlist >= 0.2,
48+ fail == 4.9.*,
49+ ghc-prim >= 0.2,
50+ hashable >= 1.1.2.0,
51+ mtl,
52+ scientific >= 0.3.4.7 && < 0.4,
53+ syb,
54+ tagged >=0.8.3 && <0.9,
55+ template-haskell >= 2.4,
56+ text >= 1.1.1.0,
57+ th-abstraction >= 0.2.2 && < 0.3,
58+ time,
59+ transformers,
60+ unordered-containers >= 0.2.3.0,
61+ uuid-types >= 1.0.3 && <1.1,
62+ vector >= 0.7.1
19
63
20 if flag(bytestring-builder)
64 if flag(bytestring-builder)
21 build-depends: bytestring >= 0.9 && < 0.10.4,
65 build-depends: bytestring >= 0.9 && < 0.10.4,
@@ -23,139 +67,26 @@ library
23 else
67 else
24 build-depends: bytestring >= 0.10.4
68 build-depends: bytestring >= 0.10.4
25
69
26− if flag(local-aeson)
27−
28− hs-source-dirs: .. ../ffi ../pure ../attoparsec-iso8601
29− c-sources: ../cbits/unescape_string.c
30− build-depends:
31− attoparsec,
32− base,
33− base-compat,
34− containers,
35− deepseq,
36− dlist,
37− fail,
38− ghc-prim,
39− hashable,
40− mtl,
41− primitive,
42− scientific,
43− syb,
44− tagged,
45− template-haskell,
46− text,
47− th-abstraction,
48− time-compat,
49− time,
50− transformers,
51− unordered-containers,
52− uuid-types,
53− vector
54−
55− if !impl(ghc >= 8.6)
70+ if impl(ghc >=7.8)
56− build-depends: contravariant
71+ cpp-options: -DHAS_COERCIBLE
57−
58− if !impl(ghc >= 8.0)
59− -- `Data.Semigroup` is available in base only since GHC 8.0 / base 4.9
60− build-depends: semigroups,
61− transformers-compat
62
72
63− if !impl(ghc >= 7.10)
73+ if !impl(ghc >= 8.0)
64− -- `Numeric.Natural` is available in base only since GHC 7.10 / base 4.8
74+ -- `Data.Semigroup` is available in base only since GHC 8.0 / base 4.9
65− build-depends: nats,
75+ build-depends: semigroups >= 0.18.2 && < 0.19
66− void
67
76
68− include-dirs: ../include
77+ include-dirs: ../include
69−
70− ghc-options: -O2 -Wall
71− cpp-options: -DGENERICS
72−
73− exposed-modules:
74− Data.Aeson
75− Data.Aeson.Encoding
76− Data.Aeson.Encoding.Builder
77− Data.Aeson.Encoding.Internal
78− Data.Aeson.Internal
79− Data.Aeson.Internal.Functions
80− Data.Aeson.Internal.Time
81− Data.Aeson.Parser
82− Data.Aeson.Parser.Internal
83− Data.Aeson.Parser.Time
84− Data.Aeson.Parser.Unescape
85− Data.Aeson.Parser.UnescapeFFI
86− Data.Aeson.Parser.UnescapePure
87− Data.Aeson.TH
88− Data.Aeson.Text
89− Data.Aeson.Types
90− Data.Aeson.Types.Class
91− Data.Aeson.Types.FromJSON
92− Data.Aeson.Types.Generic
93− Data.Aeson.Types.Internal
94− Data.Aeson.Types.ToJSON
95− Data.Attoparsec.Time
96− Data.Attoparsec.Time.Internal
97−
98− else
99−
100− build-depends: aeson
101−
102− reexported-modules:
103− Data.Aeson,
104− Data.Aeson.Encoding,
105− Data.Aeson.Parser.Internal,
106− Data.Aeson.Text,
107− Data.Aeson.TH,
108− Data.Aeson.Types
109−
110−executable aeson-benchmark-auto-compare
111− default-language: Haskell2010
112− main-is: AutoCompare.hs
113− hs-source-dirs: .
114− ghc-options: -Wall -O2 -rtsopts
115− other-modules:
116− Auto.T.D
117− Auto.T.BigRecord
118− Auto.T.BigProduct
119− Auto.T.BigSum
120− Auto.G.D
121− Auto.G.BigRecord
122− Auto.G.BigProduct
123− Auto.G.BigSum
124− Options
125− build-depends:
126− aeson-benchmarks,
127− base,
128− criterion,
129− deepseq,
130− template-haskell
131
78
132−executable aeson-benchmark-escape
133− default-language: Haskell2010
134− main-is: Escape.hs
135− hs-source-dirs: ../examples .
136− ghc-options: -Wall -O2 -rtsopts
79+ ghc-options: -O2 -Wall
137− if flag(local-aeson)
80+ cpp-options: -DGENERICS
138− build-depends:
139− aeson-benchmarks,
140− base,
141− base-compat,
142− bytestring,
143− criterion >= 1.0,
144− deepseq,
145− ghc-prim,
146− text
147− else
148− -- Disabled because of inaccessible internals
149− buildable: False
150
81
151executable aeson-benchmark-compare
82executable aeson-benchmark-compare
152− default-language: Haskell2010
153 main-is: Compare.hs
83 main-is: Compare.hs
154 hs-source-dirs: ../examples .
84 hs-source-dirs: ../examples .
155 ghc-options: -Wall -O2 -rtsopts
85 ghc-options: -Wall -O2 -rtsopts
156 other-modules:
86 other-modules:
157 Compare.BufferBuilder
87 Compare.BufferBuilder
158 Compare.JsonBench
88 Compare.JsonBench
89+ Compare.JsonBuilder
159 Twitter
90 Twitter
160 Twitter.Manual
91 Twitter.Manual
161 Typed.Common
92 Typed.Common
@@ -168,19 +99,10 @@ executable aeson-benchmark-compare
168 criterion >= 1.0,
99 criterion >= 1.0,
169 deepseq,
100 deepseq,
170 ghc-prim,
101 ghc-prim,
102+ json-builder,
171 text
103 text
172
104
173− if impl(ghc < 8.4)
174− other-modules:
175− Compare.JsonBuilder
176− build-depends:
177− json-builder
178− if impl(ghc < 8.0)
179− build-depends:
180− semigroups
181−
182executable aeson-benchmark-micro
105executable aeson-benchmark-micro
183− default-language: Haskell2010
184 main-is: Micro.hs
106 main-is: Micro.hs
185 hs-source-dirs: ../examples .
107 hs-source-dirs: ../examples .
186 ghc-options: -Wall -O2 -rtsopts
108 ghc-options: -Wall -O2 -rtsopts
@@ -195,10 +117,12 @@ executable aeson-benchmark-micro
195 text
117 text
196
118
197executable aeson-benchmark-typed
119executable aeson-benchmark-typed
198− default-language: Haskell2010
199 main-is: Typed.hs
120 main-is: Typed.hs
200 hs-source-dirs: ../examples .
121 hs-source-dirs: ../examples .
201 ghc-options: -Wall -O2 -rtsopts
122 ghc-options: -Wall -O2 -rtsopts
123+ -- We must help ourself in situations when there is both
124+ -- aeson and aeson-benchmakrs
125+ cpp-options: -DHAS_BOTH_AESON_AND_BENCHMARKS
202 other-modules:
126 other-modules:
203 Twitter
127 Twitter
204 Twitter.Generic
128 Twitter.Generic
@@ -210,6 +134,7 @@ executable aeson-benchmark-typed
210 Typed.Manual
134 Typed.Manual
211 Typed.TH
135 Typed.TH
212 build-depends:
136 build-depends:
137+ aeson,
213 aeson-benchmarks,
138 aeson-benchmarks,
214 base,
139 base,
215 base-compat,
140 base-compat,
@@ -224,14 +149,13 @@ executable aeson-benchmark-typed
224 bytestring-builder >= 0.10.4 && < 1
149 bytestring-builder >= 0.10.4 && < 1
225 else
150 else
226 build-depends: bytestring >= 0.10.4
151 build-depends: bytestring >= 0.10.4
227− if impl(ghc < 8.0)
228− build-depends: semigroups
229
152
230executable aeson-benchmark-compare-with-json
153executable aeson-benchmark-compare-with-json
231− default-language: Haskell2010
232 main-is: CompareWithJSON.hs
154 main-is: CompareWithJSON.hs
233 ghc-options: -Wall -O2 -rtsopts
155 ghc-options: -Wall -O2 -rtsopts
156+ cpp-options: -DHAS_BOTH_AESON_AND_BENCHMARKS
234 build-depends:
157 build-depends:
158+ aeson,
235 aeson-benchmarks,
159 aeson-benchmarks,
236 base,
160 base,
237 base-compat,
161 base-compat,
@@ -243,7 +167,6 @@ executable aeson-benchmark-compare-with-json
243 text
167 text
244
168
245executable aeson-benchmark-aeson-encode
169executable aeson-benchmark-aeson-encode
246− default-language: Haskell2010
247 main-is: AesonEncode.hs
170 main-is: AesonEncode.hs
248 ghc-options: -Wall -O2 -rtsopts
171 ghc-options: -Wall -O2 -rtsopts
249 build-depends:
172 build-depends:
@@ -256,7 +179,6 @@ executable aeson-benchmark-aeson-encode
256 time
179 time
257
180
258executable aeson-benchmark-aeson-parse
181executable aeson-benchmark-aeson-parse
259− default-language: Haskell2010
260 main-is: AesonParse.hs
182 main-is: AesonParse.hs
261 ghc-options: -Wall -O2 -rtsopts
183 ghc-options: -Wall -O2 -rtsopts
262 build-depends:
184 build-depends:
@@ -268,7 +190,6 @@ executable aeson-benchmark-aeson-parse
268 time
190 time
269
191
270executable aeson-benchmark-json-parse
192executable aeson-benchmark-json-parse
271− default-language: Haskell2010
272 main-is: JsonParse.hs
193 main-is: JsonParse.hs
273 ghc-options: -Wall -O2 -rtsopts
194 ghc-options: -Wall -O2 -rtsopts
274 build-depends:
195 build-depends:
@@ -279,7 +200,6 @@ executable aeson-benchmark-json-parse
279 time
200 time
280
201
281executable aeson-benchmark-dates
202executable aeson-benchmark-dates
282− default-language: Haskell2010
283 main-is: Dates.hs
203 main-is: Dates.hs
284 ghc-options: -Wall -O2 -rtsopts
204 ghc-options: -Wall -O2 -rtsopts
285 build-depends:
205 build-depends:
@@ -291,14 +211,12 @@ executable aeson-benchmark-dates
291 aeson-benchmarks,
211 aeson-benchmarks,
292 text,
212 text,
293 time
213 time
294− if impl(ghc >= 8.2)
295− ghc-options: -Wno-simplifiable-class-constraints
296
214
297executable aeson-benchmark-map
215executable aeson-benchmark-map
298− default-language: Haskell2010
299 main-is: AesonMap.hs
216 main-is: AesonMap.hs
300 ghc-options: -Wall -O2 -rtsopts
217 ghc-options: -Wall -O2 -rtsopts
301 build-depends:
218 build-depends:
219+ aeson,
302 aeson-benchmarks,
220 aeson-benchmarks,
303 base,
221 base,
304 base-compat,
222 base-compat,
@@ -312,10 +230,10 @@ executable aeson-benchmark-map
312 unordered-containers
230 unordered-containers
313
231
314executable aeson-benchmark-foldable
232executable aeson-benchmark-foldable
315− default-language: Haskell2010
316 main-is: AesonFoldable.hs
233 main-is: AesonFoldable.hs
317 ghc-options: -Wall -O2 -rtsopts
234 ghc-options: -Wall -O2 -rtsopts
318 build-depends:
235 build-depends:
236+ aeson,
319 aeson-benchmarks,
237 aeson-benchmarks,
320 base,
238 base,
321 base-compat,
239 base-compat,
@@ -328,18 +246,3 @@ executable aeson-benchmark-foldable
328 text,
246 text,
329 unordered-containers,
247 unordered-containers,
330 vector
248 vector
331−
332−executable aeson-issue-673
333− default-language: Haskell2010
334− main-is: Issue673.hs
335− ghc-options: -Wall -O1 -rtsopts
336− build-depends:
337− aeson-benchmarks,
338− attoparsec,
339− base,
340− bytestring,
341− scientific,
342− base-compat,
343− criterion >= 1.0
344− if impl(ghc < 8.0)
345− build-depends: semigroups
cabal.project−3
@@ -1,5 +1,2 @@
1with-compiler: ghc
1with-compiler: ghc
2packages: .
2packages: .
3−packages: attoparsec-iso8601
4−-- packages: benchmarks
5−tests: true
changelog.md+1−154
@@ -1,159 +1,6 @@
1For the latest version of this document, please see [https://github.com/bos/aeson/blob/master/changelog.md](https://github.com/bos/aeson/blob/master/changelog.md).
1For the latest version of this document, please see [https://github.com/bos/aeson/blob/master/changelog.md](https://github.com/bos/aeson/blob/master/changelog.md).
2
2
3−### upcoming
4−
5−* `FromJSON` instance of `Ratio a` now parses numbers in addtion to
6− standard `{numerator=..., denumerator=...}` encoding.
7−
8−### 1.4.6.0
9−
10−* Provide a clearer error message when a required tagKey for a constructor is missing, thanks to Guru Devanla.
11− The error message now looks like this: `Error in $: parsing Types.SomeType failed, expected Object with key "tag" containing one of ["nullary","unary","product","record","list"], key "tag" not found`
12−
13−* Add `formatPath` and `formatRelativePath` functions to turn a `JSONPath` into a `String`, thanks to Robbie McMichael
14−
15−
16−### 1.4.5.0
17−
18−* Expose `(<?>)`, `JSONPath` and `JSONPathElement(..)` from `Data.Aeson.Types`. Previously only available through internal modules. Thanks to Luke Clifton.
19−
20−* Support for base-compat 0.11, thanks to Ryan Scott.
21−
22−* Travis build for GHC 8.8, thanks to Oleg Grenrus.
23−
24−### 1.4.4.0
25−
26−**New features**:
27−
28−* Adds a parameterized parser `jsonWith` that can be used to choose how to handle duplicate keys in objects, thanks to Xia Li-Yao.
29−
30−* Add generic implementations of `FromJSONKey` and `ToJSONKey`, thanks to Xia Li-Yao. Example:
31−
32−```haskell
33−data Foo = Bar
34− deriving Generic
35−
36−opts :: JSONKeyOptions
37−opts = defaultJSONKeyOptions { keyModifier = toLower }
38−
39−instance ToJSONKey Foo where
40− toJSONKey = genericToJSONKey opts
41−
42−instance FromJSONKey Foo where
43− fromJSONKey = genericFromJSONKey opts
44−```
45−
46−**Minor**:
47−* aeson now uses `time-compat` instead of `time-locale-compat`, thanks to Oleg Grenrus.
48−* Prepare for `MonadFail` breakages in GHC 8.8, thanks to Oleg Grenrus.
49−* Require `bytestring >= 0.10.8.1` for newer GHCs to avoid build failures, thanks to Oleg Grenrus.
50−* Support `primitive 0.7.*`, thanks to Adam Bergmark.
51−* Allow `semigroups 0.19.*` and `hashable 1.3.*`, thanks to Oleg Grenrus.
52−* Fix a typo in the error message when parsing `NonEmpty`, thanks to Colin Woodbury.
53−* Document surprising behavior when using `omitNothingFields` with type variables, thanks to Xia Li-Yao.
54−
55−**Internal changes**:
56−* Code cleanup by Oleg Grenrus
57−* Fix dependencies of the benchmarks on older GHC's, thanks to Xia Li-Yao.
58−
59−### 1.4.3.0
60−* Improve error messages for FromJSON in existing instances and GHC Generic implementation. Thanks to Xia Li-Yao & Igor Pashev.
61−* Tweak error-reporting combinators and their documentation. Thanks to Xia Li-Yao.
62− * `typeMismatch` is now about comparing JSON types (i.e., the expected and actual names of the Value constructor).
63− * `withObject` and other `with*` combinators now also mention the JSON types they expect
64− * New `unexpected` and `prependFailure` combinators.
65−* Add `Contravariant` `ToJSONKeyFunction` instance. Thanks to Oleg Grenrus.
66−* Add `KeyValue` instance for `Object`. Thanks to Robert Hensing.
67−* Improve performance when parsing certain large numbers, thanks to Oleg Grenrus.
68−* Add `Data.Aeson.QQ.Simple` - A limited version of aeson-qq. Thanks to Oleg Grenrus.
69−* Exposes internal helper functions like `<?>`, `JSONPath`, and `parseIndexedJSON` from `Data.Aeson` module. Thanks to Abid Uzair.
70−* Better error messages when there are syntax errors parsing objects and arrays. Thanks to Fintan Halpenny.
71−* Support building with `th-abstraction-0.3.0.0` or later. Thanks to Ryan Scott.
72−
73−### 1.4.2.0
74−
75−* Add `Data.Aeson.QQ.Simple` which is a simpler version of the `aeson-qq` package, it does not support interpolation, thanks to Oleg Grenrus.
76−* Add `Contravariant ToJSONKeyFunction` instance, thanks to Oleg Grenrus.
77−* Add `KeyValue Object` instance, thanks to Robert Hensing
78−* Improved performance when parsing large numbers, thanks to Oleg Grenrus.
79−
80−### 1.4.1.0
81−
82−* Optimizations of generics, thanks to Rémy Oudompheng, here are some numbers for GHC 8.4:
83− * Compilation time: G/BigProduct.hs is 25% faster, G/BigRecord.hs is 2x faster.
84− * Runtime performance: BigRecord/toJSON/generic and BigProduct/encode/generic are more than 2x faster.
85−* Added To/FromJSON instances for `Void` and Generics's `V1`, thanks to Will Yager
86−* Added To/FromJSON instances for `primitive`'s `Array`, `SmallArray`, `PrimArray` and `UnliftedArray`, thanks to Andrew Thad.
87−* Fixes handling of `UTCTime` wrt. leap seconds , thanks to Adam Schønemann
88−* Warning and documentation fixes thanks to tom-bop, Gabor Greif, Ian Jeffries, and Mateusz Curyło.
89−
90−## 1.4.0.0
91−
92−This release introduces bounds on the size of `Scientific` numbers when they are converted to other arbitrary precision types that do not represent them efficiently in memory.
93−
94−This means that trying to decode a number such as `1e1000000000` into an `Integer` will now fail instead of using a lot of memory. If you need to represent large numbers you can add a newtype (preferably over `Scientific`) and providing a parser using `withScientific`.
95−
96−The following instances are affected by this:
97−* `FromJSON Natural`
98−* `FromJSONKey Natural`
99−* `FromJSON Integer`
100−* `FromJSONKey Integer`
101−* `FromJSON NominalDiffTime`
102−
103−For the same reasons the following instances & functions have been removed:
104−* Remove `FromJSON Data.Attoparsec.Number` instance. Note that `Data.Attoparsec.Number` is deprecated.
105−* Remove deprecated `withNumber`, use `withScientific` instead.
106−
107−Finally, encoding integral values with large exponents now uses scientific notation, this saves space for large numbers.
108−
109−#### 1.3.1.1
110−
111−* Catch 0 denominators when parsing Ratio
112−
113−### 1.3.1.0
114−
115−* Fix bug in generically derived `FromJSON` instances that are using `unwrapUnaryRecords`, thanks to Xia Li-yao
116−* Allow base-compat 0.10.*, thanks to Oleg Grenrus
117−
118−## 1.3.0.0
119−
120−Breaking changes:
121−* `GKeyValue` has been renamed to `KeyValuePair`, thanks to Xia Li-yao
122−* Removed unused `FromJSON` constraint in `withEmbeddedJson`, thanks to Tristan Seligmann
123−
124−Other improvements:
125−* Optimizations of TH toEncoding, thanks to Xia Li-yao
126−* Optimizations of hex decoding when using the default/pure unescape implementation, thanks to Xia Li-yao
127−* Improved error message on `Day` parse failures, thanks to Gershom Bazerman
128−* Add `encodeFile` as well as `decodeFile*` variants, thanks to Markus Hauck
129−* Documentation fixes, thanks to Lennart Spitzner
130−* CPP cleanup, thanks to Ryan Scott
131−
132−### 1.2.4.0
133−
134−* Add `Ord` instance for `JSONPathElement`, thanks to Simon Hengel.
135−
136−
137−### 1.2.3.0
138−
139−* Added `withEmbeddedJSON` to help parse JSON embedded inside a JSON string, thanks to Jesse Kempf.
140−* Memory usage improvements to the default (pure) parser, thanks to Jonathan Paugh. Also thanks to Neil Mitchell & Oleg Grenrus for contributing a benchmark.
141−* `omitNothingFields` now works for the `Option` newtype, thanks to Xia Li-yao.
142−* Some documentation fixes, thanks to Jonathan Paug & Philippe Crama.
143−
144−### 1.2.2.0
145−
146−* Add `FromJSON` and `ToJSON` instances for
147− * `DiffTime`, thanks to Víctor López Juan.
148− * `CTime`, thanks to Daniel Díaz.
149−* Fix handling of fractions when parsing Natural, thanks to Yuriy Syrovetskiy.
150−* Change text in error messages for Integral types to make them follow the common pattern, thanks to Yuriy Syrovetskiy.
151−* Add missing `INCOHERENT` pragma for `RecordToPair`, thanks to Xia Li-yao.
152−* Everything related to `Options` is now exported from `Data.Aeson`, thanks to Xia Li-yao.
153−* Optimizations to not escape text in clear cases, thanks to Oleg Grenrus.
154−* Some documentation fixes, thanks to Phil de Joux & Xia Li-yao.
155−
156−### 1.2.1.0
3+## 1.2.1.0
157
4
158* Add `parserThrowError` and `parserCatchError` combinators, thanks to Oleg Grenrus.
5* Add `parserThrowError` and `parserCatchError` combinators, thanks to Oleg Grenrus.
159
6
examples/Generic.hs+1−1
@@ -6,11 +6,11 @@
6-- automatically derive the Generic class for us.
6-- automatically derive the Generic class for us.
7
7
8{-# LANGUAGE DeriveGeneric #-}
8{-# LANGUAGE DeriveGeneric #-}
9−{-# LANGUAGE NoImplicitPrelude #-}
10{-# LANGUAGE OverloadedStrings #-}
9{-# LANGUAGE OverloadedStrings #-}
11
10
12module Main (main) where
11module Main (main) where
13
12
13+import Prelude ()
14import Prelude.Compat
14import Prelude.Compat
15
15
16import Data.Aeson (FromJSON, ToJSON, decode, encode)
16import Data.Aeson (FromJSON, ToJSON, decode, encode)
examples/Simplest.hs+1−1
@@ -1,9 +1,9 @@
1−{-# LANGUAGE NoImplicitPrelude #-}
2{-# LANGUAGE OverloadedStrings #-}
1{-# LANGUAGE OverloadedStrings #-}
3{-# LANGUAGE RecordWildCards #-}
2{-# LANGUAGE RecordWildCards #-}
4
3
5module Main (main) where
4module Main (main) where
6
5
6+import Prelude ()
7import Prelude.Compat
7import Prelude.Compat
8
8
9import Control.Applicative (empty)
9import Control.Applicative (empty)
examples/TemplateHaskell.hs+1−1
@@ -1,12 +1,12 @@
1-- We can use Template Haskell (TH) to generate instances of the
1-- We can use Template Haskell (TH) to generate instances of the
2-- FromJSON and ToJSON classes automatically. This is the fastest way
2-- FromJSON and ToJSON classes automatically. This is the fastest way
3-- to add JSON support for a type.
3-- to add JSON support for a type.
4−{-# LANGUAGE NoImplicitPrelude #-}
5{-# LANGUAGE OverloadedStrings #-}
4{-# LANGUAGE OverloadedStrings #-}
6{-# LANGUAGE TemplateHaskell #-}
5{-# LANGUAGE TemplateHaskell #-}
7
6
8module Main (main) where
7module Main (main) where
9
8
9+import Prelude ()
10import Prelude.Compat
10import Prelude.Compat
11
11
12import Data.Aeson (decode, encode)
12import Data.Aeson (decode, encode)
examples/Twitter.hs+2−1
@@ -10,7 +10,6 @@
10
10
11{-# LANGUAGE DeriveDataTypeable #-}
11{-# LANGUAGE DeriveDataTypeable #-}
12{-# LANGUAGE DeriveGeneric #-}
12{-# LANGUAGE DeriveGeneric #-}
13−{-# LANGUAGE NoImplicitPrelude #-}
14
13
15module Twitter
14module Twitter
16 (
15 (
@@ -20,6 +19,7 @@ module Twitter
20 , Result(..)
19 , Result(..)
21 ) where
20 ) where
22
21
22+import Prelude ()
23import Prelude.Compat
23import Prelude.Compat
24
24
25import Control.DeepSeq
25import Control.DeepSeq
@@ -27,6 +27,7 @@ import Data.Data (Typeable, Data)
27import Data.Int (Int64)
27import Data.Int (Int64)
28import Data.Text (Text)
28import Data.Text (Text)
29import GHC.Generics (Generic)
29import GHC.Generics (Generic)
30+import Prelude hiding (id)
30
31
31{-# ANN module "Hlint: ignore Use camelCase" #-}
32{-# ANN module "Hlint: ignore Use camelCase" #-}
32{-# ANN module "Hlint: ignore Use newtype instead of data" #-}
33{-# ANN module "Hlint: ignore Use newtype instead of data" #-}
examples/Twitter/Generic.hs+37−2
@@ -1,9 +1,12 @@
1-- Use GHC generics to automatically generate good instances.
1-- Use GHC generics to automatically generate good instances.
2
2
3−{-# LANGUAGE NoImplicitPrelude #-}
3+{-# LANGUAGE CPP #-}
4−
5{-# OPTIONS_GHC -fno-warn-orphans #-}
4{-# OPTIONS_GHC -fno-warn-orphans #-}
6
5
6+#ifdef HAS_BOTH_AESON_AND_BENCHMARKS
7+{-# LANGUAGE PackageImports #-}
8+#endif
9+
7module Twitter.Generic
10module Twitter.Generic
8 (
11 (
9 Metadata(..)
12 Metadata(..)
@@ -12,12 +15,18 @@ module Twitter.Generic
12 , Result(..)
15 , Result(..)
13 ) where
16 ) where
14
17
18+import Prelude ()
15import Prelude.Compat ()
19import Prelude.Compat ()
16
20
17import Twitter
21import Twitter
18import Twitter.Options
22import Twitter.Options
19
23
24+#ifndef HAS_BOTH_AESON_AND_BENCHMARKS
20import Data.Aeson (ToJSON (..), FromJSON (..), genericToJSON, genericToEncoding, genericParseJSON)
25import Data.Aeson (ToJSON (..), FromJSON (..), genericToJSON, genericToEncoding, genericParseJSON)
26+#else
27+import "aeson" Data.Aeson (ToJSON (..), FromJSON (..), genericToJSON, genericToEncoding, genericParseJSON)
28+import qualified "aeson-benchmarks" Data.Aeson as B
29+#endif
21
30
22instance ToJSON Metadata where
31instance ToJSON Metadata where
23 toJSON = genericToJSON twitterOptions
32 toJSON = genericToJSON twitterOptions
@@ -42,3 +51,29 @@ instance ToJSON Result where
42 toEncoding = genericToEncoding twitterOptions
51 toEncoding = genericToEncoding twitterOptions
43instance FromJSON Result where
52instance FromJSON Result where
44 parseJSON = genericParseJSON twitterOptions
53 parseJSON = genericParseJSON twitterOptions
54+
55+#ifdef HAS_BOTH_AESON_AND_BENCHMARKS
56+instance B.ToJSON Metadata where
57+ toJSON = B.genericToJSON btwitterOptions
58+ toEncoding = B.genericToEncoding btwitterOptions
59+instance B.FromJSON Metadata where
60+ parseJSON = B.genericParseJSON btwitterOptions
61+
62+instance B.ToJSON Geo where
63+ toJSON = B.genericToJSON btwitterOptions
64+ toEncoding = B.genericToEncoding btwitterOptions
65+instance B.FromJSON Geo where
66+ parseJSON = B.genericParseJSON btwitterOptions
67+
68+instance B.ToJSON Story where
69+ toJSON = B.genericToJSON btwitterOptions
70+ toEncoding = B.genericToEncoding btwitterOptions
71+instance B.FromJSON Story where
72+ parseJSON = B.genericParseJSON btwitterOptions
73+
74+instance B.ToJSON Result where
75+ toJSON = B.genericToJSON btwitterOptions
76+ toEncoding = B.genericToEncoding btwitterOptions
77+instance B.FromJSON Result where
78+ parseJSON = B.genericParseJSON btwitterOptions
79+#endif
examples/Twitter/Manual.hs+139−3
@@ -1,10 +1,14 @@
1-- Manually write instances.
1-- Manually write instances.
2+
2−{-# LANGUAGE NoImplicitPrelude #-}
3+{-# LANGUAGE CPP #-}
3{-# LANGUAGE OverloadedStrings #-}
4{-# LANGUAGE OverloadedStrings #-}
4{-# LANGUAGE RecordWildCards #-}
5{-# LANGUAGE RecordWildCards #-}
5−
6{-# OPTIONS_GHC -fno-warn-orphans #-}
6{-# OPTIONS_GHC -fno-warn-orphans #-}
7
7
8+#ifdef HAS_BOTH_AESON_AND_BENCHMARKS
9+{-# LANGUAGE PackageImports #-}
10+#endif
11+
8module Twitter.Manual
12module Twitter.Manual
9 (
13 (
10 Metadata(..)
14 Metadata(..)
@@ -13,13 +17,20 @@ module Twitter.Manual
13 , Result(..)
17 , Result(..)
14 ) where
18 ) where
15
19
20+import Prelude ()
16import Prelude.Compat
21import Prelude.Compat
17
22
18import Control.Applicative
23import Control.Applicative
19−import Data.Semigroup ((<>))
24+import Data.Monoid ((<>))
25+import Prelude hiding (id)
20import Twitter
26import Twitter
21
27
28+#ifndef HAS_BOTH_AESON_AND_BENCHMARKS
22import Data.Aeson hiding (Result)
29import Data.Aeson hiding (Result)
30+#else
31+import "aeson" Data.Aeson hiding (Result)
32+import qualified "aeson-benchmarks" Data.Aeson as B
33+#endif
23
34
24instance ToJSON Metadata where
35instance ToJSON Metadata where
25
36
@@ -144,3 +155,128 @@ instance FromJSON Result where
144 <*> v .: "max_id_str"
155 <*> v .: "max_id_str"
145 <*> v .: "query"
156 <*> v .: "query"
146 parseJSON _ = empty
157 parseJSON _ = empty
158+
159+#ifdef HAS_BOTH_AESON_AND_BENCHMARKS
160+instance B.ToJSON Metadata where
161+ toJSON Metadata{..} = B.object [
162+ "result_type" B..= result_type
163+ ]
164+
165+ toEncoding Metadata{..} = B.pairs $
166+ "result_type" B..= result_type
167+
168+instance B.FromJSON Metadata where
169+ parseJSON (B.Object v) = Metadata <$> v B..: "result_type"
170+ parseJSON _ = empty
171+
172+instance B.ToJSON Geo where
173+ toJSON Geo{..} = B.object [
174+ "type_" B..= type_
175+ , "coordinates" B..= coordinates
176+ ]
177+
178+ toEncoding Geo{..} = B.pairs $
179+ "type_" B..= type_
180+ <> "coordinates" B..= coordinates
181+
182+instance B.FromJSON Geo where
183+ parseJSON (B.Object v) = Geo <$>
184+ v B..: "type_"
185+ <*> v B..: "coordinates"
186+ parseJSON _ = empty
187+
188+instance B.ToJSON Story where
189+ toJSON Story{..} = B.object [
190+ "from_user_id_str" B..= from_user_id_str
191+ , "profile_image_url" B..= profile_image_url
192+ , "created_at" B..= created_at
193+ , "from_user" B..= from_user
194+ , "id_str" B..= id_str
195+ , "metadata" B..= metadata
196+ , "to_user_id" B..= to_user_id
197+ , "text" B..= text
198+ , "id" B..= id_
199+ , "from_user_id" B..= from_user_id
200+ , "geo" B..= geo
201+ , "iso_language_code" B..= iso_language_code
202+ , "to_user_id_str" B..= to_user_id_str
203+ , "source" B..= source
204+ ]
205+
206+ toEncoding Story{..} = B.pairs $
207+ "from_user_id_str" B..= from_user_id_str
208+ <> "profile_image_url" B..= profile_image_url
209+ <> "created_at" B..= created_at
210+ <> "from_user" B..= from_user
211+ <> "id_str" B..= id_str
212+ <> "metadata" B..= metadata
213+ <> "to_user_id" B..= to_user_id
214+ <> "text" B..= text
215+ <> "id" B..= id_
216+ <> "from_user_id" B..= from_user_id
217+ <> "geo" B..= geo
218+ <> "iso_language_code" B..= iso_language_code
219+ <> "to_user_id_str" B..= to_user_id_str
220+ <> "source" B..= source
221+
222+instance B.FromJSON Story where
223+ parseJSON (B.Object v) = Story <$>
224+ v B..: "from_user_id_str"
225+ <*> v B..: "profile_image_url"
226+ <*> v B..: "created_at"
227+ <*> v B..: "from_user"
228+ <*> v B..: "id_str"
229+ <*> v B..: "metadata"
230+ <*> v B..: "to_user_id"
231+ <*> v B..: "text"
232+ <*> v B..: "id"
233+ <*> v B..: "from_user_id"
234+ <*> v B..: "geo"
235+ <*> v B..: "iso_language_code"
236+ <*> v B..: "to_user_id_str"
237+ <*> v B..: "source"
238+ parseJSON _ = empty
239+
240+instance B.ToJSON Result where
241+ toJSON Result{..} = B.object [
242+ "results" B..= results
243+ , "max_id" B..= max_id
244+ , "since_id" B..= since_id
245+ , "refresh_url" B..= refresh_url
246+ , "next_page" B..= next_page
247+ , "results_per_page" B..= results_per_page
248+ , "page" B..= page
249+ , "completed_in" B..= completed_in
250+ , "since_id_str" B..= since_id_str
251+ , "max_id_str" B..= max_id_str
252+ , "query" B..= query
253+ ]
254+
255+ toEncoding Result{..} = B.pairs $
256+ "results" B..= results
257+ <> "max_id" B..= max_id
258+ <> "since_id" B..= since_id
259+ <> "refresh_url" B..= refresh_url
260+ <> "next_page" B..= next_page
261+ <> "results_per_page" B..= results_per_page
262+ <> "page" B..= page
263+ <> "completed_in" B..= completed_in
264+ <> "since_id_str" B..= since_id_str
265+ <> "max_id_str" B..= max_id_str
266+ <> "query" B..= query
267+
268+instance B.FromJSON Result where
269+ parseJSON (B.Object v) = Result <$>
270+ v B..: "results"
271+ <*> v B..: "max_id"
272+ <*> v B..: "since_id"
273+ <*> v B..: "refresh_url"
274+ <*> v B..: "next_page"
275+ <*> v B..: "results_per_page"
276+ <*> v B..: "page"
277+ <*> v B..: "completed_in"
278+ <*> v B..: "since_id_str"
279+ <*> v B..: "max_id_str"
280+ <*> v B..: "query"
281+ parseJSON _ = empty
282+#endif
examples/Twitter/Options.hs+23
@@ -1,6 +1,20 @@
1+{-# LANGUAGE CPP #-}
2+
3+#ifdef HAS_BOTH_AESON_AND_BENCHMARKS
4+{-# LANGUAGE PackageImports #-}
5+#endif
6+
1module Twitter.Options (module Twitter.Options) where
7module Twitter.Options (module Twitter.Options) where
2
8
9+#ifndef HAS_BOTH_AESON_AND_BENCHMARKS
3import Data.Aeson
10import Data.Aeson
11+import Data.Aeson.Types
12+#else
13+import "aeson" Data.Aeson
14+import "aeson" Data.Aeson.Types
15+import qualified "aeson-benchmarks" Data.Aeson as B
16+import qualified "aeson-benchmarks" Data.Aeson.Types as B
17+#endif
4
18
5twitterOptions :: Options
19twitterOptions :: Options
6twitterOptions = defaultOptions
20twitterOptions = defaultOptions
@@ -8,3 +22,12 @@ twitterOptions = defaultOptions
8 "id_" -> "id"
22 "id_" -> "id"
9 _ -> x
23 _ -> x
10 }
24 }
25+
26+#ifdef HAS_BOTH_AESON_AND_BENCHMARKS
27+btwitterOptions :: B.Options
28+btwitterOptions = B.defaultOptions
29+ { B.fieldLabelModifier = \x -> case x of
30+ "id_" -> "id"
31+ _ -> x
32+ }
33+#endif
examples/Twitter/TH.hs+18−2
@@ -1,10 +1,13 @@
1-- Use Template Haskell to generate good instances.
1-- Use Template Haskell to generate good instances.
2
2
3−{-# LANGUAGE NoImplicitPrelude #-}
3+{-# LANGUAGE CPP #-}
4{-# LANGUAGE TemplateHaskell #-}
4{-# LANGUAGE TemplateHaskell #-}
5−
6{-# OPTIONS_GHC -fno-warn-orphans #-}
5{-# OPTIONS_GHC -fno-warn-orphans #-}
7
6
7+#ifdef HAS_BOTH_AESON_AND_BENCHMARKS
8+{-# LANGUAGE PackageImports #-}
9+#endif
10+
8module Twitter.TH
11module Twitter.TH
9 (
12 (
10 Metadata(..)
13 Metadata(..)
@@ -13,13 +16,26 @@ module Twitter.TH
13 , Result(..)
16 , Result(..)
14 ) where
17 ) where
15
18
19+import Prelude ()
16
20
17import Twitter
21import Twitter
18import Twitter.Options
22import Twitter.Options
19
23
24+#ifndef HAS_BOTH_AESON_AND_BENCHMARKS
20import Data.Aeson.TH
25import Data.Aeson.TH
26+#else
27+import "aeson" Data.Aeson.TH
28+import qualified "aeson-benchmarks" Data.Aeson.TH as B
29+#endif
21
30
22$(deriveJSON twitterOptions ''Metadata)
31$(deriveJSON twitterOptions ''Metadata)
23$(deriveJSON twitterOptions ''Geo)
32$(deriveJSON twitterOptions ''Geo)
24$(deriveJSON twitterOptions ''Story)
33$(deriveJSON twitterOptions ''Story)
25$(deriveJSON twitterOptions ''Result)
34$(deriveJSON twitterOptions ''Result)
35+
36+#ifdef HAS_BOTH_AESON_AND_BENCHMARKS
37+$(B.deriveJSON btwitterOptions ''Metadata)
38+$(B.deriveJSON btwitterOptions ''Geo)
39+$(B.deriveJSON btwitterOptions ''Story)
40+$(B.deriveJSON btwitterOptions ''Result)
41+#endif
ffi/Data/Aeson/Parser/UnescapeFFI.hs+1−1
@@ -10,7 +10,7 @@ module Data.Aeson.Parser.UnescapeFFI
10import Control.Exception (evaluate, throw, try)
10import Control.Exception (evaluate, throw, try)
11import Control.Monad.ST.Unsafe (unsafeIOToST, unsafeSTToIO)
11import Control.Monad.ST.Unsafe (unsafeIOToST, unsafeSTToIO)
12import Data.ByteString as B
12import Data.ByteString as B
13−import Data.ByteString.Internal as B
13+import Data.ByteString.Internal as B hiding (c2w)
14import Data.Text.Encoding.Error (UnicodeException (..))
14import Data.Text.Encoding.Error (UnicodeException (..))
15import Data.Text.Internal (Text (..))
15import Data.Text.Internal (Text (..))
16import Data.Text.Internal.Private (runText)
16import Data.Text.Internal.Private (runText)
pure/Data/Aeson/Parser/UnescapePure.hs+36−13
@@ -119,18 +119,38 @@ decode UtfTail1 point word = case word of
119 w | 0x80 <= w && w <= 0xbf -> (UtfGround, setByte1 point word)
119 w | 0x80 <= w && w <= 0xbf -> (UtfGround, setByte1 point word)
120 _ -> throwDecodeError
120 _ -> throwDecodeError
121
121
122+{-# INLINE decode #-}
123+
122decodeHex :: Word8 -> Word16
124decodeHex :: Word8 -> Word16
123−decodeHex x
125+decodeHex 48 = 0 -- '0'
124− | 48 <= x && x <= 57 = fromIntegral x - 48 -- 0-9
126+decodeHex 49 = 1 -- '1'
125− | 65 <= x && x <= 70 = fromIntegral x - 55 -- A-F
127+decodeHex 50 = 2 -- '2'
126− | 97 <= x && x <= 102 = fromIntegral x - 87 -- a-f
128+decodeHex 51 = 3 -- '3'
129+decodeHex 52 = 4 -- '4'
130+decodeHex 53 = 5 -- '5'
131+decodeHex 54 = 6 -- '6'
132+decodeHex 55 = 7 -- '7'
133+decodeHex 56 = 8 -- '8'
134+decodeHex 57 = 9 -- '9'
135+decodeHex 65 = 10 -- 'A'
136+decodeHex 97 = 10 -- 'a'
137+decodeHex 66 = 11 -- 'B'
138+decodeHex 98 = 11 -- 'b'
139+decodeHex 67 = 12 -- 'C'
140+decodeHex 99 = 12 -- 'c'
141+decodeHex 68 = 13 -- 'D'
142+decodeHex 100 = 13 -- 'd'
143+decodeHex 69 = 14 -- 'E'
144+decodeHex 101 = 14 -- 'e'
145+decodeHex 70 = 15 -- 'F'
146+decodeHex 102 = 15 -- 'f'
127− | otherwise = throwDecodeError
147+decodeHex _ = throwDecodeError
148+{-# INLINE decodeHex #-}
128
149
129unescapeText' :: ByteString -> Text
150unescapeText' :: ByteString -> Text
130unescapeText' bs = runText $ \done -> do
151unescapeText' bs = runText $ \done -> do
131 dest <- A.new len
152 dest <- A.new len
132−
133− (pos, finalState) <- loop dest (0, StateNone) 0
153+ (pos, finalState) <- B.foldl' (f' dest) (return (0, StateNone)) bs
134
154
135 -- Check final state. Currently pos gets only increased over time, so this check should catch overflows.
155 -- Check final state. Currently pos gets only increased over time, so this check should catch overflows.
136 when ( finalState /= StateNone || pos > len)
156 when ( finalState /= StateNone || pos > len)
@@ -152,12 +172,11 @@ unescapeText' bs = runText $ \done -> do
152 (st', p) ->
172 (st', p) ->
153 return (pos, StateUtf st' p)
173 return (pos, StateUtf st' p)
154
174
155− loop :: A.MArray s -> (Int, State) -> Int -> ST s (Int, State)
175+ {-# INLINE runUtf #-}
156− loop _ ps i | i >= len = return ps
176+
157− loop dest ps i = do
158− let c = B.index bs i -- JP: We can use unsafe index once we prove bounds with Liquid Haskell.
159− ps' <- f dest ps c
177+ f' dest m c = m >>= \s -> f dest s c
160− loop dest ps' $ i+1
178+
179+ {-# INLINE f' #-}
161
180
162 -- No pending state.
181 -- No pending state.
163 f dest (pos, StateNone) c = runUtf dest pos UtfGround 0 c
182 f dest (pos, StateNone) c = runUtf dest pos UtfGround 0 c
@@ -234,6 +253,8 @@ unescapeText' bs = runText $ \done -> do
234 else
253 else
235 writeAndReturn dest pos u StateNone
254 writeAndReturn dest pos u StateNone
236
255
256+ {-# INLINE f #-}
257+
237write :: A.MArray s -> Int -> Word16 -> ST s ()
258write :: A.MArray s -> Int -> Word16 -> ST s ()
238write dest pos char =
259write dest pos char =
239 A.unsafeWrite dest pos char
260 A.unsafeWrite dest pos char
@@ -249,6 +270,8 @@ throwDecodeError :: a
249throwDecodeError =
270throwDecodeError =
250 let desc = "Data.Text.Internal.Encoding.decodeUtf8: Invalid UTF-8 stream" in
271 let desc = "Data.Text.Internal.Encoding.decodeUtf8: Invalid UTF-8 stream" in
251 throw (DecodeError desc Nothing)
272 throw (DecodeError desc Nothing)
273+{-# INLINE throwDecodeError #-}
252
274
253unescapeText :: ByteString -> Either UnicodeException Text
275unescapeText :: ByteString -> Either UnicodeException Text
254unescapeText = unsafeDupablePerformIO . try . evaluate . unescapeText'
276unescapeText = unsafeDupablePerformIO . try . evaluate . unescapeText'
277+{-# INLINE unescapeText #-}
run-hlint.shdeleted−7
@@ -1,7 +0,0 @@
1−#!/bin/bash
2−
3−if hash hlint 2>/dev/null; then
4− hlint "$@"
5−else
6− ./hlint.sh "$@"
7−fi
stack-bench.yaml+5−2
@@ -1,4 +1,4 @@
1−resolver: nightly-2019-09-21
1+resolver: lts-8.5
2# We use aeson in the snapshot to
2# We use aeson in the snapshot to
3# - avoid recompilation of criterion
3# - avoid recompilation of criterion
4# - compare against it
4# - compare against it
@@ -9,4 +9,7 @@ resolver: nightly-2019-09-21
9work-dir: .stack-work-bench
9work-dir: .stack-work-bench
10packages:
10packages:
11- benchmarks
11- benchmarks
12−extra-deps: []
12+extra-deps:
13+- aeson-1.2.1.0
14+- quickcheck-instances-0.3.14
15+- th-abstraction-0.2.2.0
stack-ffi-unescape.yamldeleted−9
@@ -1,9 +0,0 @@
1−resolver: nightly-2019-09-21
2−packages:
3−- '.'
4−flags:
5− aeson:
6− fast: true
7− cffi: true
8−extra-deps:
9−- Diff-0.4.0
stack-lts12.yamldeleted−15
@@ -1,15 +0,0 @@
1−resolver: lts-12.26
2−packages:
3−- '.'
4−- attoparsec-iso8601
5−flags:
6− aeson:
7− fast: true
8− cffi: true
9−extra-deps:
10−- base-orphans-0.8.1
11−- hashable-time-0.2.0.1
12−- QuickCheck-2.13.1
13−- quickcheck-instances-0.3.21
14−- splitmix-0.0.2
15−- time-compat-1.9.2.2
stack-lts6.yamlnew file+20
@@ -0,0 +1,20 @@
1+resolver: lts-6.30
2+packages:
3+- '.'
4+- attoparsec-iso8601
5+extra-deps:
6+- integer-logarithms-1
7+- QuickCheck-2.9.2
8+- quickcheck-instances-0.3.14
9+- semigroups-0.18.2
10+- tagged-0.8.5
11+- th-abstraction-0.2.2.0
12+- transformers-compat-0.5.1.4
13+flags:
14+flags:
15+ aeson:
16+ fast: true
17+ attoparsec-iso8601:
18+ fast: true
19+ semigroups:
20+ bytestring-builder: false
stack-lts7.yamlnew file+13
@@ -0,0 +1,13 @@
1+resolver: lts-7.19
2+packages:
3+- '.'
4+- attoparsec-iso8601
5+extra-deps:
6+- integer-logarithms-1
7+- quickcheck-instances-0.3.14
8+- th-abstraction-0.2.2.0
9+flags:
10+ aeson:
11+ fast: true
12+ attoparsec-iso8601:
13+ fast: true
stack-lts8.yamlnew file+12
@@ -0,0 +1,12 @@
1+resolver: lts-8.5
2+packages:
3+- '.'
4+- attoparsec-iso8601
5+extra-deps:
6+- quickcheck-instances-0.3.14
7+- th-abstraction-0.2.2.0
8+flags:
9+ aeson:
10+ fast: true
11+ attoparsec-iso8601:
12+ fast: true
stack-nightly.yaml+4−3
@@ -1,11 +1,12 @@
1−resolver: nightly-2019-10-14
1+resolver: nightly-2017-03-25
2packages:
2packages:
3- '.'
3- '.'
4- attoparsec-iso8601
4- attoparsec-iso8601
5+extra-deps:
6+- quickcheck-instances-0.3.14
7+- th-abstraction-0.2.2.0
5flags:
8flags:
6 aeson:
9 aeson:
7 fast: true
10 fast: true
8 attoparsec-iso8601:
11 attoparsec-iso8601:
9 fast: true
12 fast: true
10−extra-deps:
11−- generic-deriving-1.12.4
stack-pure-unescape.yamlnew file+10
@@ -0,0 +1,10 @@
1+resolver: lts-8.1
2+packages:
3+- '.'
4+extra-deps:
5+- quickcheck-instances-0.3.14
6+- th-abstraction-0.2.2.0
7+flags:
8+ aeson:
9+ fast: true
10+ cffi: false
tests/DataFamilies/Encoders.hs+1−1
@@ -1,10 +1,10 @@
1{-# LANGUAGE CPP #-}
1{-# LANGUAGE CPP #-}
2{-# LANGUAGE GADTs #-}
2{-# LANGUAGE GADTs #-}
3−{-# LANGUAGE NoImplicitPrelude #-}
4{-# LANGUAGE TemplateHaskell #-}
3{-# LANGUAGE TemplateHaskell #-}
5
4
6module DataFamilies.Encoders (module DataFamilies.Encoders) where
5module DataFamilies.Encoders (module DataFamilies.Encoders) where
7
6
7+import Prelude ()
8import Prelude.Compat
8import Prelude.Compat
9
9
10import Data.Aeson.TH
10import Data.Aeson.TH
tests/DataFamilies/Instances.hs+1−2
@@ -1,13 +1,12 @@
1{-# LANGUAGE FlexibleInstances #-}
1{-# LANGUAGE FlexibleInstances #-}
2{-# LANGUAGE GADTs #-}
2{-# LANGUAGE GADTs #-}
3−{-# LANGUAGE NoImplicitPrelude #-}
4{-# LANGUAGE TemplateHaskell #-}
3{-# LANGUAGE TemplateHaskell #-}
5{-# LANGUAGE TypeFamilies #-}
4{-# LANGUAGE TypeFamilies #-}
6−
7{-# OPTIONS_GHC -fno-warn-orphans #-}
5{-# OPTIONS_GHC -fno-warn-orphans #-}
8
6
9module DataFamilies.Instances () where
7module DataFamilies.Instances () where
10
8
9+import Prelude ()
11import Prelude.Compat
10import Prelude.Compat
12
11
13import Data.Aeson.TH
12import Data.Aeson.TH
tests/DataFamilies/Properties.hs+5−6
@@ -1,21 +1,20 @@
1{-# LANGUAGE CPP #-}
1{-# LANGUAGE CPP #-}
2−{-# LANGUAGE NoImplicitPrelude #-}
3
2
4module DataFamilies.Properties (tests) where
3module DataFamilies.Properties (tests) where
5
4
5+import Prelude ()
6import Prelude.Compat
6import Prelude.Compat
7
7
8import DataFamilies.Encoders
8import DataFamilies.Encoders
9import DataFamilies.Instances ()
9import DataFamilies.Instances ()
10−import PropUtils
10+import Properties hiding (tests)
11
11
12−
13−import Test.Tasty (TestTree, testGroup)
12+import Test.Framework (Test, testGroup)
14−import Test.Tasty.QuickCheck (testProperty)
13+import Test.Framework.Providers.QuickCheck2 (testProperty)
15
14
16--------------------------------------------------------------------------------
15--------------------------------------------------------------------------------
17
16
18−tests :: TestTree
17+tests :: Test
19tests = testGroup "data families" [
18tests = testGroup "data families" [
20 testGroup "template-haskell" [
19 testGroup "template-haskell" [
21 testGroup "toJSON" [
20 testGroup "toJSON" [
tests/DataFamilies/Types.hs+1−1
@@ -4,13 +4,13 @@
4{-# LANGUAGE FlexibleInstances #-}
4{-# LANGUAGE FlexibleInstances #-}
5{-# LANGUAGE GADTs #-}
5{-# LANGUAGE GADTs #-}
6{-# LANGUAGE GeneralizedNewtypeDeriving #-}
6{-# LANGUAGE GeneralizedNewtypeDeriving #-}
7−{-# LANGUAGE NoImplicitPrelude #-}
8{-# LANGUAGE StandaloneDeriving #-}
7{-# LANGUAGE StandaloneDeriving #-}
9{-# LANGUAGE TemplateHaskell #-}
8{-# LANGUAGE TemplateHaskell #-}
10{-# LANGUAGE TypeFamilies #-}
9{-# LANGUAGE TypeFamilies #-}
11
10
12module DataFamilies.Types (module DataFamilies.Types) where
11module DataFamilies.Types (module DataFamilies.Types) where
13
12
13+import Prelude ()
14import Prelude.Compat
14import Prelude.Compat
15
15
16import Generics.Deriving.TH (deriveAll0)
16import Generics.Deriving.TH (deriveAll0)
tests/Encoders.hs+7−81
@@ -1,12 +1,11 @@
1{-# LANGUAGE CPP #-}
1{-# LANGUAGE CPP #-}
2{-# LANGUAGE GADTs #-}
2{-# LANGUAGE GADTs #-}
3−{-# LANGUAGE NoImplicitPrelude #-}
4{-# LANGUAGE TemplateHaskell #-}
3{-# LANGUAGE TemplateHaskell #-}
5
4
6module Encoders (module Encoders) where
5module Encoders (module Encoders) where
7
6
7+import Prelude ()
8import Prelude.Compat
8import Prelude.Compat
9−import Data.Text (Text)
10
9
11import Data.Aeson.TH
10import Data.Aeson.TH
12import Data.Aeson.Types
11import Data.Aeson.Types
@@ -97,18 +96,6 @@ gNullaryToEncodingObjectWithSingleField = genericToEncoding optsObjectWithSingle
97gNullaryParseJSONObjectWithSingleField :: Value -> Parser Nullary
96gNullaryParseJSONObjectWithSingleField :: Value -> Parser Nullary
98gNullaryParseJSONObjectWithSingleField = genericParseJSON optsObjectWithSingleField
97gNullaryParseJSONObjectWithSingleField = genericParseJSON optsObjectWithSingleField
99
98
100−keyOptions :: JSONKeyOptions
101−keyOptions = defaultJSONKeyOptions { keyModifier = ('k' :) }
102−
103−gNullaryToJSONKey :: Nullary -> Either String Text
104−gNullaryToJSONKey x = case genericToJSONKey keyOptions of
105− ToJSONKeyText p _ -> Right (p x)
106− _ -> Left "Should be a ToJSONKeyText"
107−
108−gNullaryFromJSONKey :: Text -> Parser Nullary
109−gNullaryFromJSONKey t = case genericFromJSONKey keyOptions of
110− FromJSONKeyTextParser p -> p t
111− _ -> fail "Not a TextParser"
112
99
113--------------------------------------------------------------------------------
100--------------------------------------------------------------------------------
114-- SomeType encoders/decoders
101-- SomeType encoders/decoders
@@ -188,6 +175,7 @@ gSomeTypeToEncoding2ElemArray = genericToEncoding opts2ElemArray
188gSomeTypeParseJSON2ElemArray :: Value -> Parser (SomeType Int)
175gSomeTypeParseJSON2ElemArray :: Value -> Parser (SomeType Int)
189gSomeTypeParseJSON2ElemArray = genericParseJSON opts2ElemArray
176gSomeTypeParseJSON2ElemArray = genericParseJSON opts2ElemArray
190
177
178+#if __GLASGOW_HASKELL__ >= 706
191gSomeTypeLiftToEncoding2ElemArray :: LiftToEncoding SomeType a
179gSomeTypeLiftToEncoding2ElemArray :: LiftToEncoding SomeType a
192gSomeTypeLiftToEncoding2ElemArray = genericLiftToEncoding opts2ElemArray
180gSomeTypeLiftToEncoding2ElemArray = genericLiftToEncoding opts2ElemArray
193
181
@@ -196,6 +184,7 @@ gSomeTypeLiftToJSON2ElemArray = genericLiftToJSON opts2ElemArray
196
184
197gSomeTypeLiftParseJSON2ElemArray :: LiftParseJSON SomeType a
185gSomeTypeLiftParseJSON2ElemArray :: LiftParseJSON SomeType a
198gSomeTypeLiftParseJSON2ElemArray = genericLiftParseJSON opts2ElemArray
186gSomeTypeLiftParseJSON2ElemArray = genericLiftParseJSON opts2ElemArray
187+#endif
199
188
200
189
201gSomeTypeToJSONTaggedObject :: SomeType Int -> Value
190gSomeTypeToJSONTaggedObject :: SomeType Int -> Value
@@ -207,6 +196,7 @@ gSomeTypeToEncodingTaggedObject = genericToEncoding optsTaggedObject
207gSomeTypeParseJSONTaggedObject :: Value -> Parser (SomeType Int)
196gSomeTypeParseJSONTaggedObject :: Value -> Parser (SomeType Int)
208gSomeTypeParseJSONTaggedObject = genericParseJSON optsTaggedObject
197gSomeTypeParseJSONTaggedObject = genericParseJSON optsTaggedObject
209
198
199+#if __GLASGOW_HASKELL__ >= 706
210gSomeTypeLiftToEncodingTaggedObject :: LiftToEncoding SomeType a
200gSomeTypeLiftToEncodingTaggedObject :: LiftToEncoding SomeType a
211gSomeTypeLiftToEncodingTaggedObject = genericLiftToEncoding optsTaggedObject
201gSomeTypeLiftToEncodingTaggedObject = genericLiftToEncoding optsTaggedObject
212
202
@@ -215,6 +205,7 @@ gSomeTypeLiftToJSONTaggedObject = genericLiftToJSON optsTaggedObject
215
205
216gSomeTypeLiftParseJSONTaggedObject :: LiftParseJSON SomeType a
206gSomeTypeLiftParseJSONTaggedObject :: LiftParseJSON SomeType a
217gSomeTypeLiftParseJSONTaggedObject = genericLiftParseJSON optsTaggedObject
207gSomeTypeLiftParseJSONTaggedObject = genericLiftParseJSON optsTaggedObject
208+#endif
218
209
219
210
220gSomeTypeToJSONObjectWithSingleField :: SomeType Int -> Value
211gSomeTypeToJSONObjectWithSingleField :: SomeType Int -> Value
@@ -226,6 +217,7 @@ gSomeTypeToEncodingObjectWithSingleField = genericToEncoding optsObjectWithSingl
226gSomeTypeParseJSONObjectWithSingleField :: Value -> Parser (SomeType Int)
217gSomeTypeParseJSONObjectWithSingleField :: Value -> Parser (SomeType Int)
227gSomeTypeParseJSONObjectWithSingleField = genericParseJSON optsObjectWithSingleField
218gSomeTypeParseJSONObjectWithSingleField = genericParseJSON optsObjectWithSingleField
228
219
220+#if __GLASGOW_HASKELL__ >= 706
229gSomeTypeLiftToEncodingObjectWithSingleField :: LiftToEncoding SomeType a
221gSomeTypeLiftToEncodingObjectWithSingleField :: LiftToEncoding SomeType a
230gSomeTypeLiftToEncodingObjectWithSingleField = genericLiftToEncoding optsObjectWithSingleField
222gSomeTypeLiftToEncodingObjectWithSingleField = genericLiftToEncoding optsObjectWithSingleField
231
223
@@ -234,6 +226,7 @@ gSomeTypeLiftToJSONObjectWithSingleField = genericLiftToJSON optsObjectWithSingl
234
226
235gSomeTypeLiftParseJSONObjectWithSingleField :: LiftParseJSON SomeType a
227gSomeTypeLiftParseJSONObjectWithSingleField :: LiftParseJSON SomeType a
236gSomeTypeLiftParseJSONObjectWithSingleField = genericLiftParseJSON optsObjectWithSingleField
228gSomeTypeLiftParseJSONObjectWithSingleField = genericLiftParseJSON optsObjectWithSingleField
229+#endif
237
230
238
231
239gSomeTypeToJSONOmitNothingFields :: SomeType Int -> Value
232gSomeTypeToJSONOmitNothingFields :: SomeType Int -> Value
@@ -242,58 +235,6 @@ gSomeTypeToJSONOmitNothingFields = genericToJSON optsOmitNothingFields
242gSomeTypeToEncodingOmitNothingFields :: SomeType Int -> Encoding
235gSomeTypeToEncodingOmitNothingFields :: SomeType Int -> Encoding
243gSomeTypeToEncodingOmitNothingFields = genericToEncoding optsOmitNothingFields
236gSomeTypeToEncodingOmitNothingFields = genericToEncoding optsOmitNothingFields
244
237
245−
246−thSomeTypeParseJSONRejectUnknownFields :: Value -> Parser (SomeType Int)
247−thSomeTypeParseJSONRejectUnknownFields = $(mkParseJSON optsRejectUnknownFields ''SomeType)
248−
249−gSomeTypeParseJSONRejectUnknownFields :: Value -> Parser (SomeType Int)
250−gSomeTypeParseJSONRejectUnknownFields = genericParseJSON optsRejectUnknownFields
251−
252−
253−--------------------------------------------------------------------------------
254−-- Foo decoders
255−--------------------------------------------------------------------------------
256−
257−thFooParseJSONRejectUnknownFields :: Value -> Parser Foo
258−thFooParseJSONRejectUnknownFields = $(mkParseJSON optsRejectUnknownFields ''Foo)
259−
260−gFooParseJSONRejectUnknownFields :: Value -> Parser Foo
261−gFooParseJSONRejectUnknownFields = genericParseJSON optsRejectUnknownFields
262−
263−
264−thFooParseJSONRejectUnknownFieldsTagged :: Value -> Parser Foo
265−thFooParseJSONRejectUnknownFieldsTagged = $(mkParseJSON optsRejectUnknownFieldsTagged ''Foo)
266−
267−gFooParseJSONRejectUnknownFieldsTagged :: Value -> Parser Foo
268−gFooParseJSONRejectUnknownFieldsTagged = genericParseJSON optsRejectUnknownFieldsTagged
269−
270−
271−--------------------------------------------------------------------------------
272−-- Option fields
273−--------------------------------------------------------------------------------
274−
275−thOptionFieldToJSON :: OptionField -> Value
276−thOptionFieldToJSON = $(mkToJSON optsOptionField 'OptionField)
277−
278−thOptionFieldToEncoding :: OptionField -> Encoding
279−thOptionFieldToEncoding = $(mkToEncoding optsOptionField 'OptionField)
280−
281−thOptionFieldParseJSON :: Value -> Parser OptionField
282−thOptionFieldParseJSON = $(mkParseJSON optsOptionField 'OptionField)
283−
284−gOptionFieldToJSON :: OptionField -> Value
285−gOptionFieldToJSON = genericToJSON optsOptionField
286−
287−gOptionFieldToEncoding :: OptionField -> Encoding
288−gOptionFieldToEncoding = genericToEncoding optsOptionField
289−
290−gOptionFieldParseJSON :: Value -> Parser OptionField
291−gOptionFieldParseJSON = genericParseJSON optsOptionField
292−
293−thMaybeFieldToJSON :: MaybeField -> Value
294−thMaybeFieldToJSON = $(mkToJSON optsOptionField 'MaybeField)
295−
296−
297--------------------------------------------------------------------------------
238--------------------------------------------------------------------------------
298-- IncoherentInstancesNeeded
239-- IncoherentInstancesNeeded
299--------------------------------------------------------------------------------
240--------------------------------------------------------------------------------
@@ -311,11 +252,6 @@ incoherentInstancesNeededParseJSONString :: FromJSON a => Value -> Parser (Incoh
311incoherentInstancesNeededParseJSONString = case () of
252incoherentInstancesNeededParseJSONString = case () of
312 _ | True -> $(mkParseJSON defaultOptions ''IncoherentInstancesNeeded)
253 _ | True -> $(mkParseJSON defaultOptions ''IncoherentInstancesNeeded)
313 | False -> genericParseJSON defaultOptions
254 | False -> genericParseJSON defaultOptions
314−
315−incoherentInstancesNeededToJSON :: ToJSON a => IncoherentInstancesNeeded a -> Value
316−incoherentInstancesNeededToJSON = case () of
317− _ | True -> $(mkToJSON defaultOptions ''IncoherentInstancesNeeded)
318− | False -> genericToJSON defaultOptions
319#endif
255#endif
320
256
321-------------------------------------------------------------------------------
257-------------------------------------------------------------------------------
@@ -446,13 +382,3 @@ gOneConstructorToEncodingTagged = genericToEncoding optsTagSingleConstructors
446
382
447gOneConstructorParseJSONTagged :: Value -> Parser OneConstructor
383gOneConstructorParseJSONTagged :: Value -> Parser OneConstructor
448gOneConstructorParseJSONTagged = genericParseJSON optsTagSingleConstructors
384gOneConstructorParseJSONTagged = genericParseJSON optsTagSingleConstructors
449−
450−--------------------------------------------------------------------------------
451−-- Product2 encoders/decoders
452−--------------------------------------------------------------------------------
453−
454−thProduct2ParseJSON :: Value -> Parser (Product2 Int Bool)
455−thProduct2ParseJSON = $(mkParseJSON defaultOptions ''Product2)
456−
457−gProduct2ParseJSON :: Value -> Parser (Product2 Int Bool)
458−gProduct2ParseJSON = genericParseJSON defaultOptions
tests/ErrorMessages.hs+41−208
@@ -1,4 +1,3 @@
1−{-# LANGUAGE NoImplicitPrelude #-}
2{-# LANGUAGE OverloadedStrings #-}
1{-# LANGUAGE OverloadedStrings #-}
3{-# LANGUAGE ScopedTypeVariables #-}
2{-# LANGUAGE ScopedTypeVariables #-}
4
3
@@ -7,218 +6,52 @@ module ErrorMessages
7 tests
6 tests
8 ) where
7 ) where
9
8
9+import Prelude ()
10import Prelude.Compat
10import Prelude.Compat
11
11
12−import Data.Aeson (FromJSON(..), Value, json)
12+import Data.Aeson (FromJSON(..), eitherDecode)
13−import Data.Aeson.Types (Parser)
14−import Data.Aeson.Parser (eitherDecodeWith)
15−import Data.Aeson.Internal (formatError, iparse)
16−import Data.Algorithm.Diff (PolyDiff (..), getGroupedDiff)
17import Data.Proxy (Proxy(..))
13import Data.Proxy (Proxy(..))
18−import Data.Semigroup ((<>))
19−import Data.Sequence (Seq)
20import Instances ()
14import Instances ()
21−import Numeric.Natural (Natural)
15+import Test.Framework (Test)
22−import Test.Tasty (TestTree, TestName)
16+import Test.Framework.Providers.HUnit (testCase)
23−import Test.Tasty.Golden.Advanced (goldenTest)
17+import Test.HUnit (Assertion, assertFailure, assertEqual)
24import qualified Data.ByteString.Lazy.Char8 as L
18import qualified Data.ByteString.Lazy.Char8 as L
25import qualified Data.HashMap.Strict as HM
19import qualified Data.HashMap.Strict as HM
26
20
27−import Encoders
28−import Types
29−
30−tests :: [TestTree]
21+tests :: [Test]
31tests =
22tests =
32− [ aesonGoldenTest "simple" "tests/golden/simple.expected" output
23+ [
33− , aesonGoldenTest "generic" "tests/golden/generic.expected" (outputGeneric G)
24+ testCase "Int" int
25+ , testCase "String" string
26+ , testCase "HashMap" hashMap
34− ]
27+ ]
35−
28+
36−output :: Output
29+int :: Assertion
37−output = concat
30+int = do
38− [ testFor "Int" (Proxy :: Proxy Int)
31+ let t = test (Proxy :: Proxy Int)
39− [ "\"\""
32+ t "\"\"" $ expected "Int" "String"
40− , "[]"
33+ t "[]" $ expected "Int" "Array"
41− , "{}"
34+ t "{}" $ expected "Int" "Object"
42− , "null"
35+ t "null" $ expected "Int" "Null"
43− ]
36+
44−
37+string :: Assertion
45− , testFor "Integer" (Proxy :: Proxy Integer)
38+string = do
46− [ "44.44"
47− ]
48−
49− , testFor "Natural" (Proxy :: Proxy Natural)
50− [ "44.44"
51− , "-50"
52− ]
53−
54− , testFor "String" (Proxy :: Proxy String)
39+ let t = test (Proxy :: Proxy String)
55− [ "1"
40+ t "1" $ expected "String" "Number"
56− , "[]"
41+ t "[]" $ expected "String" "Array"
57− , "{}"
42+ t "{}" $ expected "String" "Object"
58− , "null"
43+ t "null" $ expected "String" "Null"
59− ]
44+
60−
45+hashMap :: Assertion
46+hashMap = do
61− , testFor "HashMap" (Proxy :: Proxy (HM.HashMap String Int))
47+ let t = test (Proxy :: Proxy (HM.HashMap String Int))
62− [ "\"\""
48+ t "\"\"" $ expected "HashMap k v" "String"
63− , "[]"
49+ t "[]" $ expected "HashMap k v" "Array"
64− ]
50+
65−
51+expected :: String -> String -> String
66− -- issue #356
52+expected ex enc = "Error in $: expected " ++ ex ++ ", encountered " ++ enc
67− , testFor "Either" (Proxy :: Proxy (Int, Either (Int, Bool) ()))
53+
68− [ "[1,{\"Left\":[2,3]}]"
54+test :: forall a proxy . (FromJSON a, Show a) => proxy a -> L.ByteString -> String -> Assertion
69− ]
55+test _ v msg = case eitherDecode v of
70−
56+ Left e -> assertEqual "Invalid error message" msg e
71− -- issue #358
57+ Right (x :: a) -> assertFailure $ "Expected parsing to fail but it suceeded with: " ++ show x
72− , testFor "Seq" (Proxy :: Proxy (Seq Int))
73− [ "[0,1,true]"
74− ]
75− ]
76−
77−data Choice = TH | G
78−
79−outputGeneric :: Choice -> Output
80−outputGeneric choice = concat
81− [ testWith "OneConstructor"
82− (select
83− thOneConstructorParseJSONDefault
84− gOneConstructorParseJSONDefault)
85− [ "\"X\""
86− , "[0]"
87− ]
88−
89− , testWith "Nullary"
90− (select
91− thNullaryParseJSONString
92− gNullaryParseJSONString)
93− [ "\"X\""
94− , "[]"
95− ]
96−
97− , testWithSomeType "SomeType (tagged)"
98− (select
99− thSomeTypeParseJSONTaggedObject
100− gSomeTypeParseJSONTaggedObject)
101− [ "{\"tag\": \"unary\", \"contents\": true}"
102− , "{\"tag\": \"unary\"}"
103− , "{\"tag\": \"record\"}"
104− , "{\"tag\": \"record\", \"testone\": true, \"testtwo\": null, \"testthree\": null}"
105− , "{\"tag\": \"X\"}"
106− , "{}"
107− , "[]"
108− ]
109−
110− , testWithSomeType "SomeType (single-field)"
111− (select
112− thSomeTypeParseJSONObjectWithSingleField
113− gSomeTypeParseJSONObjectWithSingleField)
114− [ "{\"unary\": {}}"
115− , "{\"unary\": []}"
116− , "{\"X\": []}"
117− , "{\"record\": {}, \"W\":{}}"
118− , "{}"
119− , "[]"
120− , "{\"unary\""
121− , "{\"unary\":"
122− , "{\"unary\":1"
123− ]
124−
125− , testWithSomeType "SomeType (two-element array)"
126− (select
127− thSomeTypeParseJSON2ElemArray
128− gSomeTypeParseJSON2ElemArray)
129− [ "[\"unary\", true]"
130− , "[\"record\", null]"
131− , "[\"X\", 0]"
132− , "[null, 0]"
133− , "[]"
134− , "{}"
135− , "[1"
136− , "[1,"
137− ]
138−
139− , testWithSomeType "SomeType (reject unknown fields)"
140− (select
141− thSomeTypeParseJSONRejectUnknownFields
142− gSomeTypeParseJSONRejectUnknownFields)
143− [ "{\"tag\": \"record\", \"testOne\": 1.0, \"testZero\": 1}"
144− , "{\"testZero\": 1}"
145− , "{\"tag\": \"record\", \"testone\": true, \"testtwo\": null, \"testthree\": null}"
146− ]
147−
148− , testWithFoo "Foo (reject unknown fields)"
149− (select
150− thFooParseJSONRejectUnknownFields
151− gFooParseJSONRejectUnknownFields)
152− [ "{\"tag\": \"foo\"}"
153− ]
154−
155− , testWithFoo "Foo (reject unknown fields, tagged single)"
156− (select
157− thFooParseJSONRejectUnknownFieldsTagged
158− gFooParseJSONRejectUnknownFieldsTagged)
159− [ "{\"tag\": \"foo\", \"unknownField\": 0}"
160− ]
161−
162− , testWith "EitherTextInt"
163− (select
164− thEitherTextIntParseJSONUntaggedValue
165− gEitherTextIntParseJSONUntaggedValue)
166− [ "\"X\""
167− , "[]"
168− ]
169−
170− , testWith "Product2 Int Bool"
171− (select
172− thProduct2ParseJSON
173− gProduct2ParseJSON)
174− [ "[1, null]"
175− , "[]"
176− , "{}"
177− ]
178− ]
179− where
180− select a b = case choice of
181− TH -> a
182− G -> b
183−
184−-- Test infrastructure
185−
186−type Output = [String]
187−
188−outputLine :: String -> Output
189−outputLine = pure
190−
191−aesonGoldenTest :: TestName -> FilePath -> Output -> TestTree
192−aesonGoldenTest name ref out = goldenTest name (L.readFile ref) act cmp upd
193− where
194− act = pure (L.pack (unlines out))
195− upd = L.writeFile ref
196− cmp x y | x == y = return Nothing
197− cmp x y = return $ Just $ unlines $
198− concatMap f (getGroupedDiff (L.lines x) (L.lines y))
199− where
200− f (First xs) = map (cons3 '-' . L.unpack) xs
201− f (Second ys) = map (cons3 '+' . L.unpack) ys
202− -- we print unchanged lines too. It shouldn't be a problem while we have
203− -- reasonably small examples
204− f (Both xs _) = map (cons3 ' ' . L.unpack) xs
205− -- we add three characters, so the changed lines are easier to spot
206− cons3 c cs = c : c : c : ' ' : cs
207−
208−testWith :: Show a => String -> (Value -> Parser a) -> [L.ByteString] -> Output
209−testWith name parser ts =
210− outputLine name <>
211− foldMap (\s ->
212− case eitherDecodeWith json (iparse parser) s of
213− Left err -> outputLine $ uncurry formatError err
214− Right a -> outputLine $ show a) ts
215−
216−testFor :: forall a proxy. (FromJSON a, Show a)
217− => String -> proxy a -> [L.ByteString] -> Output
218−testFor name _ = testWith name (parseJSON :: Value -> Parser a)
219−
220−testWithSomeType :: String -> (Value -> Parser (SomeType Int)) -> [L.ByteString] -> Output
221−testWithSomeType = testWith
222−
223−testWithFoo :: String -> (Value -> Parser Foo) -> [L.ByteString] -> Output
224−testWithFoo = testWith
tests/Functions.hs+1−2
@@ -1,11 +1,10 @@
1−{-# LANGUAGE NoImplicitPrelude #-}
2−
3module Functions
1module Functions
4 (
2 (
5 approxEq
3 approxEq
6 , approxEqWith
4 , approxEqWith
7 ) where
5 ) where
8
6
7+import Prelude ()
9import Prelude.Compat
8import Prelude.Compat
10
9
11approxEq :: (Fractional a, Ord a) => a -> a -> Bool
10approxEq :: (Fractional a, Ord a) => a -> a -> Bool
tests/Instances.hs+62−7
@@ -1,13 +1,13 @@
1+{-# LANGUAGE CPP #-}
1{-# LANGUAGE FlexibleContexts #-}
2{-# LANGUAGE FlexibleContexts #-}
2{-# LANGUAGE FlexibleInstances #-}
3{-# LANGUAGE FlexibleInstances #-}
3−{-# LANGUAGE NoImplicitPrelude #-}
4{-# LANGUAGE OverloadedStrings #-}
4{-# LANGUAGE OverloadedStrings #-}
5{-# LANGUAGE RecordWildCards #-}
5{-# LANGUAGE RecordWildCards #-}
6−
7{-# OPTIONS_GHC -fno-warn-orphans #-}
6{-# OPTIONS_GHC -fno-warn-orphans #-}
8
7
9module Instances () where
8module Instances () where
10
9
10+import Prelude ()
11import Prelude.Compat
11import Prelude.Compat
12
12
13import Control.Applicative (empty)
13import Control.Applicative (empty)
@@ -22,14 +22,35 @@ import Types
22import qualified Data.DList as DList
22import qualified Data.DList as DList
23import qualified Data.HashMap.Strict as HM
23import qualified Data.HashMap.Strict as HM
24
24
25+#if !MIN_VERSION_QuickCheck(2,9,0)
26+import Control.Applicative (Const(..))
27+import Data.Functor.Identity (Identity (..))
28+import Data.List.NonEmpty (NonEmpty(..))
29+import Data.Version
30+import Test.QuickCheck (getNonNegative, listOf1, resize)
31+#endif
32+
33+#if !MIN_VERSION_QuickCheck(2,10,0)
34+import Data.Functor.Compose (Compose (..))
35+import Data.Proxy (Proxy(..))
36+#endif
37+
25import Data.Orphans ()
38import Data.Orphans ()
26import Test.QuickCheck.Instances ()
39import Test.QuickCheck.Instances ()
40+#if MIN_VERSION_base(4,7,0)
27import Data.Hashable.Time ()
41import Data.Hashable.Time ()
42+#endif
43+
44+#if !MIN_VERSION_base(4,8,0) && !MIN_VERSION_QuickCheck(2,8,3)
45+import Numeric.Natural
46+#endif
47+
48+{-# ANN module ("HLint: ignore Use fewer imports"::String) #-}
28
49
29-- "System" types.
50-- "System" types.
30
51
31instance Arbitrary DotNetTime where
52instance Arbitrary DotNetTime where
32− arbitrary = DotNetTime `fmap` arbitrary
53+ arbitrary = DotNetTime `liftM` arbitrary
33 shrink = map DotNetTime . shrink . fromDotNetTime
54 shrink = map DotNetTime . shrink . fromDotNetTime
34
55
35-- | Compare timezone part only on 'timeZoneMinutes'
56-- | Compare timezone part only on 'timeZoneMinutes'
@@ -153,15 +174,49 @@ instance Arbitrary EitherTextInt where
153instance Arbitrary (GADT String) where
174instance Arbitrary (GADT String) where
154 arbitrary = GADT <$> arbitrary
175 arbitrary = GADT <$> arbitrary
155
176
156−instance Arbitrary OptionField where
157− arbitrary = OptionField <$> arbitrary
158−
159−
160instance ApproxEq Char where
177instance ApproxEq Char where
161 (=~) = (==)
178 (=~) = (==)
162
179
163instance (ApproxEq a) => ApproxEq [a] where
180instance (ApproxEq a) => ApproxEq [a] where
164 a =~ b = length a == length b && all (uncurry (=~)) (zip a b)
181 a =~ b = length a == length b && all (uncurry (=~)) (zip a b)
165
182
183+-- Version tags are deprecated, so we avoid using them in the Arbitrary
184+-- instance. However, the recommended constructor 'makeVersion' is not
185+-- exported by "Data.Version" until base-4.8.0.0. For previous versions,
186+-- a definition is given below.
187+
188+
189+#if !MIN_VERSION_base(4,8,0) && !MIN_VERSION_QuickCheck(2,8,3)
190+instance Arbitrary Natural where
191+ arbitrary = fromInteger . abs <$> arbitrary
192+#endif
193+
194+#if !MIN_VERSION_QuickCheck(2,10,0)
195+instance Arbitrary (Proxy a) where
196+ arbitrary = pure Proxy
197+
198+instance Arbitrary (f (g a)) => Arbitrary (Compose f g a) where
199+ arbitrary = Compose <$> arbitrary
200+#endif
201+
166instance Arbitrary a => Arbitrary (DList.DList a) where
202instance Arbitrary a => Arbitrary (DList.DList a) where
167 arbitrary = DList.fromList <$> arbitrary
203 arbitrary = DList.fromList <$> arbitrary
204+
205+#if !MIN_VERSION_QuickCheck(2,9,0)
206+instance Arbitrary a => Arbitrary (Const a b) where
207+ arbitrary = Const <$> arbitrary
208+
209+instance Arbitrary a => Arbitrary (NonEmpty a) where
210+ arbitrary = (:|) <$> arbitrary <*> arbitrary
211+
212+instance Arbitrary Version where
213+ arbitrary = makeVersion . fmap getNonNegative <$> resize 4 (listOf1 arbitrary)
214+
215+#if !MIN_VERSION_base(4,8,0)
216+makeVersion :: [Int] -> Version
217+makeVersion b = Version b []
218+#endif
219+
220+instance Arbitrary a => Arbitrary (Identity a) where
221+ arbitrary = Identity <$> arbitrary
222+#endif
tests/JSONTestSuite/parsers/test_haskell-aeson/testaeson.cabal+3−5
@@ -1,8 +1,6 @@
1−-- This file has been generated from package.yaml by hpack version 0.28.2.
1+-- This file has been generated from package.yaml by hpack version 0.14.0.
2--
2--
3-- see: https://github.com/sol/hpack
3-- see: https://github.com/sol/hpack
4−--
5−-- hash: 716ef4c3e64849526f097b85ed05015e0072c2e2afcc1f91e58d8bea68b123b2
6
4
7name: testaeson
5name: testaeson
8version: 0.0.0
6version: 0.0.0
@@ -14,8 +12,8 @@ executable testaeson
14 hs-source-dirs:
12 hs-source-dirs:
15 src
13 src
16 build-depends:
14 build-depends:
17− aeson
18− , base >=4.7 && <5
15+ base >= 4.7 && < 5
16+ , aeson
19 , base-prelude
17 , base-prelude
20 , bytestring
18 , bytestring
21 default-language: Haskell2010
19 default-language: Haskell2010
tests/Options.hs+1−19
@@ -1,7 +1,6 @@
1−{-# LANGUAGE NoImplicitPrelude #-}
2−
3module Options (module Options) where
1module Options (module Options) where
4
2
3+import Prelude ()
5import Prelude.Compat
4import Prelude.Compat
6
5
7import Data.Aeson.Types
6import Data.Aeson.Types
@@ -49,20 +48,3 @@ optsTagSingleConstructors = optsDefault
49 { tagSingleConstructors = True
48 { tagSingleConstructors = True
50 , allNullaryToStringTag = False
49 , allNullaryToStringTag = False
51 }
50 }
52−
53−optsOptionField :: Options
54−optsOptionField = optsDefault
55− { fieldLabelModifier = const "field"
56− , omitNothingFields = True
57− }
58−
59−optsRejectUnknownFields :: Options
60−optsRejectUnknownFields = optsDefault
61− { rejectUnknownFields = True
62− }
63−
64−optsRejectUnknownFieldsTagged :: Options
65−optsRejectUnknownFieldsTagged = optsDefault
66− { rejectUnknownFields = True
67− , tagSingleConstructors = True
68− }
tests/PropUtils.hsdeleted−208
@@ -1,208 +0,0 @@
1−{-# LANGUAGE NoImplicitPrelude #-}
2−{-# LANGUAGE OverloadedStrings #-}
3−{-# LANGUAGE RankNTypes #-}
4−
5−module PropUtils (module PropUtils) where
6−
7−import Prelude.Compat
8−
9−import Data.Aeson (eitherDecode, encode)
10−import Data.Aeson.Encoding (encodingToLazyByteString)
11−import Data.Aeson.Internal (IResult(..), formatError, ifromJSON, iparse)
12−import qualified Data.Aeson.Internal as I
13−import Data.Aeson.Parser (value)
14−import Data.Aeson.Types
15−import Data.HashMap.Strict (HashMap)
16−import Data.Hashable (Hashable)
17−import Data.Int (Int8)
18−import Data.Map (Map)
19−import Data.Time (ZonedTime)
20−import Encoders
21−import Instances ()
22−import Test.QuickCheck (Arbitrary(..), Property, Testable, (===), (.&&.), counterexample)
23−import Types
24−import qualified Data.Attoparsec.Lazy as L
25−import qualified Data.ByteString.Lazy.Char8 as L
26−import qualified Data.HashMap.Strict as H
27−import qualified Data.Map as Map
28−import qualified Data.Text as T
29−import qualified Data.Vector as V
30−
31−
32−encodeDouble :: Double -> Double -> Property
33−encodeDouble num denom
34− | isInfinite d || isNaN d = encode d === "null"
35− | otherwise = (read . L.unpack . encode) d === d
36− where d = num / denom
37−
38−encodeInteger :: Integer -> Property
39−encodeInteger i = encode i === L.pack (show i)
40−
41−toParseJSON :: (Eq a, Show a) =>
42− (Value -> Parser a) -> (a -> Value) -> a -> Property
43−toParseJSON parsejson tojson x =
44− case iparse parsejson . tojson $ x of
45− IError path msg -> failure "parse" (formatError path msg) x
46− ISuccess x' -> x === x'
47−
48−toParseJSON1
49− :: (Eq (f Int), Show (f Int))
50− => (forall a. LiftParseJSON f a)
51− -> (forall a. LiftToJSON f a)
52− -> f Int
53− -> Property
54−toParseJSON1 parsejson1 tojson1 = toParseJSON parsejson tojson
55− where
56− parsejson = parsejson1 parseJSON (listParser parseJSON)
57− tojson = tojson1 toJSON (listValue toJSON)
58−
59−roundTripEnc :: (FromJSON a, ToJSON a, Show a) =>
60− (a -> a -> Property) -> a -> a -> Property
61−roundTripEnc eq _ i =
62− case fmap ifromJSON . L.parse value . encode $ i of
63− L.Done _ (ISuccess v) -> v `eq` i
64− L.Done _ (IError path err) -> failure "fromJSON" (formatError path err) i
65− L.Fail _ _ err -> failure "parse" err i
66−
67−roundTripNoEnc :: (FromJSON a, ToJSON a, Show a) =>
68− (a -> a -> Property) -> a -> a -> Property
69−roundTripNoEnc eq _ i =
70− case ifromJSON . toJSON $ i of
71− (ISuccess v) -> v `eq` i
72− (IError path err) -> failure "fromJSON" (formatError path err) i
73−
74−roundTripEq :: (Eq a, FromJSON a, ToJSON a, Show a) => a -> a -> Property
75−roundTripEq x y = roundTripEnc (===) x y .&&. roundTripNoEnc (===) x y
76−
77−-- We test keys by encoding HashMap and Map with it
78−roundTripKey
79− :: (Ord a, Hashable a, FromJSONKey a, ToJSONKey a, Show a)
80− => a -> HashMap a Int -> Map a Int -> Property
81−roundTripKey _ h m = roundTripEq h h .&&. roundTripEq m m
82−
83−infix 4 ==~
84−(==~) :: (ApproxEq a, Show a) => a -> a -> Property
85−x ==~ y =
86− counterexample (show x ++ " /= " ++ show y) (x =~ y)
87−
88−toFromJSON :: (Arbitrary a, Eq a, FromJSON a, ToJSON a, Show a) => a -> Property
89−toFromJSON x = case ifromJSON (toJSON x) of
90− IError path err -> failure "fromJSON" (formatError path err) x
91− ISuccess x' -> x === x'
92−
93−modifyFailureProp :: String -> String -> Bool
94−modifyFailureProp orig added =
95− result == Error (added ++ orig)
96− where
97− parser = const $ modifyFailure (added ++) $ fail orig
98− result :: Result ()
99− result = parse parser ()
100−
101−parserThrowErrorProp :: String -> Property
102−parserThrowErrorProp msg =
103− result === Error msg
104− where
105− parser = const $ parserThrowError [] msg
106− result :: Result ()
107− result = parse parser ()
108−
109−-- | Tests (also) that we catch the JSONPath and it has elements in the right order.
110−parserCatchErrorProp :: [String] -> String -> Property
111−parserCatchErrorProp path msg =
112− result === Success ([I.Key "outer", I.Key "inner"] ++ jsonPath, msg)
113− where
114− parser = parserCatchError outer (curry pure)
115−
116− outer = inner I.<?> I.Key "outer"
117− inner = parserThrowError jsonPath msg I.<?> I.Key "inner"
118−
119− result :: Result (I.JSONPath, String)
120− result = parse (const parser) ()
121−
122− jsonPath = map (I.Key . T.pack) path
123−
124−-- | Perform a structural comparison of the results of two encoding
125−-- methods. Compares decoded values to account for HashMap-driven
126−-- variation in JSON object key ordering.
127−sameAs :: (a -> Value) -> (a -> Encoding) -> a -> Property
128−sameAs toVal toEnc v =
129− counterexample (show s) $
130− eitherDecode s === Right (toVal v)
131− where
132− s = encodingToLazyByteString (toEnc v)
133−
134−sameAs1
135− :: (forall a. LiftToJSON f a)
136− -> (forall a. LiftToEncoding f a)
137− -> f Int
138− -> Property
139−sameAs1 toVal1 toEnc1 v = lhs === rhs
140− where
141− rhs = Right $ toVal1 toJSON (listValue toJSON) v
142− lhs = eitherDecode . encodingToLazyByteString $
143− toEnc1 toEncoding (listEncoding toEncoding) v
144−
145−sameAs1Agree
146− :: ToJSON a
147− => (f a -> Encoding)
148− -> (forall b. LiftToEncoding f b)
149− -> f a
150− -> Property
151−sameAs1Agree toEnc toEnc1 v = rhs === lhs
152− where
153− rhs = encodingToLazyByteString $ toEnc v
154− lhs = encodingToLazyByteString $ toEnc1 toEncoding (listEncoding toEncoding) v
155−
156−type P6 = Product6 Int Bool String (Approx Double) (Int, Approx Double) ()
157−type S4 = Sum4 Int8 ZonedTime T.Text (Map.Map String Int)
158−
159−--------------------------------------------------------------------------------
160−-- Value properties
161−--------------------------------------------------------------------------------
162−
163−-- | Add the formatted @Value@ to the printed counterexample when the property
164−-- fails.
165−checkValue :: Testable a => (Value -> a) -> Value -> Property
166−checkValue prop v = counterexample (L.unpack (encode v)) (prop v)
167−
168−isString :: Value -> Bool
169−isString (String _) = True
170−isString _ = False
171−
172−is2ElemArray :: Value -> Bool
173−is2ElemArray (Array v) = V.length v == 2 && isString (V.head v)
174−is2ElemArray _ = False
175−
176−isTaggedObjectValue :: Value -> Bool
177−isTaggedObjectValue (Object obj) = "tag" `H.member` obj &&
178− "contents" `H.member` obj
179−isTaggedObjectValue _ = False
180−
181−isNullaryTaggedObject :: Value -> Bool
182−isNullaryTaggedObject obj = isTaggedObject' obj && isObjectWithSingleField obj
183−
184−isTaggedObject :: Value -> Property
185−isTaggedObject = checkValue isTaggedObject'
186−
187−isTaggedObject' :: Value -> Bool
188−isTaggedObject' (Object obj) = "tag" `H.member` obj
189−isTaggedObject' _ = False
190−
191−isObjectWithSingleField :: Value -> Bool
192−isObjectWithSingleField (Object obj) = H.size obj == 1
193−isObjectWithSingleField _ = False
194−
195−-- | is untaggedValue of EitherTextInt
196−isUntaggedValueETI :: Value -> Bool
197−isUntaggedValueETI (String s)
198− | s == "nonenullary" = True
199−isUntaggedValueETI (Bool _) = True
200−isUntaggedValueETI (Number _) = True
201−isUntaggedValueETI (Array a) = length a == 2
202−isUntaggedValueETI _ = False
203−
204−isEmptyArray :: Value -> Property
205−isEmptyArray = checkValue isEmptyArray'
206−
207−isEmptyArray' :: Value -> Bool
208−isEmptyArray' = (Array mempty ==)
tests/Properties.hs+511−14
@@ -1,28 +1,326 @@
1−{-# LANGUAGE NoImplicitPrelude #-}
1+{-# LANGUAGE CPP #-}
2+{-# LANGUAGE FlexibleContexts #-} -- For arbitrary Compose
3+{-# LANGUAGE OverloadedStrings #-}
4+{-# LANGUAGE RankNTypes #-}
5+{-# LANGUAGE ScopedTypeVariables #-}
2
6
3module Properties (module Properties) where
7module Properties (module Properties) where
4
8
9+import Prelude ()
5import Prelude.Compat
10import Prelude.Compat
6
11
12+import Control.Applicative (Const)
13+import Data.Aeson (eitherDecode, encode)
14+import Data.Aeson.Encoding (encodingToLazyByteString)
15+import Data.Aeson.Internal (IResult(..), formatError, ifromJSON, iparse)
16+import qualified Data.Aeson.Internal as I
17+import Data.Aeson.Parser (value)
18+import Data.Aeson.Types
19+import Data.DList (DList)
20+import Data.Functor.Compose (Compose (..))
21+import Data.HashMap.Strict (HashMap)
22+import Data.Hashable (Hashable)
23+import Data.Int (Int8)
24+import Data.List.NonEmpty (NonEmpty)
25+import Data.Map (Map)
26+import Data.Proxy (Proxy)
27+import Data.Ratio (Ratio)
28+import Data.Sequence (Seq)
29+import Data.Tagged (Tagged)
30+import Data.Time (Day, LocalTime, NominalDiffTime, TimeOfDay, UTCTime, ZonedTime)
31+import Data.Version (Version)
32+import Encoders
7import Instances ()
33import Instances ()
34+import Numeric.Natural (Natural)
8−import Test.Tasty (TestTree, testGroup)
35+import Test.Framework (Test, testGroup)
9−import Test.Tasty.QuickCheck (testProperty)
36+import Test.Framework.Providers.QuickCheck2 (testProperty)
10−import Test.QuickCheck (Property)
37+import Test.QuickCheck (Arbitrary(..), Property, Testable, (===), (.&&.), counterexample)
11−import PropUtils
38+import Types
12−import PropertyGeneric
39+import qualified Data.Attoparsec.Lazy as L
13−import PropertyKeys
40+import qualified Data.ByteString.Lazy.Char8 as L
14−import PropertyRoundTrip
41+import qualified Data.HashMap.Strict as H
15−import PropertyTH
42+import qualified Data.Map as Map
43+import qualified Data.Text as T
44+import qualified Data.Text.Lazy as LT
45+import qualified Data.UUID.Types as UUID
46+import qualified Data.Vector as V
16
47
48+encodeDouble :: Double -> Double -> Property
49+encodeDouble num denom
50+ | isInfinite d || isNaN d = encode d === "null"
51+ | otherwise = (read . L.unpack . encode) d === d
52+ where d = num / denom
17
53
54+encodeInteger :: Integer -> Property
55+encodeInteger i = encode i === L.pack (show i)
56+
57+toParseJSON :: (Eq a, Show a) =>
58+ (Value -> Parser a) -> (a -> Value) -> a -> Property
59+toParseJSON parsejson tojson x =
60+ case iparse parsejson . tojson $ x of
61+ IError path msg -> failure "parse" (formatError path msg) x
62+ ISuccess x' -> x === x'
63+
64+toParseJSON1
65+ :: (Eq (f Int), Show (f Int))
66+ => (forall a. LiftParseJSON f a)
67+ -> (forall a. LiftToJSON f a)
68+ -> f Int
69+ -> Property
70+toParseJSON1 parsejson1 tojson1 = toParseJSON parsejson tojson
71+ where
72+ parsejson = parsejson1 parseJSON (listParser parseJSON)
73+ tojson = tojson1 toJSON (listValue toJSON)
74+
75+roundTripEnc :: (FromJSON a, ToJSON a, Show a) =>
76+ (a -> a -> Property) -> a -> a -> Property
77+roundTripEnc eq _ i =
78+ case fmap ifromJSON . L.parse value . encode $ i of
79+ L.Done _ (ISuccess v) -> v `eq` i
80+ L.Done _ (IError path err) -> failure "fromJSON" (formatError path err) i
81+ L.Fail _ _ err -> failure "parse" err i
82+
83+roundTripNoEnc :: (FromJSON a, ToJSON a, Show a) =>
84+ (a -> a -> Property) -> a -> a -> Property
85+roundTripNoEnc eq _ i =
86+ case ifromJSON . toJSON $ i of
87+ (ISuccess v) -> v `eq` i
88+ (IError path err) -> failure "fromJSON" (formatError path err) i
89+
90+roundTripEq :: (Eq a, FromJSON a, ToJSON a, Show a) => a -> a -> Property
91+roundTripEq x y = roundTripEnc (===) x y .&&. roundTripNoEnc (===) x y
92+
93+-- We test keys by encoding HashMap and Map with it
94+roundTripKey
95+ :: (Ord a, Hashable a, FromJSONKey a, ToJSONKey a, Show a)
96+ => a -> HashMap a Int -> Map a Int -> Property
97+roundTripKey _ h m = roundTripEq h h .&&. roundTripEq m m
98+
99+infix 4 ==~
100+(==~) :: (ApproxEq a, Show a) => a -> a -> Property
101+x ==~ y =
102+ counterexample (show x ++ " /= " ++ show y) (x =~ y)
103+
104+toFromJSON :: (Arbitrary a, Eq a, FromJSON a, ToJSON a, Show a) => a -> Property
105+toFromJSON x = case ifromJSON (toJSON x) of
106+ IError path err -> failure "fromJSON" (formatError path err) x
107+ ISuccess x' -> x === x'
108+
109+modifyFailureProp :: String -> String -> Bool
110+modifyFailureProp orig added =
111+ result == Error (added ++ orig)
112+ where
113+ parser = const $ modifyFailure (added ++) $ fail orig
114+ result :: Result ()
115+ result = parse parser ()
116+
117+parserThrowErrorProp :: String -> Property
118+parserThrowErrorProp msg =
119+ result === Error msg
120+ where
121+ parser = const $ parserThrowError [] msg
122+ result :: Result ()
123+ result = parse parser ()
124+
125+-- | Tests (also) that we catch the JSONPath and it has elements in the right order.
126+parserCatchErrorProp :: [String] -> String -> Property
127+parserCatchErrorProp path msg =
128+ result === Success ([I.Key "outer", I.Key "inner"] ++ jsonPath, msg)
129+ where
130+ parser = parserCatchError outer (curry pure)
131+
132+ outer = inner I.<?> I.Key "outer"
133+ inner = parserThrowError jsonPath msg I.<?> I.Key "inner"
134+
135+ result :: Result (I.JSONPath, String)
136+ result = parse (const parser) ()
137+
138+ jsonPath = map (I.Key . T.pack) path
139+
140+-- | Perform a structural comparison of the results of two encoding
141+-- methods. Compares decoded values to account for HashMap-driven
142+-- variation in JSON object key ordering.
143+sameAs :: (a -> Value) -> (a -> Encoding) -> a -> Property
144+sameAs toVal toEnc v =
145+ eitherDecode (encodingToLazyByteString (toEnc v)) === Right (toVal v)
146+
147+sameAs1
148+ :: (forall a. LiftToJSON f a)
149+ -> (forall a. LiftToEncoding f a)
150+ -> f Int
151+ -> Property
152+sameAs1 toVal1 toEnc1 v = lhs === rhs
153+ where
154+ rhs = Right $ toVal1 toJSON (listValue toJSON) v
155+ lhs = eitherDecode . encodingToLazyByteString $
156+ toEnc1 toEncoding (listEncoding toEncoding) v
157+
158+sameAs1Agree
159+ :: ToJSON a
160+ => (f a -> Encoding)
161+ -> (forall b. LiftToEncoding f b)
162+ -> f a
163+ -> Property
164+sameAs1Agree toEnc toEnc1 v = rhs === lhs
165+ where
166+ rhs = encodingToLazyByteString $ toEnc v
167+ lhs = encodingToLazyByteString $ toEnc1 toEncoding (listEncoding toEncoding) v
168+
169+type P6 = Product6 Int Bool String (Approx Double) (Int, Approx Double) ()
170+type S4 = Sum4 Int8 ZonedTime T.Text (Map.Map String Int)
171+
172+--------------------------------------------------------------------------------
173+-- Value properties
174+--------------------------------------------------------------------------------
175+
176+-- | Add the formatted @Value@ to the printed counterexample when the property
177+-- fails.
178+checkValue :: Testable a => (Value -> a) -> Value -> Property
179+checkValue prop v = counterexample (L.unpack (encode v)) (prop v)
180+
181+isString :: Value -> Bool
182+isString (String _) = True
183+isString _ = False
184+
185+is2ElemArray :: Value -> Bool
186+is2ElemArray (Array v) = V.length v == 2 && isString (V.head v)
187+is2ElemArray _ = False
188+
189+isTaggedObjectValue :: Value -> Bool
190+isTaggedObjectValue (Object obj) = "tag" `H.member` obj &&
191+ "contents" `H.member` obj
192+isTaggedObjectValue _ = False
193+
194+isNullaryTaggedObject :: Value -> Bool
195+isNullaryTaggedObject obj = isTaggedObject' obj && isObjectWithSingleField obj
196+
197+isTaggedObject :: Value -> Property
198+isTaggedObject = checkValue isTaggedObject'
199+
200+isTaggedObject' :: Value -> Bool
201+isTaggedObject' (Object obj) = "tag" `H.member` obj
202+isTaggedObject' _ = False
203+
204+isObjectWithSingleField :: Value -> Bool
205+isObjectWithSingleField (Object obj) = H.size obj == 1
206+isObjectWithSingleField _ = False
207+
208+-- | is untaggedValue of EitherTextInt
209+isUntaggedValueETI :: Value -> Bool
210+isUntaggedValueETI (String s)
211+ | s == "nonenullary" = True
212+isUntaggedValueETI (Bool _) = True
213+isUntaggedValueETI (Number _) = True
214+isUntaggedValueETI (Array a) = length a == 2
215+isUntaggedValueETI _ = False
216+
217+isEmptyArray :: Value -> Property
218+isEmptyArray = checkValue isEmptyArray'
219+
220+isEmptyArray' :: Value -> Bool
221+isEmptyArray' = (Array mempty ==)
222+
223+
224+--------------------------------------------------------------------------------
225+
226+
18−tests :: TestTree
227+tests :: Test
19tests = testGroup "properties" [
228tests = testGroup "properties" [
20 testGroup "encode" [
229 testGroup "encode" [
21 testProperty "encodeDouble" encodeDouble
230 testProperty "encodeDouble" encodeDouble
22 , testProperty "encodeInteger" encodeInteger
231 , testProperty "encodeInteger" encodeInteger
23 ]
232 ]
24− , roundTripTests
233+ , testGroup "roundTrip" [
25− , keysTests
234+ testProperty "Bool" $ roundTripEq True
235+ , testProperty "Double" $ roundTripEq (1 :: Approx Double)
236+ , testProperty "Int" $ roundTripEq (1 :: Int)
237+ , testProperty "NonEmpty Char" $ roundTripEq (undefined :: NonEmpty Char)
238+ , testProperty "Integer" $ roundTripEq (1 :: Integer)
239+ , testProperty "String" $ roundTripEq ("" :: String)
240+ , testProperty "Text" $ roundTripEq T.empty
241+ , testProperty "Lazy Text" $ roundTripEq LT.empty
242+ , testProperty "Foo" $ roundTripEq (undefined :: Foo)
243+ , testProperty "Day" $ roundTripEq (undefined :: Day)
244+ , testProperty "BCE Day" $ roundTripEq (undefined :: BCEDay)
245+ , testProperty "DotNetTime" $ roundTripEq (undefined :: Approx DotNetTime)
246+ , testProperty "LocalTime" $ roundTripEq (undefined :: LocalTime)
247+ , testProperty "TimeOfDay" $ roundTripEq (undefined :: TimeOfDay)
248+ , testProperty "UTCTime" $ roundTripEq (undefined :: UTCTime)
249+ , testProperty "ZonedTime" $ roundTripEq (undefined :: ZonedTime)
250+ , testProperty "NominalDiffTime" $ roundTripEq (undefined :: NominalDiffTime)
251+ , testProperty "Version" $ roundTripEq (undefined :: Version)
252+ , testProperty "Natural" $ roundTripEq (undefined :: Natural)
253+ , testProperty "Proxy" $ roundTripEq (undefined :: Proxy Int)
254+ , testProperty "Tagged" $ roundTripEq (undefined :: Tagged Int Char)
255+ , testProperty "Const" $ roundTripEq (undefined :: Const Int Char)
256+ , testProperty "DList" $ roundTripEq (undefined :: DList Int)
257+ , testProperty "Seq" $ roundTripEq (undefined :: Seq Int)
258+ , testProperty "Rational" $ roundTripEq (undefined :: Rational)
259+ , testProperty "Ratio Int" $ roundTripEq (undefined :: Ratio Int)
260+ , testProperty "UUID" $ roundTripEq UUID.nil
261+ , testGroup "functors"
262+ [ testProperty "Identity Char" $ roundTripEq (undefined :: I Int)
263+
264+ , testProperty "Identity Char" $ roundTripEq (undefined :: I Char)
265+ , testProperty "Identity [Char]" $ roundTripEq (undefined :: I String)
266+ , testProperty "[Identity Char]" $ roundTripEq (undefined :: [I Char])
267+
268+ , testProperty "Compose I I Int" $ roundTripEq (undefined :: LogScaled (Compose I I Int))
269+ , testProperty "Compose [] I Int" $ roundTripEq (undefined :: LogScaled (Compose [] I Int))
270+ , testProperty "Compose I [] Int" $ roundTripEq (undefined :: LogScaled (Compose I [] Int))
271+ , testProperty "Compose [] [] Int" $ roundTripEq (undefined :: LogScaled (Compose [] [] Int))
272+
273+ , testProperty "Compose I I Char" $ roundTripEq (undefined :: LogScaled (Compose I I Char))
274+ , testProperty "Compose [] I Char" $ roundTripEq (undefined :: LogScaled (Compose [] I Char))
275+ , testProperty "Compose I [] Char" $ roundTripEq (undefined :: LogScaled (Compose I [] Char))
276+ , testProperty "Compose [] [] Char" $ roundTripEq (undefined :: LogScaled (Compose [] [] Char))
277+
278+ , testProperty "Compose3 I I I Char" $ roundTripEq (undefined :: LogScaled (Compose3 I I I Char))
279+ , testProperty "Compose3 I [] I Char" $ roundTripEq (undefined :: LogScaled (Compose3 I [] I Char))
280+ , testProperty "Compose3 I I [] Char" $ roundTripEq (undefined :: LogScaled (Compose3 I I [] Char))
281+ , testProperty "Compose3 I [] [] Char" $ roundTripEq (undefined :: LogScaled (Compose3 I [] [] Char))
282+ , testProperty "Compose3 [] I I Char" $ roundTripEq (undefined :: LogScaled (Compose3 [] I I Char))
283+ , testProperty "Compose3 [] [] I Char" $ roundTripEq (undefined :: LogScaled (Compose3 [] [] I Char))
284+ , testProperty "Compose3 [] I [] Char" $ roundTripEq (undefined :: LogScaled (Compose3 [] I [] Char))
285+ , testProperty "Compose3 [] [] [] Char" $ roundTripEq (undefined :: LogScaled (Compose3 [] [] [] Char))
286+
287+ , testProperty "Compose3' I I I Char" $ roundTripEq (undefined :: LogScaled (Compose3' I I I Char))
288+ , testProperty "Compose3' I [] I Char" $ roundTripEq (undefined :: LogScaled (Compose3' I [] I Char))
289+ , testProperty "Compose3' I I [] Char" $ roundTripEq (undefined :: LogScaled (Compose3' I I [] Char))
290+ , testProperty "Compose3' I [] [] Char" $ roundTripEq (undefined :: LogScaled (Compose3' I [] [] Char))
291+ , testProperty "Compose3' [] I I Char" $ roundTripEq (undefined :: LogScaled (Compose3' [] I I Char))
292+ , testProperty "Compose3' [] [] I Char" $ roundTripEq (undefined :: LogScaled (Compose3' [] [] I Char))
293+ , testProperty "Compose3' [] I [] Char" $ roundTripEq (undefined :: LogScaled (Compose3' [] I [] Char))
294+ , testProperty "Compose3' [] [] [] Char" $ roundTripEq (undefined :: LogScaled (Compose3' [] [] [] Char))
295+ ]
296+ , testGroup "ghcGenerics" [
297+ testProperty "OneConstructor" $ roundTripEq OneConstructor
298+ , testProperty "Product2" $ roundTripEq (undefined :: Product2 Int Bool)
299+ , testProperty "Product6" $ roundTripEq (undefined :: P6)
300+ , testProperty "Sum4" $ roundTripEq (undefined :: S4)
301+ ]
302+ ]
303+ , testGroup "roundTrip Key"
304+ [ testProperty "Bool" $ roundTripKey True
305+ , testProperty "Text" $ roundTripKey (undefined :: T.Text)
306+ , testProperty "String" $ roundTripKey (undefined :: String)
307+ , testProperty "Int" $ roundTripKey (undefined :: Int)
308+ , testProperty "[Text]" $ roundTripKey (undefined :: LogScaled [T.Text])
309+ , testProperty "(Int,Char)" $ roundTripKey (undefined :: (Int,Char))
310+ , testProperty "Integer" $ roundTripKey (undefined :: Integer)
311+ , testProperty "Natural" $ roundTripKey (undefined :: Natural)
312+ , testProperty "Float" $ roundTripKey (undefined :: Float)
313+ , testProperty "Double" $ roundTripKey (undefined :: Double)
314+#if MIN_VERSION_base(4,7,0)
315+ , testProperty "Day" $ roundTripKey (undefined :: Day)
316+ , testProperty "LocalTime" $ roundTripKey (undefined :: LocalTime)
317+ , testProperty "TimeOfDay" $ roundTripKey (undefined :: TimeOfDay)
318+ , testProperty "UTCTime" $ roundTripKey (undefined :: UTCTime)
319+#endif
320+ , testProperty "Version" $ roundTripKey (undefined :: Version)
321+ , testProperty "Lazy Text" $ roundTripKey (undefined :: LT.Text)
322+ , testProperty "UUID" $ roundTripKey UUID.nil
323+ ]
26 , testGroup "toFromJSON" [
324 , testGroup "toFromJSON" [
27 testProperty "Integer" (toFromJSON :: Integer -> Property)
325 testProperty "Integer" (toFromJSON :: Integer -> Property)
28 , testProperty "Double" (toFromJSON :: Double -> Property)
326 , testProperty "Double" (toFromJSON :: Double -> Property)
@@ -35,6 +333,205 @@ tests = testGroup "properties" [
35 , testProperty "parserThrowError" parserThrowErrorProp
333 , testProperty "parserThrowError" parserThrowErrorProp
36 , testProperty "parserCatchError" parserCatchErrorProp
334 , testProperty "parserCatchError" parserCatchErrorProp
37 ]
335 ]
38− , genericTests
336+ , testGroup "generic" [
39− , templateHaskellTests
337+ testGroup "toJSON" [
338+ testGroup "Nullary" [
339+ testProperty "string" (isString . gNullaryToJSONString)
340+ , testProperty "2ElemArray" (is2ElemArray . gNullaryToJSON2ElemArray)
341+ , testProperty "TaggedObject" (isNullaryTaggedObject . gNullaryToJSONTaggedObject)
342+ , testProperty "ObjectWithSingleField" (isObjectWithSingleField . gNullaryToJSONObjectWithSingleField)
343+ , testGroup "roundTrip" [
344+ testProperty "string" (toParseJSON gNullaryParseJSONString gNullaryToJSONString)
345+ , testProperty "2ElemArray" (toParseJSON gNullaryParseJSON2ElemArray gNullaryToJSON2ElemArray)
346+ , testProperty "TaggedObject" (toParseJSON gNullaryParseJSONTaggedObject gNullaryToJSONTaggedObject)
347+ , testProperty "ObjectWithSingleField" (toParseJSON gNullaryParseJSONObjectWithSingleField gNullaryToJSONObjectWithSingleField)
348+ ]
349+ ]
350+ , testGroup "EitherTextInt" [
351+ testProperty "UntaggedValue" (isUntaggedValueETI . gEitherTextIntToJSONUntaggedValue)
352+ , testProperty "roundtrip" (toParseJSON gEitherTextIntParseJSONUntaggedValue gEitherTextIntToJSONUntaggedValue)
353+ ]
354+ , testGroup "SomeType" [
355+ testProperty "2ElemArray" (is2ElemArray . gSomeTypeToJSON2ElemArray)
356+ , testProperty "TaggedObject" (isTaggedObject . gSomeTypeToJSONTaggedObject)
357+ , testProperty "ObjectWithSingleField" (isObjectWithSingleField . gSomeTypeToJSONObjectWithSingleField)
358+ , testGroup "roundTrip" [
359+ testProperty "2ElemArray" (toParseJSON gSomeTypeParseJSON2ElemArray gSomeTypeToJSON2ElemArray)
360+ , testProperty "TaggedObject" (toParseJSON gSomeTypeParseJSONTaggedObject gSomeTypeToJSONTaggedObject)
361+ , testProperty "ObjectWithSingleField" (toParseJSON gSomeTypeParseJSONObjectWithSingleField gSomeTypeToJSONObjectWithSingleField)
362+
363+#if __GLASGOW_HASKELL__ >= 706
364+ , testProperty "2ElemArray unary" (toParseJSON1 gSomeTypeLiftParseJSON2ElemArray gSomeTypeLiftToJSON2ElemArray)
365+ , testProperty "TaggedObject unary" (toParseJSON1 gSomeTypeLiftParseJSONTaggedObject gSomeTypeLiftToJSONTaggedObject)
366+ , testProperty "ObjectWithSingleField unary" (toParseJSON1 gSomeTypeLiftParseJSONObjectWithSingleField gSomeTypeLiftToJSONObjectWithSingleField)
367+#endif
368+ ]
369+ ]
370+ , testGroup "OneConstructor" [
371+ testProperty "default" (isEmptyArray . gOneConstructorToJSONDefault)
372+ , testProperty "Tagged" (isTaggedObject . gOneConstructorToJSONTagged)
373+ , testGroup "roundTrip" [
374+ testProperty "default" (toParseJSON gOneConstructorParseJSONDefault gOneConstructorToJSONDefault)
375+ , testProperty "Tagged" (toParseJSON gOneConstructorParseJSONTagged gOneConstructorToJSONTagged)
376+ ]
377+ ]
378+ ]
379+ , testGroup "toEncoding" [
380+ testProperty "NullaryString" $
381+ gNullaryToJSONString `sameAs` gNullaryToEncodingString
382+ , testProperty "Nullary2ElemArray" $
383+ gNullaryToJSON2ElemArray `sameAs` gNullaryToEncoding2ElemArray
384+ , testProperty "NullaryTaggedObject" $
385+ gNullaryToJSONTaggedObject `sameAs` gNullaryToEncodingTaggedObject
386+ , testProperty "NullaryObjectWithSingleField" $
387+ gNullaryToJSONObjectWithSingleField `sameAs`
388+ gNullaryToEncodingObjectWithSingleField
389+ -- , testProperty "ApproxUnwrap" $
390+ -- gApproxToJSONUnwrap `sameAs` gApproxToEncodingUnwrap
391+ , testProperty "ApproxDefault" $
392+ gApproxToJSONDefault `sameAs` gApproxToEncodingDefault
393+
394+ , testProperty "EitherTextInt UntaggedValue" $
395+ gEitherTextIntToJSONUntaggedValue `sameAs` gEitherTextIntToEncodingUntaggedValue
396+
397+ , testProperty "SomeType2ElemArray" $
398+ gSomeTypeToJSON2ElemArray `sameAs` gSomeTypeToEncoding2ElemArray
399+#if __GLASGOW_HASKELL__ >= 706
400+ , testProperty "SomeType2ElemArray unary" $
401+ gSomeTypeLiftToJSON2ElemArray `sameAs1` gSomeTypeLiftToEncoding2ElemArray
402+ , testProperty "SomeType2ElemArray unary agree" $
403+ gSomeTypeToEncoding2ElemArray `sameAs1Agree` gSomeTypeLiftToEncoding2ElemArray
404+#endif
405+
406+ , testProperty "SomeTypeTaggedObject" $
407+ gSomeTypeToJSONTaggedObject `sameAs` gSomeTypeToEncodingTaggedObject
408+#if __GLASGOW_HASKELL__ >= 706
409+ , testProperty "SomeTypeTaggedObject unary" $
410+ gSomeTypeLiftToJSONTaggedObject `sameAs1` gSomeTypeLiftToEncodingTaggedObject
411+ , testProperty "SomeTypeTaggedObject unary agree" $
412+ gSomeTypeToEncodingTaggedObject `sameAs1Agree` gSomeTypeLiftToEncodingTaggedObject
413+#endif
414+
415+ , testProperty "SomeTypeObjectWithSingleField" $
416+ gSomeTypeToJSONObjectWithSingleField `sameAs` gSomeTypeToEncodingObjectWithSingleField
417+#if __GLASGOW_HASKELL__ >= 706
418+ , testProperty "SomeTypeObjectWithSingleField unary" $
419+ gSomeTypeLiftToJSONObjectWithSingleField `sameAs1` gSomeTypeLiftToEncodingObjectWithSingleField
420+ , testProperty "SomeTypeObjectWithSingleField unary agree" $
421+ gSomeTypeToEncodingObjectWithSingleField `sameAs1Agree` gSomeTypeLiftToEncodingObjectWithSingleField
422+#endif
423+
424+ , testProperty "SomeTypeOmitNothingFields" $
425+ gSomeTypeToJSONOmitNothingFields `sameAs` gSomeTypeToEncodingOmitNothingFields
426+
427+ , testProperty "OneConstructorDefault" $
428+ gOneConstructorToJSONDefault `sameAs` gOneConstructorToEncodingDefault
429+ , testProperty "OneConstructorTagged" $
430+ gOneConstructorToJSONTagged `sameAs` gOneConstructorToEncodingTagged
431+ ]
432+ ]
433+ , testGroup "template-haskell" [
434+ testGroup "toJSON" [
435+ testGroup "Nullary" [
436+ testProperty "string" (isString . thNullaryToJSONString)
437+ , testProperty "2ElemArray" (is2ElemArray . thNullaryToJSON2ElemArray)
438+ , testProperty "TaggedObject" (isNullaryTaggedObject . thNullaryToJSONTaggedObject)
439+ , testProperty "ObjectWithSingleField" (isObjectWithSingleField . thNullaryToJSONObjectWithSingleField)
440+
441+ , testGroup "roundTrip" [
442+ testProperty "string" (toParseJSON thNullaryParseJSONString thNullaryToJSONString)
443+ , testProperty "2ElemArray" (toParseJSON thNullaryParseJSON2ElemArray thNullaryToJSON2ElemArray)
444+ , testProperty "TaggedObject" (toParseJSON thNullaryParseJSONTaggedObject thNullaryToJSONTaggedObject)
445+ , testProperty "ObjectWithSingleField" (toParseJSON thNullaryParseJSONObjectWithSingleField thNullaryToJSONObjectWithSingleField)
446+ ]
447+ ]
448+ , testGroup "EitherTextInt" [
449+ testProperty "UntaggedValue" (isUntaggedValueETI . thEitherTextIntToJSONUntaggedValue)
450+ , testProperty "roundtrip" (toParseJSON thEitherTextIntParseJSONUntaggedValue thEitherTextIntToJSONUntaggedValue)
451+ ]
452+ , testGroup "SomeType" [
453+ testProperty "2ElemArray" (is2ElemArray . thSomeTypeToJSON2ElemArray)
454+ , testProperty "TaggedObject" (isTaggedObject . thSomeTypeToJSONTaggedObject)
455+ , testProperty "ObjectWithSingleField" (isObjectWithSingleField . thSomeTypeToJSONObjectWithSingleField)
456+ , testGroup "roundTrip" [
457+ testProperty "2ElemArray" (toParseJSON thSomeTypeParseJSON2ElemArray thSomeTypeToJSON2ElemArray)
458+ , testProperty "TaggedObject" (toParseJSON thSomeTypeParseJSONTaggedObject thSomeTypeToJSONTaggedObject)
459+ , testProperty "ObjectWithSingleField" (toParseJSON thSomeTypeParseJSONObjectWithSingleField thSomeTypeToJSONObjectWithSingleField)
460+
461+ , testProperty "2ElemArray unary" (toParseJSON1 thSomeTypeLiftParseJSON2ElemArray thSomeTypeLiftToJSON2ElemArray)
462+ , testProperty "TaggedObject unary" (toParseJSON1 thSomeTypeLiftParseJSONTaggedObject thSomeTypeLiftToJSONTaggedObject)
463+ , testProperty "ObjectWithSingleField unary" (toParseJSON1 thSomeTypeLiftParseJSONObjectWithSingleField thSomeTypeLiftToJSONObjectWithSingleField)
464+
465+ ]
466+ ]
467+ , testGroup "Approx" [
468+ testProperty "string" (isString . thApproxToJSONUnwrap)
469+ , testProperty "ObjectWithSingleField" (isObjectWithSingleField . thApproxToJSONDefault)
470+ , testGroup "roundTrip" [
471+ testProperty "string" (toParseJSON thApproxParseJSONUnwrap thApproxToJSONUnwrap)
472+ , testProperty "ObjectWithSingleField" (toParseJSON thApproxParseJSONDefault thApproxToJSONDefault)
473+ ]
474+ ]
475+ , testGroup "GADT" [
476+ testProperty "string" (isString . thGADTToJSONUnwrap)
477+ , testProperty "ObjectWithSingleField" (isObjectWithSingleField . thGADTToJSONDefault)
478+ , testGroup "roundTrip" [
479+ testProperty "string" (toParseJSON thGADTParseJSONUnwrap thGADTToJSONUnwrap)
480+ , testProperty "ObjectWithSingleField" (toParseJSON thGADTParseJSONDefault thGADTToJSONDefault)
481+ ]
482+ ]
483+ , testGroup "OneConstructor" [
484+ testProperty "default" (isEmptyArray . thOneConstructorToJSONDefault)
485+ , testProperty "Tagged" (isTaggedObject . thOneConstructorToJSONTagged)
486+ , testGroup "roundTrip" [
487+ testProperty "default" (toParseJSON thOneConstructorParseJSONDefault thOneConstructorToJSONDefault)
488+ , testProperty "Tagged" (toParseJSON thOneConstructorParseJSONTagged thOneConstructorToJSONTagged)
489+ ]
490+ ]
491+ ]
492+ , testGroup "toEncoding" [
493+ testProperty "NullaryString" $
494+ thNullaryToJSONString `sameAs` thNullaryToEncodingString
495+ , testProperty "Nullary2ElemArray" $
496+ thNullaryToJSON2ElemArray `sameAs` thNullaryToEncoding2ElemArray
497+ , testProperty "NullaryTaggedObject" $
498+ thNullaryToJSONTaggedObject `sameAs` thNullaryToEncodingTaggedObject
499+ , testProperty "NullaryObjectWithSingleField" $
500+ thNullaryToJSONObjectWithSingleField `sameAs`
501+ thNullaryToEncodingObjectWithSingleField
502+ , testProperty "ApproxUnwrap" $
503+ thApproxToJSONUnwrap `sameAs` thApproxToEncodingUnwrap
504+ , testProperty "ApproxDefault" $
505+ thApproxToJSONDefault `sameAs` thApproxToEncodingDefault
506+
507+ , testProperty "EitherTextInt UntaggedValue" $
508+ thEitherTextIntToJSONUntaggedValue `sameAs` thEitherTextIntToEncodingUntaggedValue
509+
510+ , testProperty "SomeType2ElemArray" $
511+ thSomeTypeToJSON2ElemArray `sameAs` thSomeTypeToEncoding2ElemArray
512+ , testProperty "SomeType2ElemArray unary" $
513+ thSomeTypeLiftToJSON2ElemArray `sameAs1` thSomeTypeLiftToEncoding2ElemArray
514+ , testProperty "SomeType2ElemArray unary agree" $
515+ thSomeTypeToEncoding2ElemArray `sameAs1Agree` thSomeTypeLiftToEncoding2ElemArray
516+
517+ , testProperty "SomeTypeTaggedObject" $
518+ thSomeTypeToJSONTaggedObject `sameAs` thSomeTypeToEncodingTaggedObject
519+ , testProperty "SomeTypeTaggedObject unary" $
520+ thSomeTypeLiftToJSONTaggedObject `sameAs1` thSomeTypeLiftToEncodingTaggedObject
521+ , testProperty "SomeTypeTaggedObject unary agree" $
522+ thSomeTypeToEncodingTaggedObject `sameAs1Agree` thSomeTypeLiftToEncodingTaggedObject
523+
524+ , testProperty "SomeTypeObjectWithSingleField" $
525+ thSomeTypeToJSONObjectWithSingleField `sameAs` thSomeTypeToEncodingObjectWithSingleField
526+ , testProperty "SomeTypeObjectWithSingleField unary" $
527+ thSomeTypeLiftToJSONObjectWithSingleField `sameAs1` thSomeTypeLiftToEncodingObjectWithSingleField
528+ , testProperty "SomeTypeObjectWithSingleField unary agree" $
529+ thSomeTypeToEncodingObjectWithSingleField `sameAs1Agree` thSomeTypeLiftToEncodingObjectWithSingleField
530+
531+ , testProperty "OneConstructorDefault" $
532+ thOneConstructorToJSONDefault `sameAs` thOneConstructorToEncodingDefault
533+ , testProperty "OneConstructorTagged" $
534+ thOneConstructorToJSONTagged `sameAs` thOneConstructorToEncodingTagged
535+ ]
536+ ]
40 ]
537 ]
tests/PropertyGeneric.hsdeleted−116
@@ -1,116 +0,0 @@
1−{-# LANGUAGE CPP #-}
2−{-# LANGUAGE NoImplicitPrelude #-}
3−
4−module PropertyGeneric ( genericTests ) where
5−
6−import Prelude.Compat
7−
8−import Data.Semigroup (Option(..))
9−import Encoders
10−import Instances ()
11−import Test.Tasty (TestTree, testGroup)
12−import Test.Tasty.QuickCheck (testProperty)
13−import Test.QuickCheck ( (===) )
14−import Types
15−import PropUtils
16−
17−
18−genericTests :: TestTree
19−genericTests =
20− testGroup "generic" [
21− testGroup "toJSON" [
22− testGroup "Nullary" [
23− testProperty "string" (isString . gNullaryToJSONString)
24− , testProperty "2ElemArray" (is2ElemArray . gNullaryToJSON2ElemArray)
25− , testProperty "TaggedObject" (isNullaryTaggedObject . gNullaryToJSONTaggedObject)
26− , testProperty "ObjectWithSingleField" (isObjectWithSingleField . gNullaryToJSONObjectWithSingleField)
27− , testGroup "roundTrip" [
28− testProperty "string" (toParseJSON gNullaryParseJSONString gNullaryToJSONString)
29− , testProperty "2ElemArray" (toParseJSON gNullaryParseJSON2ElemArray gNullaryToJSON2ElemArray)
30− , testProperty "TaggedObject" (toParseJSON gNullaryParseJSONTaggedObject gNullaryToJSONTaggedObject)
31− , testProperty "ObjectWithSingleField" (toParseJSON gNullaryParseJSONObjectWithSingleField gNullaryToJSONObjectWithSingleField)
32− ]
33− ]
34− , testGroup "EitherTextInt" [
35− testProperty "UntaggedValue" (isUntaggedValueETI . gEitherTextIntToJSONUntaggedValue)
36− , testProperty "roundtrip" (toParseJSON gEitherTextIntParseJSONUntaggedValue gEitherTextIntToJSONUntaggedValue)
37− ]
38− , testGroup "SomeType" [
39− testProperty "2ElemArray" (is2ElemArray . gSomeTypeToJSON2ElemArray)
40− , testProperty "TaggedObject" (isTaggedObject . gSomeTypeToJSONTaggedObject)
41− , testProperty "ObjectWithSingleField" (isObjectWithSingleField . gSomeTypeToJSONObjectWithSingleField)
42− , testGroup "roundTrip" [
43− testProperty "2ElemArray" (toParseJSON gSomeTypeParseJSON2ElemArray gSomeTypeToJSON2ElemArray)
44− , testProperty "TaggedObject" (toParseJSON gSomeTypeParseJSONTaggedObject gSomeTypeToJSONTaggedObject)
45− , testProperty "ObjectWithSingleField" (toParseJSON gSomeTypeParseJSONObjectWithSingleField gSomeTypeToJSONObjectWithSingleField)
46−
47− , testProperty "2ElemArray unary" (toParseJSON1 gSomeTypeLiftParseJSON2ElemArray gSomeTypeLiftToJSON2ElemArray)
48− , testProperty "TaggedObject unary" (toParseJSON1 gSomeTypeLiftParseJSONTaggedObject gSomeTypeLiftToJSONTaggedObject)
49− , testProperty "ObjectWithSingleField unary" (toParseJSON1 gSomeTypeLiftParseJSONObjectWithSingleField gSomeTypeLiftToJSONObjectWithSingleField)
50− ]
51− ]
52− , testGroup "OneConstructor" [
53− testProperty "default" (isEmptyArray . gOneConstructorToJSONDefault)
54− , testProperty "Tagged" (isTaggedObject . gOneConstructorToJSONTagged)
55− , testGroup "roundTrip" [
56− testProperty "default" (toParseJSON gOneConstructorParseJSONDefault gOneConstructorToJSONDefault)
57− , testProperty "Tagged" (toParseJSON gOneConstructorParseJSONTagged gOneConstructorToJSONTagged)
58− ]
59− ]
60− , testGroup "OptionField" [
61− testProperty "like Maybe" $
62− \x -> gOptionFieldToJSON (OptionField (Option x)) === thMaybeFieldToJSON (MaybeField x)
63− , testProperty "roundTrip" (toParseJSON gOptionFieldParseJSON gOptionFieldToJSON)
64− ]
65− ]
66− , testGroup "toEncoding" [
67− testProperty "NullaryString" $
68− gNullaryToJSONString `sameAs` gNullaryToEncodingString
69− , testProperty "Nullary2ElemArray" $
70− gNullaryToJSON2ElemArray `sameAs` gNullaryToEncoding2ElemArray
71− , testProperty "NullaryTaggedObject" $
72− gNullaryToJSONTaggedObject `sameAs` gNullaryToEncodingTaggedObject
73− , testProperty "NullaryObjectWithSingleField" $
74− gNullaryToJSONObjectWithSingleField `sameAs`
75− gNullaryToEncodingObjectWithSingleField
76− -- , testProperty "ApproxUnwrap" $
77− -- gApproxToJSONUnwrap `sameAs` gApproxToEncodingUnwrap
78− , testProperty "ApproxDefault" $
79− gApproxToJSONDefault `sameAs` gApproxToEncodingDefault
80−
81− , testProperty "EitherTextInt UntaggedValue" $
82− gEitherTextIntToJSONUntaggedValue `sameAs` gEitherTextIntToEncodingUntaggedValue
83−
84− , testProperty "SomeType2ElemArray" $
85− gSomeTypeToJSON2ElemArray `sameAs` gSomeTypeToEncoding2ElemArray
86− , testProperty "SomeType2ElemArray unary" $
87− gSomeTypeLiftToJSON2ElemArray `sameAs1` gSomeTypeLiftToEncoding2ElemArray
88− , testProperty "SomeType2ElemArray unary agree" $
89− gSomeTypeToEncoding2ElemArray `sameAs1Agree` gSomeTypeLiftToEncoding2ElemArray
90−
91− , testProperty "SomeTypeTaggedObject" $
92− gSomeTypeToJSONTaggedObject `sameAs` gSomeTypeToEncodingTaggedObject
93− , testProperty "SomeTypeTaggedObject unary" $
94− gSomeTypeLiftToJSONTaggedObject `sameAs1` gSomeTypeLiftToEncodingTaggedObject
95− , testProperty "SomeTypeTaggedObject unary agree" $
96− gSomeTypeToEncodingTaggedObject `sameAs1Agree` gSomeTypeLiftToEncodingTaggedObject
97−
98− , testProperty "SomeTypeObjectWithSingleField" $
99− gSomeTypeToJSONObjectWithSingleField `sameAs` gSomeTypeToEncodingObjectWithSingleField
100− , testProperty "SomeTypeObjectWithSingleField unary" $
101− gSomeTypeLiftToJSONObjectWithSingleField `sameAs1` gSomeTypeLiftToEncodingObjectWithSingleField
102− , testProperty "SomeTypeObjectWithSingleField unary agree" $
103− gSomeTypeToEncodingObjectWithSingleField `sameAs1Agree` gSomeTypeLiftToEncodingObjectWithSingleField
104−
105− , testProperty "SomeTypeOmitNothingFields" $
106− gSomeTypeToJSONOmitNothingFields `sameAs` gSomeTypeToEncodingOmitNothingFields
107−
108− , testProperty "OneConstructorDefault" $
109− gOneConstructorToJSONDefault `sameAs` gOneConstructorToEncodingDefault
110− , testProperty "OneConstructorTagged" $
111− gOneConstructorToJSONTagged `sameAs` gOneConstructorToEncodingTagged
112−
113− , testProperty "OptionField" $
114− gOptionFieldToJSON `sameAs` gOptionFieldToEncoding
115− ]
116− ]
tests/PropertyKeys.hsdeleted−40
@@ -1,40 +0,0 @@
1−{-# LANGUAGE NoImplicitPrelude #-}
2−
3−module PropertyKeys ( keysTests ) where
4−
5−import Prelude.Compat
6−
7−import Data.Time (Day, LocalTime, TimeOfDay, UTCTime)
8−import Data.Version (Version)
9−import Instances ()
10−import Numeric.Natural (Natural)
11−import Test.Tasty (TestTree, testGroup)
12−import Test.Tasty.QuickCheck (testProperty)
13−import Types
14−import qualified Data.Text as T
15−import qualified Data.Text.Lazy as LT
16−import qualified Data.UUID.Types as UUID
17−import PropUtils
18−
19−
20−keysTests :: TestTree
21−keysTests =
22− testGroup "roundTrip Key"
23− [ testProperty "Bool" $ roundTripKey True
24− , testProperty "Text" $ roundTripKey (undefined :: T.Text)
25− , testProperty "String" $ roundTripKey (undefined :: String)
26− , testProperty "Int" $ roundTripKey (undefined :: Int)
27− , testProperty "[Text]" $ roundTripKey (undefined :: LogScaled [T.Text])
28− , testProperty "(Int,Char)" $ roundTripKey (undefined :: (Int,Char))
29− , testProperty "Integer" $ roundTripKey (undefined :: Integer)
30− , testProperty "Natural" $ roundTripKey (undefined :: Natural)
31− , testProperty "Float" $ roundTripKey (undefined :: Float)
32− , testProperty "Double" $ roundTripKey (undefined :: Double)
33− , testProperty "Day" $ roundTripKey (undefined :: Day)
34− , testProperty "LocalTime" $ roundTripKey (undefined :: LocalTime)
35− , testProperty "TimeOfDay" $ roundTripKey (undefined :: TimeOfDay)
36− , testProperty "UTCTime" $ roundTripKey (undefined :: UTCTime)
37− , testProperty "Version" $ roundTripKey (undefined :: Version)
38− , testProperty "Lazy Text" $ roundTripKey (undefined :: LT.Text)
39− , testProperty "UUID" $ roundTripKey UUID.nil
40− ]
tests/PropertyRTFunctors.hsdeleted−51
@@ -1,51 +0,0 @@
1−{-# LANGUAGE NoImplicitPrelude #-}
2−
3−module PropertyRTFunctors ( roundTripFunctorsTests ) where
4−
5−import Prelude.Compat
6−
7−import Data.Functor.Compose (Compose (..))
8−import Instances ()
9−import Test.Tasty (TestTree, testGroup)
10−import Test.Tasty.QuickCheck (testProperty)
11−import Types
12−import PropUtils
13−
14−
15−roundTripFunctorsTests :: TestTree
16−roundTripFunctorsTests =
17− testGroup "functors"
18− [ testProperty "Identity Char" $ roundTripEq (undefined :: I Int)
19−
20− , testProperty "Identity Char" $ roundTripEq (undefined :: I Char)
21− , testProperty "Identity [Char]" $ roundTripEq (undefined :: I String)
22− , testProperty "[Identity Char]" $ roundTripEq (undefined :: [I Char])
23−
24− , testProperty "Compose I I Int" $ roundTripEq (undefined :: LogScaled (Compose I I Int))
25− , testProperty "Compose [] I Int" $ roundTripEq (undefined :: LogScaled (Compose [] I Int))
26− , testProperty "Compose I [] Int" $ roundTripEq (undefined :: LogScaled (Compose I [] Int))
27− , testProperty "Compose [] [] Int" $ roundTripEq (undefined :: LogScaled (Compose [] [] Int))
28−
29− , testProperty "Compose I I Char" $ roundTripEq (undefined :: LogScaled (Compose I I Char))
30− , testProperty "Compose [] I Char" $ roundTripEq (undefined :: LogScaled (Compose [] I Char))
31− , testProperty "Compose I [] Char" $ roundTripEq (undefined :: LogScaled (Compose I [] Char))
32− , testProperty "Compose [] [] Char" $ roundTripEq (undefined :: LogScaled (Compose [] [] Char))
33−
34− , testProperty "Compose3 I I I Char" $ roundTripEq (undefined :: LogScaled (Compose3 I I I Char))
35− , testProperty "Compose3 I [] I Char" $ roundTripEq (undefined :: LogScaled (Compose3 I [] I Char))
36− , testProperty "Compose3 I I [] Char" $ roundTripEq (undefined :: LogScaled (Compose3 I I [] Char))
37− , testProperty "Compose3 I [] [] Char" $ roundTripEq (undefined :: LogScaled (Compose3 I [] [] Char))
38− , testProperty "Compose3 [] I I Char" $ roundTripEq (undefined :: LogScaled (Compose3 [] I I Char))
39− , testProperty "Compose3 [] [] I Char" $ roundTripEq (undefined :: LogScaled (Compose3 [] [] I Char))
40− , testProperty "Compose3 [] I [] Char" $ roundTripEq (undefined :: LogScaled (Compose3 [] I [] Char))
41− , testProperty "Compose3 [] [] [] Char" $ roundTripEq (undefined :: LogScaled (Compose3 [] [] [] Char))
42−
43− , testProperty "Compose3' I I I Char" $ roundTripEq (undefined :: LogScaled (Compose3' I I I Char))
44− , testProperty "Compose3' I [] I Char" $ roundTripEq (undefined :: LogScaled (Compose3' I [] I Char))
45− , testProperty "Compose3' I I [] Char" $ roundTripEq (undefined :: LogScaled (Compose3' I I [] Char))
46− , testProperty "Compose3' I [] [] Char" $ roundTripEq (undefined :: LogScaled (Compose3' I [] [] Char))
47− , testProperty "Compose3' [] I I Char" $ roundTripEq (undefined :: LogScaled (Compose3' [] I I Char))
48− , testProperty "Compose3' [] [] I Char" $ roundTripEq (undefined :: LogScaled (Compose3' [] [] I Char))
49− , testProperty "Compose3' [] I [] Char" $ roundTripEq (undefined :: LogScaled (Compose3' [] I [] Char))
50− , testProperty "Compose3' [] [] [] Char" $ roundTripEq (undefined :: LogScaled (Compose3' [] [] [] Char))
51− ]
tests/PropertyRoundTrip.hsdeleted−74
@@ -1,74 +0,0 @@
1−{-# LANGUAGE NoImplicitPrelude #-}
2−
3−module PropertyRoundTrip ( roundTripTests ) where
4−
5−import Prelude.Compat
6−
7−import Control.Applicative (Const)
8−import Data.Aeson.Types
9−import Data.DList (DList)
10−import Data.List.NonEmpty (NonEmpty)
11−import Data.Proxy (Proxy)
12−import Data.Ratio (Ratio)
13−import Data.Sequence (Seq)
14−import Data.Tagged (Tagged)
15−import Data.Time (Day, DiffTime, LocalTime, NominalDiffTime, TimeOfDay, UTCTime, ZonedTime)
16−import Data.Version (Version)
17−import Data.Time.Calendar.Compat (CalendarDiffDays, DayOfWeek)
18−import Data.Time.LocalTime.Compat (CalendarDiffTime)
19−import Data.Time.Clock.System.Compat (SystemTime)
20−import Instances ()
21−import Numeric.Natural (Natural)
22−import Test.Tasty (TestTree, testGroup)
23−import Test.Tasty.QuickCheck (testProperty)
24−import Types
25−import qualified Data.Text as T
26−import qualified Data.Text.Lazy as LT
27−import qualified Data.UUID.Types as UUID
28−import PropUtils
29−import PropertyRTFunctors
30−
31−
32−roundTripTests :: TestTree
33−roundTripTests =
34− testGroup "roundTrip" [
35− testProperty "Bool" $ roundTripEq True
36− , testProperty "Double" $ roundTripEq (1 :: Approx Double)
37− , testProperty "Int" $ roundTripEq (1 :: Int)
38− , testProperty "NonEmpty Char" $ roundTripEq (undefined :: NonEmpty Char)
39− , testProperty "Integer" $ roundTripEq (1 :: Integer)
40− , testProperty "String" $ roundTripEq ("" :: String)
41− , testProperty "Text" $ roundTripEq T.empty
42− , testProperty "Lazy Text" $ roundTripEq LT.empty
43− , testProperty "Foo" $ roundTripEq (undefined :: Foo)
44− , testProperty "Day" $ roundTripEq (undefined :: Day)
45− , testProperty "BCE Day" $ roundTripEq (undefined :: BCEDay)
46− , testProperty "DotNetTime" $ roundTripEq (undefined :: Approx DotNetTime)
47− , testProperty "LocalTime" $ roundTripEq (undefined :: LocalTime)
48− , testProperty "TimeOfDay" $ roundTripEq (undefined :: TimeOfDay)
49− , testProperty "UTCTime" $ roundTripEq (undefined :: UTCTime)
50− , testProperty "ZonedTime" $ roundTripEq (undefined :: ZonedTime)
51− , testProperty "NominalDiffTime" $ roundTripEq (undefined :: NominalDiffTime)
52− , testProperty "DiffTime" $ roundTripEq (undefined :: DiffTime)
53− , testProperty "DayOfWeek" $ roundTripEq (undefined :: DayOfWeek)
54− , testProperty "SystemTime" $ roundTripEq (undefined :: SystemTime)
55− , testProperty "CalendarDiffTime" $ roundTripEq (undefined :: CalendarDiffTime)
56− , testProperty "CalendarDiffDays" $ roundTripEq (undefined :: CalendarDiffDays)
57− , testProperty "Version" $ roundTripEq (undefined :: Version)
58− , testProperty "Natural" $ roundTripEq (undefined :: Natural)
59− , testProperty "Proxy" $ roundTripEq (undefined :: Proxy Int)
60− , testProperty "Tagged" $ roundTripEq (undefined :: Tagged Int Char)
61− , testProperty "Const" $ roundTripEq (undefined :: Const Int Char)
62− , testProperty "DList" $ roundTripEq (undefined :: DList Int)
63− , testProperty "Seq" $ roundTripEq (undefined :: Seq Int)
64− , testProperty "Rational" $ roundTripEq (undefined :: Rational)
65− , testProperty "Ratio Int" $ roundTripEq (undefined :: Ratio Int)
66− , testProperty "UUID" $ roundTripEq UUID.nil
67− , roundTripFunctorsTests
68− , testGroup "ghcGenerics" [
69− testProperty "OneConstructor" $ roundTripEq OneConstructor
70− , testProperty "Product2" $ roundTripEq (undefined :: Product2 Int Bool)
71− , testProperty "Product6" $ roundTripEq (undefined :: P6)
72− , testProperty "Sum4" $ roundTripEq (undefined :: S4)
73− ]
74− ]
tests/PropertyTH.hsdeleted−130
@@ -1,130 +0,0 @@
1−{-# LANGUAGE NoImplicitPrelude #-}
2−
3−module PropertyTH ( templateHaskellTests ) where
4−
5−import Prelude.Compat
6−
7−import Data.Semigroup (Option(..))
8−import Encoders
9−import Instances ()
10−import Test.Tasty (TestTree, testGroup)
11−import Test.Tasty.QuickCheck (testProperty)
12−import Test.QuickCheck ( (===) )
13−import Types
14−import PropUtils
15−
16−
17−templateHaskellTests :: TestTree
18−templateHaskellTests =
19− testGroup "template-haskell" [
20− testGroup "toJSON" [
21− testGroup "Nullary" [
22− testProperty "string" (isString . thNullaryToJSONString)
23− , testProperty "2ElemArray" (is2ElemArray . thNullaryToJSON2ElemArray)
24− , testProperty "TaggedObject" (isNullaryTaggedObject . thNullaryToJSONTaggedObject)
25− , testProperty "ObjectWithSingleField" (isObjectWithSingleField . thNullaryToJSONObjectWithSingleField)
26−
27− , testGroup "roundTrip" [
28− testProperty "string" (toParseJSON thNullaryParseJSONString thNullaryToJSONString)
29− , testProperty "2ElemArray" (toParseJSON thNullaryParseJSON2ElemArray thNullaryToJSON2ElemArray)
30− , testProperty "TaggedObject" (toParseJSON thNullaryParseJSONTaggedObject thNullaryToJSONTaggedObject)
31− , testProperty "ObjectWithSingleField" (toParseJSON thNullaryParseJSONObjectWithSingleField thNullaryToJSONObjectWithSingleField)
32− ]
33− ]
34− , testGroup "EitherTextInt" [
35− testProperty "UntaggedValue" (isUntaggedValueETI . thEitherTextIntToJSONUntaggedValue)
36− , testProperty "roundtrip" (toParseJSON thEitherTextIntParseJSONUntaggedValue thEitherTextIntToJSONUntaggedValue)
37− ]
38− , testGroup "SomeType" [
39− testProperty "2ElemArray" (is2ElemArray . thSomeTypeToJSON2ElemArray)
40− , testProperty "TaggedObject" (isTaggedObject . thSomeTypeToJSONTaggedObject)
41− , testProperty "ObjectWithSingleField" (isObjectWithSingleField . thSomeTypeToJSONObjectWithSingleField)
42− , testGroup "roundTrip" [
43− testProperty "2ElemArray" (toParseJSON thSomeTypeParseJSON2ElemArray thSomeTypeToJSON2ElemArray)
44− , testProperty "TaggedObject" (toParseJSON thSomeTypeParseJSONTaggedObject thSomeTypeToJSONTaggedObject)
45− , testProperty "ObjectWithSingleField" (toParseJSON thSomeTypeParseJSONObjectWithSingleField thSomeTypeToJSONObjectWithSingleField)
46−
47− , testProperty "2ElemArray unary" (toParseJSON1 thSomeTypeLiftParseJSON2ElemArray thSomeTypeLiftToJSON2ElemArray)
48− , testProperty "TaggedObject unary" (toParseJSON1 thSomeTypeLiftParseJSONTaggedObject thSomeTypeLiftToJSONTaggedObject)
49− , testProperty "ObjectWithSingleField unary" (toParseJSON1 thSomeTypeLiftParseJSONObjectWithSingleField thSomeTypeLiftToJSONObjectWithSingleField)
50−
51− ]
52− ]
53− , testGroup "Approx" [
54− testProperty "string" (isString . thApproxToJSONUnwrap)
55− , testProperty "ObjectWithSingleField" (isObjectWithSingleField . thApproxToJSONDefault)
56− , testGroup "roundTrip" [
57− testProperty "string" (toParseJSON thApproxParseJSONUnwrap thApproxToJSONUnwrap)
58− , testProperty "ObjectWithSingleField" (toParseJSON thApproxParseJSONDefault thApproxToJSONDefault)
59− ]
60− ]
61− , testGroup "GADT" [
62− testProperty "string" (isString . thGADTToJSONUnwrap)
63− , testProperty "ObjectWithSingleField" (isObjectWithSingleField . thGADTToJSONDefault)
64− , testGroup "roundTrip" [
65− testProperty "string" (toParseJSON thGADTParseJSONUnwrap thGADTToJSONUnwrap)
66− , testProperty "ObjectWithSingleField" (toParseJSON thGADTParseJSONDefault thGADTToJSONDefault)
67− ]
68− ]
69− , testGroup "OneConstructor" [
70− testProperty "default" (isEmptyArray . thOneConstructorToJSONDefault)
71− , testProperty "Tagged" (isTaggedObject . thOneConstructorToJSONTagged)
72− , testGroup "roundTrip" [
73− testProperty "default" (toParseJSON thOneConstructorParseJSONDefault thOneConstructorToJSONDefault)
74− , testProperty "Tagged" (toParseJSON thOneConstructorParseJSONTagged thOneConstructorToJSONTagged)
75− ]
76− ]
77− , testGroup "OptionField" [
78− testProperty "like Maybe" $
79− \x -> thOptionFieldToJSON (OptionField (Option x)) === thMaybeFieldToJSON (MaybeField x)
80− , testProperty "roundTrip" (toParseJSON thOptionFieldParseJSON thOptionFieldToJSON)
81− ]
82− ]
83− , testGroup "toEncoding" [
84− testProperty "NullaryString" $
85− thNullaryToJSONString `sameAs` thNullaryToEncodingString
86− , testProperty "Nullary2ElemArray" $
87− thNullaryToJSON2ElemArray `sameAs` thNullaryToEncoding2ElemArray
88− , testProperty "NullaryTaggedObject" $
89− thNullaryToJSONTaggedObject `sameAs` thNullaryToEncodingTaggedObject
90− , testProperty "NullaryObjectWithSingleField" $
91− thNullaryToJSONObjectWithSingleField `sameAs`
92− thNullaryToEncodingObjectWithSingleField
93− , testProperty "ApproxUnwrap" $
94− thApproxToJSONUnwrap `sameAs` thApproxToEncodingUnwrap
95− , testProperty "ApproxDefault" $
96− thApproxToJSONDefault `sameAs` thApproxToEncodingDefault
97−
98− , testProperty "EitherTextInt UntaggedValue" $
99− thEitherTextIntToJSONUntaggedValue `sameAs` thEitherTextIntToEncodingUntaggedValue
100−
101− , testProperty "SomeType2ElemArray" $
102− thSomeTypeToJSON2ElemArray `sameAs` thSomeTypeToEncoding2ElemArray
103− , testProperty "SomeType2ElemArray unary" $
104− thSomeTypeLiftToJSON2ElemArray `sameAs1` thSomeTypeLiftToEncoding2ElemArray
105− , testProperty "SomeType2ElemArray unary agree" $
106− thSomeTypeToEncoding2ElemArray `sameAs1Agree` thSomeTypeLiftToEncoding2ElemArray
107−
108− , testProperty "SomeTypeTaggedObject" $
109− thSomeTypeToJSONTaggedObject `sameAs` thSomeTypeToEncodingTaggedObject
110− , testProperty "SomeTypeTaggedObject unary" $
111− thSomeTypeLiftToJSONTaggedObject `sameAs1` thSomeTypeLiftToEncodingTaggedObject
112− , testProperty "SomeTypeTaggedObject unary agree" $
113− thSomeTypeToEncodingTaggedObject `sameAs1Agree` thSomeTypeLiftToEncodingTaggedObject
114−
115− , testProperty "SomeTypeObjectWithSingleField" $
116− thSomeTypeToJSONObjectWithSingleField `sameAs` thSomeTypeToEncodingObjectWithSingleField
117− , testProperty "SomeTypeObjectWithSingleField unary" $
118− thSomeTypeLiftToJSONObjectWithSingleField `sameAs1` thSomeTypeLiftToEncodingObjectWithSingleField
119− , testProperty "SomeTypeObjectWithSingleField unary agree" $
120− thSomeTypeToEncodingObjectWithSingleField `sameAs1Agree` thSomeTypeLiftToEncodingObjectWithSingleField
121−
122− , testProperty "OneConstructorDefault" $
123− thOneConstructorToJSONDefault `sameAs` thOneConstructorToEncodingDefault
124− , testProperty "OneConstructorTagged" $
125− thOneConstructorToJSONTagged `sameAs` thOneConstructorToEncodingTagged
126−
127− , testProperty "OptionField" $
128− thOptionFieldToJSON `sameAs` thOptionFieldToEncoding
129− ]
130− ]
tests/SerializationFormatSpec.hs+30−53
@@ -1,9 +1,10 @@
1{-# LANGUAGE CPP #-}
1{-# LANGUAGE CPP #-}
2{-# LANGUAGE DeriveGeneric #-}
2{-# LANGUAGE DeriveGeneric #-}
3{-# LANGUAGE GADTs #-}
3{-# LANGUAGE GADTs #-}
4−{-# LANGUAGE NoImplicitPrelude #-}
5{-# LANGUAGE OverloadedStrings #-}
4{-# LANGUAGE OverloadedStrings #-}
5+#if __GLASGOW_HASKELL__ >= 708
6{-# LANGUAGE DataKinds #-}
6{-# LANGUAGE DataKinds #-}
7+#endif
7
8
8------------------------------------------------------------------------------
9------------------------------------------------------------------------------
9-- These tests assert that the JSON serialization doesn't change by accident.
10-- These tests assert that the JSON serialization doesn't change by accident.
@@ -14,6 +15,7 @@ module SerializationFormatSpec
14 tests
15 tests
15 ) where
16 ) where
16
17
18+import Prelude ()
17import Prelude.Compat
19import Prelude.Compat
18
20
19import Control.Applicative (Const(..))
21import Control.Applicative (Const(..))
@@ -30,14 +32,12 @@ import Data.Proxy (Proxy(..))
30import Data.Scientific (Scientific)
32import Data.Scientific (Scientific)
31import Data.Tagged (Tagged(..))
33import Data.Tagged (Tagged(..))
32import Data.Time (fromGregorian)
34import Data.Time (fromGregorian)
33−import Data.Time.Calendar.Compat (CalendarDiffDays (..), DayOfWeek (..))
34−import Data.Time.LocalTime.Compat (CalendarDiffTime (..))
35−import Data.Time.Clock.System.Compat (SystemTime (..))
36import Data.Word (Word8)
35import Data.Word (Word8)
37import GHC.Generics (Generic)
36import GHC.Generics (Generic)
38import Instances ()
37import Instances ()
39−import Test.Tasty (TestTree, testGroup)
38+import Test.Framework (Test, testGroup)
39+import Test.Framework.Providers.HUnit (testCase)
40−import Test.Tasty.HUnit (assertFailure, assertEqual, testCase)
40+import Test.HUnit (assertFailure, assertEqual)
41import Types (Approx(..), Compose3, Compose3', I)
41import Types (Approx(..), Compose3, Compose3', I)
42import qualified Data.ByteString.Lazy.Char8 as L
42import qualified Data.ByteString.Lazy.Char8 as L
43import qualified Data.DList as DList
43import qualified Data.DList as DList
@@ -54,7 +54,7 @@ import qualified Data.Tree as Tree
54import qualified Data.UUID.Types as UUID
54import qualified Data.UUID.Types as UUID
55import qualified Data.Vector as Vector
55import qualified Data.Vector as Vector
56
56
57−tests :: [TestTree]
57+tests :: [Test]
58tests =
58tests =
59 [
59 [
60 testGroup "To JSON representation" $ fmap assertJsonEncodingExample jsonEncodingExamples
60 testGroup "To JSON representation" $ fmap assertJsonEncodingExample jsonEncodingExamples
@@ -69,14 +69,13 @@ jsonExamples =
69 example "Either Left" "{\"Left\":1}" (Left 1 :: Either Int Int)
69 example "Either Left" "{\"Left\":1}" (Left 1 :: Either Int Int)
70 , example "Either Right" "{\"Right\":1}" (Right 1 :: Either Int Int)
70 , example "Either Right" "{\"Right\":1}" (Right 1 :: Either Int Int)
71 , example "Nothing" "null" (Nothing :: Maybe Int)
71 , example "Nothing" "null" (Nothing :: Maybe Int)
72− -- Maybe serialising is lossy
73− -- https://github.com/bos/aeson/issues/376
74− , Example "Just Nothing" ["null"] (Just Nothing :: Maybe (Maybe Int)) Nothing
75 , example "Just" "1" (Just 1 :: Maybe Int)
72 , example "Just" "1" (Just 1 :: Maybe Int)
76 , example "Proxy Int" "null" (Proxy :: Proxy Int)
73 , example "Proxy Int" "null" (Proxy :: Proxy Int)
77 , example "Tagged Char Int" "1" (Tagged 1 :: Tagged Char Int)
74 , example "Tagged Char Int" "1" (Tagged 1 :: Tagged Char Int)
75+#if __GLASGOW_HASKELL__ >= 708
78 -- Test Tagged instance is polykinded
76 -- Test Tagged instance is polykinded
79 , example "Tagged 123 Int" "1" (Tagged 1 :: Tagged 123 Int)
77 , example "Tagged 123 Int" "1" (Tagged 1 :: Tagged 123 Int)
78+#endif
80 , example "Const Char Int" "\"c\"" (Const 'c' :: Const Char Int)
79 , example "Const Char Int" "\"c\"" (Const 'c' :: Const Char Int)
81 , example "Tuple" "[1,2]" ((1, 2) :: (Int, Int))
80 , example "Tuple" "[1,2]" ((1, 2) :: (Int, Int))
82 , example "NonEmpty" "[1,2,3]" (1 :| [2, 3] :: NonEmpty Int)
81 , example "NonEmpty" "[1,2,3]" (1 :| [2, 3] :: NonEmpty Int)
@@ -84,22 +83,22 @@ jsonExamples =
84 , example "DList" "[1,2,3]" (DList.fromList [1, 2, 3] :: DList.DList Int)
83 , example "DList" "[1,2,3]" (DList.fromList [1, 2, 3] :: DList.DList Int)
85 , example "()" "[]" ()
84 , example "()" "[]" ()
86
85
87− , ndExample "HashMap Int Int"
86+ , Example "HashMap Int Int"
88 [ "{\"0\":1,\"2\":3}", "{\"2\":3,\"0\":1}"]
87 [ "{\"0\":1,\"2\":3}", "{\"2\":3,\"0\":1}"]
89 (HM.fromList [(0,1),(2,3)] :: HM.HashMap Int Int)
88 (HM.fromList [(0,1),(2,3)] :: HM.HashMap Int Int)
90− , ndExample "Map Int Int"
89+ , Example "Map Int Int"
91 [ "{\"0\":1,\"2\":3}", "{\"2\":3,\"0\":1}"]
90 [ "{\"0\":1,\"2\":3}", "{\"2\":3,\"0\":1}"]
92 (M.fromList [(0,1),(2,3)] :: M.Map Int Int)
91 (M.fromList [(0,1),(2,3)] :: M.Map Int Int)
93− , ndExample "Map (Tagged Int Int) Int"
92+ , Example "Map (Tagged Int Int) Int"
94 [ "{\"0\":1,\"2\":3}", "{\"2\":3,\"0\":1}"]
93 [ "{\"0\":1,\"2\":3}", "{\"2\":3,\"0\":1}"]
95 (M.fromList [(Tagged 0,1),(Tagged 2,3)] :: M.Map (Tagged Int Int) Int)
94 (M.fromList [(Tagged 0,1),(Tagged 2,3)] :: M.Map (Tagged Int Int) Int)
96 , example "Map [Int] Int"
95 , example "Map [Int] Int"
97 "[[[0],1],[[2],3]]"
96 "[[[0],1],[[2],3]]"
98 (M.fromList [([0],1),([2],3)] :: M.Map [Int] Int)
97 (M.fromList [([0],1),([2],3)] :: M.Map [Int] Int)
99− , ndExample "Map [Char] Int"
98+ , Example "Map [Char] Int"
100 [ "{\"ab\":1,\"cd\":3}", "{\"cd\":3,\"ab\":1}" ]
99 [ "{\"ab\":1,\"cd\":3}", "{\"cd\":3,\"ab\":1}" ]
101 (M.fromList [("ab",1),("cd",3)] :: M.Map String Int)
100 (M.fromList [("ab",1),("cd",3)] :: M.Map String Int)
102− , ndExample "Map [I Char] Int"
101+ , Example "Map [I Char] Int"
103 [ "{\"ab\":1,\"cd\":3}", "{\"cd\":3,\"ab\":1}" ]
102 [ "{\"ab\":1,\"cd\":3}", "{\"cd\":3,\"ab\":1}" ]
104 (M.fromList [(map pure "ab",1),(map pure "cd",3)] :: M.Map [I Char] Int)
103 (M.fromList [(map pure "ab",1),(map pure "cd",3)] :: M.Map [I Char] Int)
105
104
@@ -195,27 +194,16 @@ jsonExamples =
195 , example "Semigroup.WrappedMonoid Int" "2" (Semigroup.WrapMonoid 2 :: Semigroup.WrappedMonoid Int)
194 , example "Semigroup.WrappedMonoid Int" "2" (Semigroup.WrapMonoid 2 :: Semigroup.WrappedMonoid Int)
196 , example "Semigroup.Option Just" "2" (pure 2 :: Semigroup.Option Int)
195 , example "Semigroup.Option Just" "2" (pure 2 :: Semigroup.Option Int)
197 , example "Semigroup.Option Nothing" "null" (Semigroup.Option (Nothing :: Maybe Bool))
196 , example "Semigroup.Option Nothing" "null" (Semigroup.Option (Nothing :: Maybe Bool))
198−
199− -- time 1.9
200− , example "SystemTime" "123.123456789" (MkSystemTime 123 123456789)
201− , Example "SystemTime" ["124.23456789"]
202− (MkSystemTime 123 1234567890)
203− (MkSystemTime 124 234567890)
204− , ndExample "CalendarDiffTime"
205− [ "{\"months\":12,\"time\":456.789}", "{\"time\":456.789,\"months\":12}" ]
206− (CalendarDiffTime 12 456.789)
207− , ndExample "CalendarDiffDays"
208− [ "{\"months\":12,\"days\":20}", "{\"days\":20,\"months\":12}" ]
209− (CalendarDiffDays 12 20)
210− , example "DayOfWeek" "\"monday\"" Monday
211 ]
197 ]
212
198
213jsonEncodingExamples :: [Example]
199jsonEncodingExamples :: [Example]
214jsonEncodingExamples =
200jsonEncodingExamples =
215 [
201 [
202+ -- Maybe serialising is lossy
203+ -- https://github.com/bos/aeson/issues/376
204+ example "Just Nothing" "null" (Just Nothing :: Maybe (Maybe Int))
216 -- infinities cannot be recovered, null is decoded as NaN
205 -- infinities cannot be recovered, null is decoded as NaN
217− example "inf :: Double" "null" (Approx $ 1/0 :: Approx Double)
218− , example "-inf :: Double" "null" (Approx $ -1/0 :: Approx Double)
206+ , example "inf :: Double" "null" (Approx $ 1/0 :: Approx Double)
219 ]
207 ]
220
208
221jsonDecodingExamples :: [Example]
209jsonDecodingExamples :: [Example]
@@ -237,27 +225,16 @@ jsonDecodingExamples = [
237 ]
225 ]
238
226
239data Example where
227data Example where
240− Example
228+ Example
241− :: (Eq a, Show a, ToJSON a, FromJSON a)
229+ :: (Eq a, Show a, ToJSON a, FromJSON a)
242− => String -- name
243− -> [L.ByteString] -- encoded variants
244− -> a -- input
245− -> a -- decoded
246− -> Example -- empty bytestring will fail, any p [] == False
230+ => String -> [L.ByteString] -> a -> Example -- empty bytestring will fail, any p [] == False
247−
248− MaybeExample
231+ MaybeExample
249− :: (Eq a, Show a, FromJSON a)
232+ :: (Eq a, Show a, FromJSON a)
250− => String -> L.ByteString -> Maybe a -> Example
233+ => String -> L.ByteString -> Maybe a -> Example
251
234
252example :: (Eq a, Show a, ToJSON a, FromJSON a)
235example :: (Eq a, Show a, ToJSON a, FromJSON a)
253 => String -> L.ByteString -> a -> Example
236 => String -> L.ByteString -> a -> Example
254−example n bs x = Example n [bs] x x
237+example n bs x = Example n [bs] x
255−
256−-- | Non-deterministic example, input encodes to some of bytestrings.
257−ndExample :: (Eq a, Show a, ToJSON a, FromJSON a)
258− => String -> [L.ByteString] -> a -> Example
259−ndExample n bss x = Example n bss x x
260−
261
238
262data MyEither a b = MyLeft a | MyRight b
239data MyEither a b = MyLeft a | MyRight b
263 deriving (Generic, Show, Eq)
240 deriving (Generic, Show, Eq)
@@ -269,17 +246,17 @@ instance (ToJSON a, ToJSON b) => ToJSON (MyEither a b) where
269instance (FromJSON a, FromJSON b) => FromJSON (MyEither a b) where
246instance (FromJSON a, FromJSON b) => FromJSON (MyEither a b) where
270 parseJSON = genericParseJSON defaultOptions { sumEncoding = UntaggedValue }
247 parseJSON = genericParseJSON defaultOptions { sumEncoding = UntaggedValue }
271
248
272−assertJsonExample :: Example -> TestTree
249+assertJsonExample :: Example -> Test
273−assertJsonExample (Example name bss val val') = testCase name $ do
250+assertJsonExample (Example name bss val) = testCase name $ do
274 assertSomeEqual "encode" bss (encode val)
251 assertSomeEqual "encode" bss (encode val)
275 assertSomeEqual "encode/via value" bss (encode $ toJSON val)
252 assertSomeEqual "encode/via value" bss (encode $ toJSON val)
276 for_ bss $ \bs ->
253 for_ bss $ \bs ->
277− assertEqual "decode" (Just val') (decode bs)
254+ assertEqual "decode" (Just val) (decode bs)
278assertJsonExample (MaybeExample name bs mval) = testCase name $
255assertJsonExample (MaybeExample name bs mval) = testCase name $
279 assertEqual "decode" mval (decode bs)
256 assertEqual "decode" mval (decode bs)
280
257
281−assertJsonEncodingExample :: Example -> TestTree
258+assertJsonEncodingExample :: Example -> Test
282−assertJsonEncodingExample (Example name bss val _) = testCase name $ do
259+assertJsonEncodingExample (Example name bss val) = testCase name $ do
283 assertSomeEqual "encode" bss (encode val)
260 assertSomeEqual "encode" bss (encode val)
284 assertSomeEqual "encode/via value" bss (encode $ toJSON val)
261 assertSomeEqual "encode/via value" bss (encode $ toJSON val)
285assertJsonEncodingExample (MaybeExample name _ _) = testCase name $
262assertJsonEncodingExample (MaybeExample name _ _) = testCase name $
tests/Tests.hs+3−5
@@ -1,10 +1,9 @@
1−{-# LANGUAGE NoImplicitPrelude #-}
2−
3module Main (main) where
1module Main (main) where
4
2
3+import Prelude ()
5import Prelude.Compat
4import Prelude.Compat
6
5
7−import Test.Tasty (defaultMain, testGroup)
6+import Test.Framework (defaultMain)
8import qualified DataFamilies.Properties as DF
7import qualified DataFamilies.Properties as DF
9import qualified Properties
8import qualified Properties
10import qualified UnitTests
9import qualified UnitTests
@@ -12,5 +11,4 @@ import qualified UnitTests
12main :: IO ()
11main :: IO ()
13main = do
12main = do
14 ioTests <- UnitTests.ioTests
13 ioTests <- UnitTests.ioTests
15− let allTests = DF.tests : Properties.tests : UnitTests.tests : ioTests
14+ defaultMain (DF.tests : Properties.tests : UnitTests.tests : ioTests)
16− defaultMain (testGroup "tests" allTests)
tests/Types.hs+5−10
@@ -4,21 +4,19 @@
4{-# LANGUAGE FlexibleInstances #-}
4{-# LANGUAGE FlexibleInstances #-}
5{-# LANGUAGE GADTs #-}
5{-# LANGUAGE GADTs #-}
6{-# LANGUAGE GeneralizedNewtypeDeriving #-}
6{-# LANGUAGE GeneralizedNewtypeDeriving #-}
7−{-# LANGUAGE NoImplicitPrelude #-}
8−{-# LANGUAGE ScopedTypeVariables #-}
9{-# LANGUAGE StandaloneDeriving #-}
7{-# LANGUAGE StandaloneDeriving #-}
8+{-# LANGUAGE ScopedTypeVariables #-}
10
9
11module Types (module Types) where
10module Types (module Types) where
12
11
12+import Prelude ()
13import Prelude.Compat
13import Prelude.Compat
14
14
15import Math.NumberTheory.Logarithms (intLog2)
15import Math.NumberTheory.Logarithms (intLog2)
16−import Control.Applicative ((<$>))
17import Data.Data
16import Data.Data
18import Data.Functor.Compose (Compose (..))
17import Data.Functor.Compose (Compose (..))
19import Data.Functor.Identity (Identity (..))
18import Data.Functor.Identity (Identity (..))
20import Data.Hashable (Hashable (..))
19import Data.Hashable (Hashable (..))
21−import Data.Semigroup (Option)
22import Data.Text
20import Data.Text
23import Data.Time (Day (..), fromGregorian)
21import Data.Time (Day (..), fromGregorian)
24import GHC.Generics
22import GHC.Generics
@@ -106,10 +104,6 @@ deriving instance Data (GADT String)
106deriving instance Eq (GADT a)
104deriving instance Eq (GADT a)
107deriving instance Show (GADT a)
105deriving instance Show (GADT a)
108
106
109−newtype MaybeField = MaybeField { maybeField :: Maybe Int }
110−newtype OptionField = OptionField { optionField :: Option Int }
111− deriving (Eq, Show)
112−
113deriving instance Generic Foo
107deriving instance Generic Foo
114deriving instance Generic UFoo
108deriving instance Generic UFoo
115deriving instance Generic OneConstructor
109deriving instance Generic OneConstructor
@@ -119,8 +113,9 @@ deriving instance Generic (Sum4 a b c d)
119deriving instance Generic (Approx a)
113deriving instance Generic (Approx a)
120deriving instance Generic Nullary
114deriving instance Generic Nullary
121deriving instance Generic (SomeType a)
115deriving instance Generic (SomeType a)
116+#if __GLASGOW_HASKELL__ >= 706
122deriving instance Generic1 SomeType
117deriving instance Generic1 SomeType
123−deriving instance Generic OptionField
118+#endif
124deriving instance Generic EitherTextInt
119deriving instance Generic EitherTextInt
125
120
126failure :: Show a => String -> String -> a -> Property
121failure :: Show a => String -> String -> a -> Property
@@ -151,7 +146,7 @@ instance Hashable a => Hashable (LogScaled a) where
151 hashWithSalt salt (LogScaled a) = hashWithSalt salt a
146 hashWithSalt salt (LogScaled a) = hashWithSalt salt a
152
147
153instance Arbitrary a => Arbitrary (LogScaled a) where
148instance Arbitrary a => Arbitrary (LogScaled a) where
154− arbitrary = LogScaled <$> scale (\x -> intLog2 $ x + 1) arbitrary
149+ arbitrary = fmap LogScaled $ scale (\x -> intLog2 $ x + 1) arbitrary
155 shrink = fmap LogScaled . shrink . getLogScaled
150 shrink = fmap LogScaled . shrink . getLogScaled
156
151
157instance ToJSON a => ToJSON (LogScaled a) where
152instance ToJSON a => ToJSON (LogScaled a) where
tests/UnitTests.hs+39−276
@@ -1,72 +1,55 @@
1{-# LANGUAGE CPP #-}
1{-# LANGUAGE CPP #-}
2{-# LANGUAGE DeriveGeneric #-}
2{-# LANGUAGE DeriveGeneric #-}
3{-# LANGUAGE GeneralizedNewtypeDeriving #-}
3{-# LANGUAGE GeneralizedNewtypeDeriving #-}
4−{-# LANGUAGE NoImplicitPrelude #-}
5{-# LANGUAGE OverloadedStrings #-}
4{-# LANGUAGE OverloadedStrings #-}
6{-# LANGUAGE ScopedTypeVariables #-}
5{-# LANGUAGE ScopedTypeVariables #-}
7{-# LANGUAGE TemplateHaskell #-}
6{-# LANGUAGE TemplateHaskell #-}
8−{-# LANGUAGE QuasiQuotes #-}
9
7
10-- For Data.Aeson.Types.camelTo
8-- For Data.Aeson.Types.camelTo
11{-# OPTIONS_GHC -fno-warn-deprecations #-}
9{-# OPTIONS_GHC -fno-warn-deprecations #-}
12
10
13−#if MIN_VERSION_base(4,9,0)
14−{-# OPTIONS_GHC -fno-warn-unused-top-binds #-}
15−#endif
16−
17module UnitTests
11module UnitTests
18 (
12 (
19 ioTests
13 ioTests
20 , tests
14 , tests
21− , withEmbeddedJSONTest
22 ) where
15 ) where
23
16
17+import Prelude ()
24import Prelude.Compat
18import Prelude.Compat
25
19
26import Control.Monad (forM, forM_)
20import Control.Monad (forM, forM_)
27−import Data.Aeson ((.=), (.:), (.:?), (.:!), FromJSON(..), FromJSONKeyFunction(..), FromJSONKey(..), ToJSON1(..), decode, eitherDecode, encode, fromJSON, genericParseJSON, genericToEncoding, genericToJSON, object, withObject, withEmbeddedJSON)
21+import Data.Aeson ((.=), (.:), (.:?), (.:!), FromJSON(..), FromJSONKeyFunction(..), FromJSONKey(..), ToJSON1(..), decode, eitherDecode, encode, fromJSON, genericParseJSON, genericToEncoding, genericToJSON, object, withObject)
28import Data.Aeson.Internal (JSONPathElement(..), formatError)
22import Data.Aeson.Internal (JSONPathElement(..), formatError)
29−import Data.Aeson.QQ.Simple (aesonQQ)
30import Data.Aeson.TH (deriveJSON, deriveToJSON, deriveToJSON1)
23import Data.Aeson.TH (deriveJSON, deriveToJSON, deriveToJSON1)
31import Data.Aeson.Text (encodeToTextBuilder)
24import Data.Aeson.Text (encodeToTextBuilder)
32−import Data.Aeson.Parser
33− ( json, jsonLast, jsonAccum, jsonNoDup
34− , json', jsonLast', jsonAccum', jsonNoDup')
35−import Data.Aeson.Types
36− ( Options(..), Result(Success, Error), ToJSON(..)
37− , Value(Array, Bool, Null, Number, Object, String), camelTo, camelTo2
38− , defaultOptions, formatPath, formatRelativePath, omitNothingFields, parse)
25+import Data.Aeson.Types (Options(..), Result(Success), ToJSON(..), Value(Null), camelTo, camelTo2, defaultOptions, omitNothingFields, parse)
39−import Data.Attoparsec.ByteString (Parser, parseOnly)
40import Data.Char (toUpper)
26import Data.Char (toUpper)
41import Data.Either.Compat (isLeft, isRight)
27import Data.Either.Compat (isLeft, isRight)
42import Data.Hashable (hash)
28import Data.Hashable (hash)
43−import Data.HashMap.Strict (HashMap)
29+import Data.List (sort)
44−import Data.List (sort, isSuffixOf)
45import Data.Maybe (fromMaybe)
30import Data.Maybe (fromMaybe)
46−import Data.Scientific (Scientific, scientific)
31+import Data.Sequence (Seq)
47import Data.Tagged (Tagged(..))
32import Data.Tagged (Tagged(..))
48import Data.Text (Text)
33import Data.Text (Text)
49import Data.Time (UTCTime)
34import Data.Time (UTCTime)
50−import Data.Time.Format.Compat (parseTimeM, defaultTimeLocale)
35+import Data.Time.Format (parseTime)
36+import Data.Time.Locale.Compat (defaultTimeLocale)
51import GHC.Generics (Generic)
37import GHC.Generics (Generic)
52import Instances ()
38import Instances ()
53−import Numeric.Natural (Natural)
54import System.Directory (getDirectoryContents)
39import System.Directory (getDirectoryContents)
55import System.FilePath ((</>), takeExtension, takeFileName)
40import System.FilePath ((</>), takeExtension, takeFileName)
56−import Test.Tasty (TestTree, testGroup)
41+import Test.Framework (Test, testGroup)
42+import Test.Framework.Providers.HUnit (testCase)
57−import Test.Tasty.HUnit (Assertion, assertBool, assertFailure, assertEqual, testCase, (@?=))
43+import Test.HUnit (Assertion, assertBool, assertFailure, assertEqual)
58import Text.Printf (printf)
44import Text.Printf (printf)
59import UnitTests.NullaryConstructors (nullaryConstructors)
45import UnitTests.NullaryConstructors (nullaryConstructors)
60−import qualified Data.ByteString as S
61import qualified Data.ByteString.Base16.Lazy as LBase16
46import qualified Data.ByteString.Base16.Lazy as LBase16
62import qualified Data.ByteString.Lazy.Char8 as L
47import qualified Data.ByteString.Lazy.Char8 as L
63import qualified Data.HashSet as HashSet
48import qualified Data.HashSet as HashSet
64−import qualified Data.HashMap.Lazy as HashMap
65import qualified Data.Text.Lazy as LT
49import qualified Data.Text.Lazy as LT
66import qualified Data.Text.Lazy.Builder as TLB
50import qualified Data.Text.Lazy.Builder as TLB
67import qualified Data.Text.Lazy.Encoding as LT
51import qualified Data.Text.Lazy.Encoding as LT
68import qualified Data.Text.Lazy.Encoding as TLE
52import qualified Data.Text.Lazy.Encoding as TLE
69−import qualified Data.Vector as Vector
70import qualified ErrorMessages
53import qualified ErrorMessages
71import qualified SerializationFormatSpec
54import qualified SerializationFormatSpec
72
55
@@ -74,7 +57,7 @@ import qualified SerializationFormatSpec
74import Data.Aeson.Parser.UnescapeFFI ()
57import Data.Aeson.Parser.UnescapeFFI ()
75import Data.Aeson.Parser.UnescapePure ()
58import Data.Aeson.Parser.UnescapePure ()
76
59
77−tests :: TestTree
60+tests :: Test
78tests = testGroup "unit" [
61tests = testGroup "unit" [
79 testGroup "SerializationFormatSpec" SerializationFormatSpec.tests
62 testGroup "SerializationFormatSpec" SerializationFormatSpec.tests
80 , testGroup "ErrorMessages" ErrorMessages.tests
63 , testGroup "ErrorMessages" ErrorMessages.tests
@@ -101,8 +84,8 @@ tests = testGroup "unit" [
101 testCase "example 1" formatErrorExample
84 testCase "example 1" formatErrorExample
102 ]
85 ]
103 , testGroup ".:, .:?, .:!" $ fmap (testCase "-") dotColonMark
86 , testGroup ".:, .:?, .:!" $ fmap (testCase "-") dotColonMark
87+ , testGroup "JSONPath" $ fmap (testCase "-") jsonPath
104 , testGroup "Hashable laws" $ fmap (testCase "-") hashableLaws
88 , testGroup "Hashable laws" $ fmap (testCase "-") hashableLaws
105− , testGroup "Object construction" $ fmap (testCase "-") objectConstruction
106 , testGroup "Issue #351" $ fmap (testCase "-") issue351
89 , testGroup "Issue #351" $ fmap (testCase "-") issue351
107 , testGroup "Nullary constructors" $ fmap (testCase "-") nullaryConstructors
90 , testGroup "Nullary constructors" $ fmap (testCase "-") nullaryConstructors
108 , testGroup "FromJSONKey" $ fmap (testCase "-") fromJSONKeyAssertions
91 , testGroup "FromJSONKey" $ fmap (testCase "-") fromJSONKeyAssertions
@@ -110,22 +93,6 @@ tests = testGroup "unit" [
110 , testCase "Unescape string (PR #477)" unescapeString
93 , testCase "Unescape string (PR #477)" unescapeString
111 , testCase "Show Options" showOptions
94 , testCase "Show Options" showOptions
112 , testGroup "SingleMaybeField" singleMaybeField
95 , testGroup "SingleMaybeField" singleMaybeField
113− , testCase "withEmbeddedJSON" withEmbeddedJSONTest
114− , testCase "SingleFieldCon" singleFieldCon
115− , testGroup "UnknownFields" unknownFields
116− , testGroup "Ordering of object keys" keyOrdering
117− , testCase "Ratio with denominator 0" ratioDenominator0
118− , testCase "Rational parses number" rationalNumber
119− , testCase "Big rational" bigRationalDecoding
120− , testCase "Small rational" smallRationalDecoding
121− , testCase "Big scientific exponent" bigScientificExponent
122− , testCase "Big integer decoding" bigIntegerDecoding
123− , testCase "Big natural decading" bigNaturalDecoding
124− , testCase "Big integer key decoding" bigIntegerKeyDecoding
125− , testGroup "QQ.Simple"
126− [ testCase "example" $
127− assertEqual "" (object ["foo" .= True]) [aesonQQ| {"foo": true } |]
128− ]
129 ]
96 ]
130
97
131roundTripCamel :: String -> Assertion
98roundTripCamel :: String -> Assertion
@@ -218,18 +185,10 @@ utcTimeGood = do
218 assertEqual "utctime" (parseWithRead "%F %H:%MZ" ts11) t11
185 assertEqual "utctime" (parseWithRead "%F %H:%MZ" ts11) t11
219 assertEqual "utctime" (parseWithRead "%FT%T%QZ" "2015-01-01T14:30:00Z") t12
186 assertEqual "utctime" (parseWithRead "%FT%T%QZ" "2015-01-01T14:30:00Z") t12
220
187
221− -- leap seconds are included correctly
222− let ts13 = "2015-08-23T23:59:60.128+00" :: LT.Text
223− let (Just (t13 :: UTCTime)) = parseWithAeson ts13
224− assertEqual "utctime" (parseWithRead "%FT%T%QZ" "2015-08-23T23:59:60.128Z") t13
225− let ts14 = "2015-08-23T23:59:60.999999999999+00" :: LT.Text
226− let (Just (t14 :: UTCTime)) = parseWithAeson ts14
227− assertEqual "utctime" (parseWithRead "%FT%T%QZ" "2015-08-23T23:59:60.999999999999Z") t14
228−
229 where
188 where
230 parseWithRead :: String -> LT.Text -> UTCTime
189 parseWithRead :: String -> LT.Text -> UTCTime
231 parseWithRead f s =
190 parseWithRead f s =
232− fromMaybe (error "parseTime input malformed") . parseTimeM True defaultTimeLocale f . LT.unpack $ s
191+ fromMaybe (error "parseTime input malformed") . parseTime defaultTimeLocale f . LT.unpack $ s
233 parseWithAeson :: LT.Text -> Maybe UTCTime
192 parseWithAeson :: LT.Text -> Maybe UTCTime
234 parseWithAeson s = decode . LT.encodeUtf8 $ LT.concat ["\"", s, "\""]
193 parseWithAeson s = decode . LT.encodeUtf8 $ LT.concat ["\"", s, "\""]
235
194
@@ -245,7 +204,6 @@ utcTimeBad = do
245 verifyFailParse "2015-01-01T12:30:00.00+00:00Z" -- no Zulu if offset given
204 verifyFailParse "2015-01-01T12:30:00.00+00:00Z" -- no Zulu if offset given
246 verifyFailParse "2015-01-03 12:13:00.Z" -- decimal at the end but no digits
205 verifyFailParse "2015-01-03 12:13:00.Z" -- decimal at the end but no digits
247 verifyFailParse "2015-01-03 12:13.000Z" -- decimal at the end, but no seconds
206 verifyFailParse "2015-01-03 12:13.000Z" -- decimal at the end, but no seconds
248− verifyFailParse "2015-01-03 23:59:61Z" -- exceeds allowed seconds per day
249 where
207 where
250 verifyFailParse (s :: LT.Text) =
208 verifyFailParse (s :: LT.Text) =
251 let (dec :: Maybe UTCTime) = decode . LT.encodeUtf8 $ LT.concat ["\"", s, "\""] in
209 let (dec :: Maybe UTCTime) = decode . LT.encodeUtf8 $ LT.concat ["\"", s, "\""] in
@@ -258,18 +216,6 @@ formatErrorExample =
258 lhs = "Error in $[0].foo.bar['a.b.c']['']['\\'\\\\'].end: error msg"
216 lhs = "Error in $[0].foo.bar['a.b.c']['']['\\'\\\\'].end: error msg"
259 in assertEqual "formatError example" lhs rhs
217 in assertEqual "formatError example" lhs rhs
260
218
261−formatPathExample :: Assertion
262−formatPathExample =
263− let rhs = formatPath [Key "x", Index 0]
264− lhs = "$.x[0]"
265− in assertEqual "formatPath example" lhs rhs
266−
267−formatRelativePathExample :: Assertion
268−formatRelativePathExample =
269− let rhs = formatPath [Key "x", Index 0]
270− lhs = ".x[0]"
271− in assertEqual "formatRelativePath example" lhs rhs
272−
273------------------------------------------------------------------------------
219------------------------------------------------------------------------------
274-- Comparison (.:?) and (.:!)
220-- Comparison (.:?) and (.:!)
275------------------------------------------------------------------------------
221------------------------------------------------------------------------------
@@ -300,6 +246,27 @@ dotColonMark = [
300 ex2 = "{\"value\": 42 }"
246 ex2 = "{\"value\": 42 }"
301 ex3 = "{\"value\": null }"
247 ex3 = "{\"value\": null }"
302
248
249+------------------------------------------------------------------------------
250+-- These tests check that JSONPath is tracked correctly
251+-----------------------------------------------------------------------------
252+
253+jsonPath :: [Assertion]
254+jsonPath = [
255+ -- issue #356
256+ assertEqual "Either"
257+ (Left "Error in $[1].Left[1]: expected Bool, encountered Number")
258+ (eitherDecode "[1,{\"Left\":[2,3]}]"
259+ :: Either String (Int, Either (Int, Bool) ()))
260+ -- issue #358
261+ , assertEqual "Seq a"
262+ (Left "Error in $[2]: expected Int, encountered Boolean")
263+ (eitherDecode "[0,1,true]" :: Either String (Seq Int))
264+ , assertEqual "Wibble"
265+ (Left "Error in $.wibbleInt: expected Int, encountered Boolean")
266+ (eitherDecode "{\"wibbleString\":\"\",\"wibbleInt\":true}"
267+ :: Either String Wibble)
268+ ]
269+
303------------------------------------------------------------------------------
270------------------------------------------------------------------------------
304-- Check that the hashes of two equal Value are the same
271-- Check that the hashes of two equal Value are the same
305------------------------------------------------------------------------------
272------------------------------------------------------------------------------
@@ -312,18 +279,6 @@ hashableLaws = [
312 a = object ["223" .= False, "807882556" .= True]
279 a = object ["223" .= False, "807882556" .= True]
313 b = object ["807882556" .= True, "223" .= False]
280 b = object ["807882556" .= True, "223" .= False]
314
281
315−------------------------------------------------------------------------------
316−-- Check that an alternative way to construct objects works
317−------------------------------------------------------------------------------
318−
319−objectConstruction :: [Assertion]
320−objectConstruction = [
321− assertEqual "Equal objects constructed differently" recommended notRecommended
322− ]
323− where
324− recommended = object ["foo" .= True, "bar" .= (-1 :: Int)]
325− notRecommended = Object (mconcat ["foo" .= True, "bar" .= (-1 :: Int)])
326−
327-------------------------------------------------------------------------------
282-------------------------------------------------------------------------------
328-- ToJSONKey
283-- ToJSONKey
329-------------------------------------------------------------------------------
284-------------------------------------------------------------------------------
@@ -384,13 +339,13 @@ issue351 = [
384-- Comparison between bytestring and text encoders
339-- Comparison between bytestring and text encoders
385------------------------------------------------------------------------------
340------------------------------------------------------------------------------
386
341
387−ioTests :: IO [TestTree]
342+ioTests :: IO [Test]
388ioTests = do
343ioTests = do
389 enc <- encoderComparisonTests
344 enc <- encoderComparisonTests
390 js <- jsonTestSuite
345 js <- jsonTestSuite
391 return [enc, js]
346 return [enc, js]
392
347
393−encoderComparisonTests :: IO TestTree
348+encoderComparisonTests :: IO Test
394encoderComparisonTests = do
349encoderComparisonTests = do
395 encoderTests <- forM testFiles $ \file0 -> do
350 encoderTests <- forM testFiles $ \file0 -> do
396 let file = "benchmarks/json-data/" ++ file0
351 let file = "benchmarks/json-data/" ++ file0
@@ -450,7 +405,7 @@ unescapeString = do
450
405
451-- JSONTestSuite
406-- JSONTestSuite
452
407
453−jsonTestSuiteTest :: FilePath -> TestTree
408+jsonTestSuiteTest :: FilePath -> Test
454jsonTestSuiteTest path = testCase fileName $ do
409jsonTestSuiteTest path = testCase fileName $ do
455 payload <- L.readFile path
410 payload <- L.readFile path
456 let result = eitherDecode payload :: Either String Value
411 let result = eitherDecode payload :: Either String Value
@@ -465,7 +420,7 @@ jsonTestSuiteTest path = testCase fileName $ do
465-- Build a collection of tests based on the current contents of the
420-- Build a collection of tests based on the current contents of the
466-- JSONTestSuite test directories.
421-- JSONTestSuite test directories.
467
422
468−jsonTestSuite :: IO TestTree
423+jsonTestSuite :: IO Test
469jsonTestSuite = do
424jsonTestSuite = do
470 let suitePath = "tests/JSONTestSuite"
425 let suitePath = "tests/JSONTestSuite"
471 let suites = ["test_parsing", "test_transform"]
426 let suites = ["test_parsing", "test_transform"]
@@ -540,14 +495,13 @@ showOptions =
540 ++ ", sumEncoding = TaggedObject {tagFieldName = \"tag\", contentsFieldName = \"contents\"}"
495 ++ ", sumEncoding = TaggedObject {tagFieldName = \"tag\", contentsFieldName = \"contents\"}"
541 ++ ", unwrapUnaryRecords = False"
496 ++ ", unwrapUnaryRecords = False"
542 ++ ", tagSingleConstructors = False"
497 ++ ", tagSingleConstructors = False"
543− ++ ", rejectUnknownFields = False"
544 ++ "}")
498 ++ "}")
545 (show defaultOptions)
499 (show defaultOptions)
546
500
547newtype SingleMaybeField = SingleMaybeField { smf :: Maybe Int }
501newtype SingleMaybeField = SingleMaybeField { smf :: Maybe Int }
548 deriving (Eq, Show, Generic)
502 deriving (Eq, Show, Generic)
549
503
550−singleMaybeField :: [TestTree]
504+singleMaybeField :: [Test]
551singleMaybeField = do
505singleMaybeField = do
552 (gName, gToJSON, gToEncoding, gFromJSON) <-
506 (gName, gToJSON, gToEncoding, gFromJSON) <-
553 [ ("generic", genericToJSON opts, genericToEncoding opts, parse (genericParseJSON opts))
507 [ ("generic", genericToJSON opts, genericToEncoding opts, parse (genericParseJSON opts))
@@ -561,200 +515,9 @@ singleMaybeField = do
561 v = SingleMaybeField Nothing
515 v = SingleMaybeField Nothing
562 opts = defaultOptions{omitNothingFields=True,unwrapUnaryRecords=True}
516 opts = defaultOptions{omitNothingFields=True,unwrapUnaryRecords=True}
563
517
564−
565−newtype EmbeddedJSONTest = EmbeddedJSONTest Int
566− deriving (Eq, Show)
567−
568−instance FromJSON EmbeddedJSONTest where
569− parseJSON =
570− withObject "Object" $ \o ->
571− EmbeddedJSONTest <$> (o .: "prop" >>= withEmbeddedJSON "Quoted Int" parseJSON)
572−
573−withEmbeddedJSONTest :: Assertion
574−withEmbeddedJSONTest =
575− assertEqual "Unquote embedded JSON" (Right $ EmbeddedJSONTest 1) (eitherDecode "{\"prop\":\"1\"}")
576−
577−-- Regression test for https://github.com/bos/aeson/issues/627
578−newtype SingleFieldCon = SingleFieldCon Int deriving (Eq, Show, Generic)
579−
580−instance FromJSON SingleFieldCon where
581− parseJSON = genericParseJSON defaultOptions{unwrapUnaryRecords=True}
582− -- This option should have no effect on this type
583−
584−singleFieldCon :: Assertion
585−singleFieldCon =
586− assertEqual "fromJSON" (Right (SingleFieldCon 0)) (eitherDecode "0")
587−
588−newtype UnknownFields = UnknownFields { knownField :: Int }
589− deriving (Eq, Show, Generic)
590−newtype UnknownFieldsTag = UnknownFieldsTag { tag :: Int }
591− deriving (Eq, Show, Generic)
592−newtype UnknownFieldsUnaryTagged = UnknownFieldsUnaryTagged { knownFieldUnaryTagged :: Int }
593− deriving (Eq, Show, Generic)
594−data UnknownFieldsSum
595− = UnknownFields1 { knownField1 :: Int }
596− | UnknownFields2 { knownField2 :: Int }
597− deriving (Eq, Show, Generic)
598−
599−unknownFields :: [TestTree]
600−unknownFields = concat
601− [ testsUnary
602− "unary-unknown"
603− (object [("knownField", Number 1), ("unknownField", Number 1)])
604− (Error "nknown fields: [\"unknownField\"]" :: Result UnknownFields)
605− , testsUnary
606− "unary-unknown-tag"
607− (object [("knownField", Number 1), ("tag", String "UnknownFields")])
608− (Error "nknown fields: [\"tag\"]" :: Result UnknownFields)
609− , testsUnaryTag
610− "unary-explicit-tag"
611− (object [("tag", Number 1)])
612− (Success $ UnknownFieldsTag 1)
613− , testsSum
614− "sum-tag"
615− (object [("knownField1", Number 1), ("tag", String "UnknownFields1")])
616− (Success $ UnknownFields1 1)
617− , testsSum
618− "sum-unknown-in-branch"
619− (object [("knownField1", Number 1), ("knownField2", Number 1), ("tag", String "UnknownFields1")])
620− (Error "nknown fields: [\"knownField2\"]" :: Result UnknownFieldsSum)
621− , testsSum
622− "sum-unknown"
623− (object [("knownField1", Number 1), ("unknownField", Number 1), ("tag", String "UnknownFields1")])
624− (Error "nknown fields: [\"unknownField\"]" :: Result UnknownFieldsSum)
625− , testsTagged
626− "unary-tagged"
627− (object [("knownFieldUnaryTagged", Number 1), ("tag", String "UnknownFieldsUnaryTagged")])
628− (Success $ UnknownFieldsUnaryTagged 1)
629− , -- Just a case to verify that the tag isn't optional, this is likely already tested by other unit tests
630− testsTagged
631− "unary-tagged-notag"
632− (object [("knownFieldUnaryTagged", Number 1)])
633− (Error "key \"tag\" not found" :: Result UnknownFieldsUnaryTagged)
634− , testsTagged
635− "unary-tagged-unknown"
636− (object [ ("knownFieldUnaryTagged", Number 1), ("unknownField", Number 1)
637− , ("tag", String "UnknownFieldsUnaryTagged")])
638− (Error "nknown fields: [\"unknownField\"]" :: Result UnknownFieldsUnaryTagged)
639− ]
640− where
641− opts = defaultOptions{rejectUnknownFields=True}
642− taggedOpts = opts{tagSingleConstructors=True}
643− assertApprox :: (Show a, Eq a) => Result a -> Result a -> IO ()
644− assertApprox (Error expected) (Error actual) | expected `isSuffixOf` actual = return ()
645− assertApprox expected actual = assertEqual "fromJSON" expected actual
646− testsBase :: (Show a, Eq a) => (Value -> Result a) -> (Value -> Result a)
647− -> String -> Value -> Result a -> [TestTree]
648− testsBase th g name value expected =
649− [ testCase (name ++ "-th") $ assertApprox expected (th value)
650− , testCase (name ++ "-generic") $ assertApprox expected (g value)
651− ]
652− testsUnary :: String -> Value -> Result UnknownFields -> [TestTree]
653− testsUnary = testsBase fromJSON (parse (genericParseJSON opts))
654− testsUnaryTag :: String -> Value -> Result UnknownFieldsTag -> [TestTree]
655− testsUnaryTag = testsBase fromJSON (parse (genericParseJSON opts))
656− testsSum :: String -> Value -> Result UnknownFieldsSum -> [TestTree]
657− testsSum = testsBase fromJSON (parse (genericParseJSON opts))
658− testsTagged :: String -> Value -> Result UnknownFieldsUnaryTagged -> [TestTree]
659− testsTagged = testsBase fromJSON (parse (genericParseJSON taggedOpts))
660−
661−testParser :: (Eq a, Show a)
662− => String -> Parser a -> S.ByteString -> Either String a -> TestTree
663−testParser name json_ s expected =
664− testCase name (parseOnly json_ s @?= expected)
665−
666−keyOrdering :: [TestTree]
667−keyOrdering =
668− [ testParser "json" json
669− "{\"k\":true,\"k\":false}" $
670− Right (Object (HashMap.fromList [("k", Bool True)]))
671− , testParser "jsonLast" jsonLast
672− "{\"k\":true,\"k\":false}" $
673− Right (Object (HashMap.fromList [("k", Bool False)]))
674− , testParser "jsonAccum" jsonAccum
675− "{\"k\":true,\"k\":false}" $
676− Right (Object (HashMap.fromList [("k", Array (Vector.fromList [Bool True, Bool False]))]))
677− , testParser "jsonNoDup" jsonNoDup
678− "{\"k\":true,\"k\":false}" $
679− Left "Failed reading: found duplicate key: \"k\""
680−
681− , testParser "json'" json'
682− "{\"k\":true,\"k\":false}" $
683− Right (Object (HashMap.fromList [("k", Bool True)]))
684− , testParser "jsonLast'" jsonLast'
685− "{\"k\":true,\"k\":false}" $
686− Right (Object (HashMap.fromList [("k", Bool False)]))
687− , testParser "jsonAccum'" jsonAccum'
688− "{\"k\":true,\"k\":false}" $
689− Right (Object (HashMap.fromList [("k", Array (Vector.fromList [Bool True, Bool False]))]))
690− , testParser "jsonNoDup'" jsonNoDup'
691− "{\"k\":true,\"k\":false}" $
692− Left "Failed reading: found duplicate key: \"k\""
693− ]
694−
695−ratioDenominator0 :: Assertion
696−ratioDenominator0 =
697− assertEqual "Ratio with denominator 0"
698− (Left "Error in $: Ratio denominator was 0")
699− (eitherDecode "{ \"numerator\": 1, \"denominator\": 0 }" :: Either String Rational)
700−
701−rationalNumber :: Assertion
702−rationalNumber =
703− assertEqual "Ratio with denominator 0"
704− (Right 1.37)
705− (eitherDecode "1.37" :: Either String Rational)
706−
707−bigRationalDecoding :: Assertion
708−bigRationalDecoding =
709− assertEqual "Decoding an Integer with a large exponent should fail"
710− (Left "Error in $: parsing Ratio failed, found a number with exponent 2000, but it must not be greater than 1024 or less than -1024")
711− ((eitherDecode :: L.ByteString -> Either String Rational) "1e2000")
712−
713−smallRationalDecoding :: Assertion
714−smallRationalDecoding =
715− assertEqual "Decoding an Integer with a large exponent should fail"
716− (Left "Error in $: parsing Ratio failed, found a number with exponent -2000, but it must not be greater than 1024 or less than -1024")
717− ((eitherDecode :: L.ByteString -> Either String Rational) "1e-2000")
718−
719−
720−bigScientificExponent :: Assertion
721−bigScientificExponent =
722− assertEqual "Encoding an integral scientific with a large exponent should normalize it"
723− "1.0e2000"
724− (encode (scientific 1 2000 :: Scientific))
725−
726−bigIntegerDecoding :: Assertion
727−bigIntegerDecoding =
728− assertEqual "Decoding an Integer with a large exponent should fail"
729− (Left "Error in $: parsing Integer failed, found a number with exponent 2000, but it must not be greater than 1024")
730− ((eitherDecode :: L.ByteString -> Either String Integer) "1e2000")
731−
732−bigNaturalDecoding :: Assertion
733−bigNaturalDecoding =
734− assertEqual "Decoding a Natural with a large exponent should fail"
735− (Left "Error in $: parsing Natural failed, found a number with exponent 2000, but it must not be greater than 1024")
736− ((eitherDecode :: L.ByteString -> Either String Natural) "1e2000")
737−
738−bigIntegerKeyDecoding :: Assertion
739−bigIntegerKeyDecoding =
740− assertEqual "Decoding an Integer key with a large exponent should fail"
741− (Left "Error in $['1e2000']: parsing Integer failed, found a number with exponent 2000, but it must not be greater than 1024")
742− ((eitherDecode :: L.ByteString -> Either String (HashMap Integer Value)) "{ \"1e2000\": null }")
743−
744−bigNaturalKeyDecoding :: Assertion
745−bigNaturalKeyDecoding =
746− assertEqual "Decoding an Integer key with a large exponent should fail"
747− (Left "Error in $['1e2000']: found a number with exponent 2000, but it must not be greater than 1024")
748− ((eitherDecode :: L.ByteString -> Either String (HashMap Natural Value)) "{ \"1e2000\": null }")
749−
750deriveJSON defaultOptions{omitNothingFields=True} ''MyRecord
518deriveJSON defaultOptions{omitNothingFields=True} ''MyRecord
751
519
752deriveToJSON defaultOptions ''Foo
520deriveToJSON defaultOptions ''Foo
753deriveToJSON1 defaultOptions ''Foo
521deriveToJSON1 defaultOptions ''Foo
754
522
755deriveJSON defaultOptions{omitNothingFields=True,unwrapUnaryRecords=True} ''SingleMaybeField
523deriveJSON defaultOptions{omitNothingFields=True,unwrapUnaryRecords=True} ''SingleMaybeField
756−
757−deriveJSON defaultOptions{rejectUnknownFields=True} ''UnknownFields
758−deriveJSON defaultOptions{rejectUnknownFields=True} ''UnknownFieldsTag
759−deriveJSON defaultOptions{tagSingleConstructors=True,rejectUnknownFields=True} ''UnknownFieldsUnaryTagged
760−deriveJSON defaultOptions{rejectUnknownFields=True} ''UnknownFieldsSum
tests/UnitTests/NullaryConstructors.hs+3−8
@@ -1,5 +1,4 @@
1−{-# LANGUAGE NoImplicitPrelude #-}
2−{-# LANGUAGE OverloadedStrings #-}
1+{-# LANGUAGE OverloadedStrings #-}
3
2
4{-# OPTIONS_GHC -fno-warn-deprecations #-}
3{-# OPTIONS_GHC -fno-warn-deprecations #-}
5
4
@@ -8,16 +7,16 @@ module UnitTests.NullaryConstructors
8 nullaryConstructors
7 nullaryConstructors
9 ) where
8 ) where
10
9
10+import Prelude ()
11import Prelude.Compat
11import Prelude.Compat
12
12
13import Data.Aeson (decode, eitherDecode, fromEncoding, Value)
13import Data.Aeson (decode, eitherDecode, fromEncoding, Value)
14import Data.Aeson.Internal (IResult (..), iparse)
14import Data.Aeson.Internal (IResult (..), iparse)
15import Data.Aeson.Types (Parser)
15import Data.Aeson.Types (Parser)
16import Data.ByteString.Builder (toLazyByteString)
16import Data.ByteString.Builder (toLazyByteString)
17−import Data.Foldable (for_)
18import Data.Maybe (fromJust)
17import Data.Maybe (fromJust)
19import Encoders
18import Encoders
20−import Test.Tasty.HUnit ((@=?), Assertion)
19+import Test.HUnit ((@=?), Assertion)
21import Types
20import Types
22import qualified Data.ByteString.Lazy.Char8 as L
21import qualified Data.ByteString.Lazy.Char8 as L
23
22
@@ -53,10 +52,6 @@ nullaryConstructors =
53 -- Make sure that the old `"contents" : []' is still allowed
52 -- Make sure that the old `"contents" : []' is still allowed
54 , ISuccess C1 @=? parse thNullaryParseJSONTaggedObject (dec "{\"tag\":\"c1\",\"contents\":[]}")
53 , ISuccess C1 @=? parse thNullaryParseJSONTaggedObject (dec "{\"tag\":\"c1\",\"contents\":[]}")
55 , ISuccess C1 @=? parse gNullaryParseJSONTaggedObject (dec "{\"tag\":\"c1\",\"contents\":[]}")
54 , ISuccess C1 @=? parse gNullaryParseJSONTaggedObject (dec "{\"tag\":\"c1\",\"contents\":[]}")
56−
57− , for_ [("kC1", C1), ("kC2", C2), ("kC3", C3)] $ \(jkey, key) -> do
58− Right jkey @=? gNullaryToJSONKey key
59− ISuccess key @=? parse gNullaryFromJSONKey jkey
60 ]
55 ]
61 where
56 where
62 enc = eitherDecode . toLazyByteString . fromEncoding
57 enc = eitherDecode . toLazyByteString . fromEncoding
tests/golden/generic.expecteddeleted−48
@@ -1,48 +0,0 @@
1−OneConstructor
2−Error in $: parsing Types.OneConstructor(OneConstructor) failed, expected Array, but encountered String
3−Error in $: parsing Types.OneConstructor(OneConstructor) failed, expected an empty Array, but encountered an Array of length 1
4−Nullary
5−Error in $: parsing Types.Nullary failed, expected one of the tags ["C1","C2","C3"], but found tag "X"
6−Error in $: parsing Types.Nullary failed, expected String, but encountered Array
7−SomeType (tagged)
8−Error in $.contents: parsing Int failed, expected Number, but encountered Boolean
9−Error in $: parsing Types.SomeType(Unary) failed, key "contents" not found
10−Error in $: parsing Types.SomeType(Record) failed, key "testone" not found
11−Error in $.testone: parsing Double failed, unexpected Boolean
12−Error in $.tag: parsing Types.SomeType failed, expected tag field to be one of ["nullary","unary","product","record","list"], but found tag "X"
13−Error in $: parsing Types.SomeType failed, expected Object with key "tag" containing one of ["nullary","unary","product","record","list"], key "tag" not found
14−Error in $: parsing Types.SomeType failed, expected Object, but encountered Array
15−SomeType (single-field)
16−Error in $.unary: parsing Int failed, expected Number, but encountered Object
17−Error in $.unary: parsing Int failed, expected Number, but encountered Array
18−Error in $: parsing Types.SomeType failed, expected an Object with a single pair where the tag is one of ["nullary","unary","product","record","list"], but found tag "X"
19−Error in $: parsing Types.SomeType failed, expected an Object with a single pair, but found 2 pairs
20−Error in $: parsing Types.SomeType failed, expected an Object with a single pair, but found 0 pairs
21−Error in $: parsing Types.SomeType failed, expected Object, but encountered Array
22−Error in $: not enough input. Expecting ':'
23−Error in $: not enough input. Expecting object value
24−Error in $: not enough input. Expecting ',' or '}'
25−SomeType (two-element array)
26−Error in $[1]: parsing Int failed, expected Number, but encountered Boolean
27−Error in $[1]: parsing Types.SomeType(Record) failed, expected Object, but encountered Null
28−Error in $[0]: parsing Types.SomeType failed, expected tag of the 2-element Array to be one of ["nullary","unary","product","record","list"], but found tag "X"
29−Error in $[0]: parsing Types.SomeType failed, tag element is not a String
30−Error in $: parsing Types.SomeType failed, expected a 2-element Array, but encountered an Array of length 0
31−Error in $: parsing Types.SomeType failed, expected Array, but encountered Object
32−Error in $: not enough input. Expecting ',' or ']'
33−Error in $: not enough input. Expecting json list value
34−SomeType (reject unknown fields)
35−Error in $: parsing Types.SomeType(Record) failed, unknown fields: ["testZero"]
36−Error in $: parsing Types.SomeType failed, expected Object with key "tag" containing one of ["nullary","unary","product","record","list"], key "tag" not found
37−Error in $: parsing Types.SomeType(Record) failed, unknown fields: ["testtwo","testone","testthree"]
38−Foo (reject unknown fields)
39−Error in $: parsing Types.Foo(Foo) failed, unknown fields: ["tag"]
40−Foo (reject unknown fields, tagged single)
41−Error in $: parsing Types.Foo(Foo) failed, unknown fields: ["unknownField"]
42−EitherTextInt
43−Error in $: parsing Types.EitherTextInt(NoneNullary) failed, expected tag "nonenullary", but found tag "X"
44−Error in $: parsing Types.EitherTextInt(NoneNullary) failed, expected String, but encountered Array
45−Product2 Int Bool
46−Error in $[1]: expected Bool, but encountered Null
47−Error in $: parsing Types.Product2(Product2) failed, expected an Array of length 2, but encountered an Array of length 0
48−Error in $: parsing Types.Product2(Product2) failed, expected Array, but encountered Object
tests/golden/simple.expecteddeleted−22
@@ -1,22 +0,0 @@
1−Int
2−Error in $: parsing Int failed, expected Number, but encountered String
3−Error in $: parsing Int failed, expected Number, but encountered Array
4−Error in $: parsing Int failed, expected Number, but encountered Object
5−Error in $: parsing Int failed, expected Number, but encountered Null
6−Integer
7−Error in $: parsing Integer failed, unexpected floating number 44.44
8−Natural
9−Error in $: parsing Natural failed, unexpected floating number 44.44
10−Error in $: parsing Natural failed, unexpected negative number -50
11−String
12−Error in $: expected String, but encountered Number
13−Error in $: expected String, but encountered Array
14−Error in $: expected String, but encountered Object
15−Error in $: expected String, but encountered Null
16−HashMap
17−Error in $: parsing HashMap failed, expected Object, but encountered String
18−Error in $: parsing HashMap failed, expected Object, but encountered Array
19−Either
20−Error in $[1].Left[1]: expected Bool, but encountered Number
21−Seq
22−Error in $[2]: parsing Int failed, expected Number, but encountered Boolean
travis/cabal_macros.hdeleted−448
@@ -1,448 +0,0 @@
1−/* DO NOT EDIT: This file is automatically generated by Cabal */
2−
3−/* package aeson-1.4.4.0 */
4−#ifndef VERSION_aeson
5−#define VERSION_aeson "1.4.4.0"
6−#endif /* VERSION_aeson */
7−#ifndef MIN_VERSION_aeson
8−#define MIN_VERSION_aeson(major1,major2,minor) (\
9− (major1) < 1 || \
10− (major1) == 1 && (major2) < 4 || \
11− (major1) == 1 && (major2) == 4 && (minor) <= 4)
12−#endif /* MIN_VERSION_aeson */
13−
14−/* package Diff-0.3.4 */
15−#ifndef VERSION_Diff
16−#define VERSION_Diff "0.3.4"
17−#endif /* VERSION_Diff */
18−#ifndef MIN_VERSION_Diff
19−#define MIN_VERSION_Diff(major1,major2,minor) (\
20− (major1) < 0 || \
21− (major1) == 0 && (major2) < 3 || \
22− (major1) == 0 && (major2) == 3 && (minor) <= 4)
23−#endif /* MIN_VERSION_Diff */
24−
25−/* package QuickCheck-2.13.1 */
26−#ifndef VERSION_QuickCheck
27−#define VERSION_QuickCheck "2.13.1"
28−#endif /* VERSION_QuickCheck */
29−#ifndef MIN_VERSION_QuickCheck
30−#define MIN_VERSION_QuickCheck(major1,major2,minor) (\
31− (major1) < 2 || \
32− (major1) == 2 && (major2) < 13 || \
33− (major1) == 2 && (major2) == 13 && (minor) <= 1)
34−#endif /* MIN_VERSION_QuickCheck */
35−
36−/* package aeson-1.4.4.0 */
37−#ifndef VERSION_aeson
38−#define VERSION_aeson "1.4.4.0"
39−#endif /* VERSION_aeson */
40−#ifndef MIN_VERSION_aeson
41−#define MIN_VERSION_aeson(major1,major2,minor) (\
42− (major1) < 1 || \
43− (major1) == 1 && (major2) < 4 || \
44− (major1) == 1 && (major2) == 4 && (minor) <= 4)
45−#endif /* MIN_VERSION_aeson */
46−
47−/* package attoparsec-0.13.2.2 */
48−#ifndef VERSION_attoparsec
49−#define VERSION_attoparsec "0.13.2.2"
50−#endif /* VERSION_attoparsec */
51−#ifndef MIN_VERSION_attoparsec
52−#define MIN_VERSION_attoparsec(major1,major2,minor) (\
53− (major1) < 0 || \
54− (major1) == 0 && (major2) < 13 || \
55− (major1) == 0 && (major2) == 13 && (minor) <= 2)
56−#endif /* MIN_VERSION_attoparsec */
57−
58−/* package base-4.12.0.0 */
59−#ifndef VERSION_base
60−#define VERSION_base "4.12.0.0"
61−#endif /* VERSION_base */
62−#ifndef MIN_VERSION_base
63−#define MIN_VERSION_base(major1,major2,minor) (\
64− (major1) < 4 || \
65− (major1) == 4 && (major2) < 12 || \
66− (major1) == 4 && (major2) == 12 && (minor) <= 0)
67−#endif /* MIN_VERSION_base */
68−
69−/* package base-compat-0.10.5 */
70−#ifndef VERSION_base_compat
71−#define VERSION_base_compat "0.10.5"
72−#endif /* VERSION_base_compat */
73−#ifndef MIN_VERSION_base_compat
74−#define MIN_VERSION_base_compat(major1,major2,minor) (\
75− (major1) < 0 || \
76− (major1) == 0 && (major2) < 10 || \
77− (major1) == 0 && (major2) == 10 && (minor) <= 5)
78−#endif /* MIN_VERSION_base_compat */
79−
80−/* package base-orphans-0.8.1 */
81−#ifndef VERSION_base_orphans
82−#define VERSION_base_orphans "0.8.1"
83−#endif /* VERSION_base_orphans */
84−#ifndef MIN_VERSION_base_orphans
85−#define MIN_VERSION_base_orphans(major1,major2,minor) (\
86− (major1) < 0 || \
87− (major1) == 0 && (major2) < 8 || \
88− (major1) == 0 && (major2) == 8 && (minor) <= 1)
89−#endif /* MIN_VERSION_base_orphans */
90−
91−/* package base16-bytestring-0.1.1.6 */
92−#ifndef VERSION_base16_bytestring
93−#define VERSION_base16_bytestring "0.1.1.6"
94−#endif /* VERSION_base16_bytestring */
95−#ifndef MIN_VERSION_base16_bytestring
96−#define MIN_VERSION_base16_bytestring(major1,major2,minor) (\
97− (major1) < 0 || \
98− (major1) == 0 && (major2) < 1 || \
99− (major1) == 0 && (major2) == 1 && (minor) <= 1)
100−#endif /* MIN_VERSION_base16_bytestring */
101−
102−/* package bytestring-0.10.8.2 */
103−#ifndef VERSION_bytestring
104−#define VERSION_bytestring "0.10.8.2"
105−#endif /* VERSION_bytestring */
106−#ifndef MIN_VERSION_bytestring
107−#define MIN_VERSION_bytestring(major1,major2,minor) (\
108− (major1) < 0 || \
109− (major1) == 0 && (major2) < 10 || \
110− (major1) == 0 && (major2) == 10 && (minor) <= 8)
111−#endif /* MIN_VERSION_bytestring */
112−
113−/* package containers-0.6.0.1 */
114−#ifndef VERSION_containers
115−#define VERSION_containers "0.6.0.1"
116−#endif /* VERSION_containers */
117−#ifndef MIN_VERSION_containers
118−#define MIN_VERSION_containers(major1,major2,minor) (\
119− (major1) < 0 || \
120− (major1) == 0 && (major2) < 6 || \
121− (major1) == 0 && (major2) == 6 && (minor) <= 0)
122−#endif /* MIN_VERSION_containers */
123−
124−/* package directory-1.3.3.0 */
125−#ifndef VERSION_directory
126−#define VERSION_directory "1.3.3.0"
127−#endif /* VERSION_directory */
128−#ifndef MIN_VERSION_directory
129−#define MIN_VERSION_directory(major1,major2,minor) (\
130− (major1) < 1 || \
131− (major1) == 1 && (major2) < 3 || \
132− (major1) == 1 && (major2) == 3 && (minor) <= 3)
133−#endif /* MIN_VERSION_directory */
134−
135−/* package dlist-0.8.0.6 */
136−#ifndef VERSION_dlist
137−#define VERSION_dlist "0.8.0.6"
138−#endif /* VERSION_dlist */
139−#ifndef MIN_VERSION_dlist
140−#define MIN_VERSION_dlist(major1,major2,minor) (\
141− (major1) < 0 || \
142− (major1) == 0 && (major2) < 8 || \
143− (major1) == 0 && (major2) == 8 && (minor) <= 0)
144−#endif /* MIN_VERSION_dlist */
145−
146−/* package filepath-1.4.2.1 */
147−#ifndef VERSION_filepath
148−#define VERSION_filepath "1.4.2.1"
149−#endif /* VERSION_filepath */
150−#ifndef MIN_VERSION_filepath
151−#define MIN_VERSION_filepath(major1,major2,minor) (\
152− (major1) < 1 || \
153− (major1) == 1 && (major2) < 4 || \
154− (major1) == 1 && (major2) == 4 && (minor) <= 2)
155−#endif /* MIN_VERSION_filepath */
156−
157−/* package generic-deriving-1.12.4 */
158−#ifndef VERSION_generic_deriving
159−#define VERSION_generic_deriving "1.12.4"
160−#endif /* VERSION_generic_deriving */
161−#ifndef MIN_VERSION_generic_deriving
162−#define MIN_VERSION_generic_deriving(major1,major2,minor) (\
163− (major1) < 1 || \
164− (major1) == 1 && (major2) < 12 || \
165− (major1) == 1 && (major2) == 12 && (minor) <= 4)
166−#endif /* MIN_VERSION_generic_deriving */
167−
168−/* package ghc-prim-0.5.3 */
169−#ifndef VERSION_ghc_prim
170−#define VERSION_ghc_prim "0.5.3"
171−#endif /* VERSION_ghc_prim */
172−#ifndef MIN_VERSION_ghc_prim
173−#define MIN_VERSION_ghc_prim(major1,major2,minor) (\
174− (major1) < 0 || \
175− (major1) == 0 && (major2) < 5 || \
176− (major1) == 0 && (major2) == 5 && (minor) <= 3)
177−#endif /* MIN_VERSION_ghc_prim */
178−
179−/* package hashable-1.3.0.0 */
180−#ifndef VERSION_hashable
181−#define VERSION_hashable "1.3.0.0"
182−#endif /* VERSION_hashable */
183−#ifndef MIN_VERSION_hashable
184−#define MIN_VERSION_hashable(major1,major2,minor) (\
185− (major1) < 1 || \
186− (major1) == 1 && (major2) < 3 || \
187− (major1) == 1 && (major2) == 3 && (minor) <= 0)
188−#endif /* MIN_VERSION_hashable */
189−
190−/* package hashable-time-0.2.0.2 */
191−#ifndef VERSION_hashable_time
192−#define VERSION_hashable_time "0.2.0.2"
193−#endif /* VERSION_hashable_time */
194−#ifndef MIN_VERSION_hashable_time
195−#define MIN_VERSION_hashable_time(major1,major2,minor) (\
196− (major1) < 0 || \
197− (major1) == 0 && (major2) < 2 || \
198− (major1) == 0 && (major2) == 2 && (minor) <= 0)
199−#endif /* MIN_VERSION_hashable_time */
200−
201−/* package integer-logarithms-1.0.3 */
202−#ifndef VERSION_integer_logarithms
203−#define VERSION_integer_logarithms "1.0.3"
204−#endif /* VERSION_integer_logarithms */
205−#ifndef MIN_VERSION_integer_logarithms
206−#define MIN_VERSION_integer_logarithms(major1,major2,minor) (\
207− (major1) < 1 || \
208− (major1) == 1 && (major2) < 0 || \
209− (major1) == 1 && (major2) == 0 && (minor) <= 3)
210−#endif /* MIN_VERSION_integer_logarithms */
211−
212−/* package quickcheck-instances-0.3.21 */
213−#ifndef VERSION_quickcheck_instances
214−#define VERSION_quickcheck_instances "0.3.21"
215−#endif /* VERSION_quickcheck_instances */
216−#ifndef MIN_VERSION_quickcheck_instances
217−#define MIN_VERSION_quickcheck_instances(major1,major2,minor) (\
218− (major1) < 0 || \
219− (major1) == 0 && (major2) < 3 || \
220− (major1) == 0 && (major2) == 3 && (minor) <= 21)
221−#endif /* MIN_VERSION_quickcheck_instances */
222−
223−/* package scientific-0.3.6.2 */
224−#ifndef VERSION_scientific
225−#define VERSION_scientific "0.3.6.2"
226−#endif /* VERSION_scientific */
227−#ifndef MIN_VERSION_scientific
228−#define MIN_VERSION_scientific(major1,major2,minor) (\
229− (major1) < 0 || \
230− (major1) == 0 && (major2) < 3 || \
231− (major1) == 0 && (major2) == 3 && (minor) <= 6)
232−#endif /* MIN_VERSION_scientific */
233−
234−/* package tagged-0.8.6 */
235−#ifndef VERSION_tagged
236−#define VERSION_tagged "0.8.6"
237−#endif /* VERSION_tagged */
238−#ifndef MIN_VERSION_tagged
239−#define MIN_VERSION_tagged(major1,major2,minor) (\
240− (major1) < 0 || \
241− (major1) == 0 && (major2) < 8 || \
242− (major1) == 0 && (major2) == 8 && (minor) <= 6)
243−#endif /* MIN_VERSION_tagged */
244−
245−/* package tasty-1.2.3 */
246−#ifndef VERSION_tasty
247−#define VERSION_tasty "1.2.3"
248−#endif /* VERSION_tasty */
249−#ifndef MIN_VERSION_tasty
250−#define MIN_VERSION_tasty(major1,major2,minor) (\
251− (major1) < 1 || \
252− (major1) == 1 && (major2) < 2 || \
253− (major1) == 1 && (major2) == 2 && (minor) <= 3)
254−#endif /* MIN_VERSION_tasty */
255−
256−/* package tasty-golden-2.3.2 */
257−#ifndef VERSION_tasty_golden
258−#define VERSION_tasty_golden "2.3.2"
259−#endif /* VERSION_tasty_golden */
260−#ifndef MIN_VERSION_tasty_golden
261−#define MIN_VERSION_tasty_golden(major1,major2,minor) (\
262− (major1) < 2 || \
263− (major1) == 2 && (major2) < 3 || \
264− (major1) == 2 && (major2) == 3 && (minor) <= 2)
265−#endif /* MIN_VERSION_tasty_golden */
266−
267−/* package tasty-hunit-0.10.0.2 */
268−#ifndef VERSION_tasty_hunit
269−#define VERSION_tasty_hunit "0.10.0.2"
270−#endif /* VERSION_tasty_hunit */
271−#ifndef MIN_VERSION_tasty_hunit
272−#define MIN_VERSION_tasty_hunit(major1,major2,minor) (\
273− (major1) < 0 || \
274− (major1) == 0 && (major2) < 10 || \
275− (major1) == 0 && (major2) == 10 && (minor) <= 0)
276−#endif /* MIN_VERSION_tasty_hunit */
277−
278−/* package tasty-quickcheck-0.10.1 */
279−#ifndef VERSION_tasty_quickcheck
280−#define VERSION_tasty_quickcheck "0.10.1"
281−#endif /* VERSION_tasty_quickcheck */
282−#ifndef MIN_VERSION_tasty_quickcheck
283−#define MIN_VERSION_tasty_quickcheck(major1,major2,minor) (\
284− (major1) < 0 || \
285− (major1) == 0 && (major2) < 10 || \
286− (major1) == 0 && (major2) == 10 && (minor) <= 1)
287−#endif /* MIN_VERSION_tasty_quickcheck */
288−
289−/* package template-haskell-2.14.0.0 */
290−#ifndef VERSION_template_haskell
291−#define VERSION_template_haskell "2.14.0.0"
292−#endif /* VERSION_template_haskell */
293−#ifndef MIN_VERSION_template_haskell
294−#define MIN_VERSION_template_haskell(major1,major2,minor) (\
295− (major1) < 2 || \
296− (major1) == 2 && (major2) < 14 || \
297− (major1) == 2 && (major2) == 14 && (minor) <= 0)
298−#endif /* MIN_VERSION_template_haskell */
299−
300−/* package text-1.2.3.1 */
301−#ifndef VERSION_text
302−#define VERSION_text "1.2.3.1"
303−#endif /* VERSION_text */
304−#ifndef MIN_VERSION_text
305−#define MIN_VERSION_text(major1,major2,minor) (\
306− (major1) < 1 || \
307− (major1) == 1 && (major2) < 2 || \
308− (major1) == 1 && (major2) == 2 && (minor) <= 3)
309−#endif /* MIN_VERSION_text */
310−
311−/* package time-1.8.0.2 */
312−#ifndef VERSION_time
313−#define VERSION_time "1.8.0.2"
314−#endif /* VERSION_time */
315−#ifndef MIN_VERSION_time
316−#define MIN_VERSION_time(major1,major2,minor) (\
317− (major1) < 1 || \
318− (major1) == 1 && (major2) < 8 || \
319− (major1) == 1 && (major2) == 8 && (minor) <= 0)
320−#endif /* MIN_VERSION_time */
321−
322−/* package time-compat-1.9.2.2 */
323−#ifndef VERSION_time_compat
324−#define VERSION_time_compat "1.9.2.2"
325−#endif /* VERSION_time_compat */
326−#ifndef MIN_VERSION_time_compat
327−#define MIN_VERSION_time_compat(major1,major2,minor) (\
328− (major1) < 1 || \
329− (major1) == 1 && (major2) < 9 || \
330− (major1) == 1 && (major2) == 9 && (minor) <= 2)
331−#endif /* MIN_VERSION_time_compat */
332−
333−/* package unordered-containers-0.2.10.0 */
334−#ifndef VERSION_unordered_containers
335−#define VERSION_unordered_containers "0.2.10.0"
336−#endif /* VERSION_unordered_containers */
337−#ifndef MIN_VERSION_unordered_containers
338−#define MIN_VERSION_unordered_containers(major1,major2,minor) (\
339− (major1) < 0 || \
340− (major1) == 0 && (major2) < 2 || \
341− (major1) == 0 && (major2) == 2 && (minor) <= 10)
342−#endif /* MIN_VERSION_unordered_containers */
343−
344−/* package uuid-types-1.0.3 */
345−#ifndef VERSION_uuid_types
346−#define VERSION_uuid_types "1.0.3"
347−#endif /* VERSION_uuid_types */
348−#ifndef MIN_VERSION_uuid_types
349−#define MIN_VERSION_uuid_types(major1,major2,minor) (\
350− (major1) < 1 || \
351− (major1) == 1 && (major2) < 0 || \
352− (major1) == 1 && (major2) == 0 && (minor) <= 3)
353−#endif /* MIN_VERSION_uuid_types */
354−
355−/* package vector-0.12.0.3 */
356−#ifndef VERSION_vector
357−#define VERSION_vector "0.12.0.3"
358−#endif /* VERSION_vector */
359−#ifndef MIN_VERSION_vector
360−#define MIN_VERSION_vector(major1,major2,minor) (\
361− (major1) < 0 || \
362− (major1) == 0 && (major2) < 12 || \
363− (major1) == 0 && (major2) == 12 && (minor) <= 0)
364−#endif /* MIN_VERSION_vector */
365−
366−/* tool gcc-4.2.1 */
367−#ifndef TOOL_VERSION_gcc
368−#define TOOL_VERSION_gcc "4.2.1"
369−#endif /* TOOL_VERSION_gcc */
370−#ifndef MIN_TOOL_VERSION_gcc
371−#define MIN_TOOL_VERSION_gcc(major1,major2,minor) (\
372− (major1) < 4 || \
373− (major1) == 4 && (major2) < 2 || \
374− (major1) == 4 && (major2) == 2 && (minor) <= 1)
375−#endif /* MIN_TOOL_VERSION_gcc */
376−
377−/* tool ghc-8.6.5 */
378−#ifndef TOOL_VERSION_ghc
379−#define TOOL_VERSION_ghc "8.6.5"
380−#endif /* TOOL_VERSION_ghc */
381−#ifndef MIN_TOOL_VERSION_ghc
382−#define MIN_TOOL_VERSION_ghc(major1,major2,minor) (\
383− (major1) < 8 || \
384− (major1) == 8 && (major2) < 6 || \
385− (major1) == 8 && (major2) == 6 && (minor) <= 5)
386−#endif /* MIN_TOOL_VERSION_ghc */
387−
388−/* tool ghc-pkg-8.6.5 */
389−#ifndef TOOL_VERSION_ghc_pkg
390−#define TOOL_VERSION_ghc_pkg "8.6.5"
391−#endif /* TOOL_VERSION_ghc_pkg */
392−#ifndef MIN_TOOL_VERSION_ghc_pkg
393−#define MIN_TOOL_VERSION_ghc_pkg(major1,major2,minor) (\
394− (major1) < 8 || \
395− (major1) == 8 && (major2) < 6 || \
396− (major1) == 8 && (major2) == 6 && (minor) <= 5)
397−#endif /* MIN_TOOL_VERSION_ghc_pkg */
398−
399−/* tool haddock-2.22.0 */
400−#ifndef TOOL_VERSION_haddock
401−#define TOOL_VERSION_haddock "2.22.0"
402−#endif /* TOOL_VERSION_haddock */
403−#ifndef MIN_TOOL_VERSION_haddock
404−#define MIN_TOOL_VERSION_haddock(major1,major2,minor) (\
405− (major1) < 2 || \
406− (major1) == 2 && (major2) < 22 || \
407− (major1) == 2 && (major2) == 22 && (minor) <= 0)
408−#endif /* MIN_TOOL_VERSION_haddock */
409−
410−/* tool hpc-0.67 */
411−#ifndef TOOL_VERSION_hpc
412−#define TOOL_VERSION_hpc "0.67"
413−#endif /* TOOL_VERSION_hpc */
414−#ifndef MIN_TOOL_VERSION_hpc
415−#define MIN_TOOL_VERSION_hpc(major1,major2,minor) (\
416− (major1) < 0 || \
417− (major1) == 0 && (major2) < 67 || \
418− (major1) == 0 && (major2) == 67 && (minor) <= 0)
419−#endif /* MIN_TOOL_VERSION_hpc */
420−
421−/* tool hsc2hs-0.68.5 */
422−#ifndef TOOL_VERSION_hsc2hs
423−#define TOOL_VERSION_hsc2hs "0.68.5"
424−#endif /* TOOL_VERSION_hsc2hs */
425−#ifndef MIN_TOOL_VERSION_hsc2hs
426−#define MIN_TOOL_VERSION_hsc2hs(major1,major2,minor) (\
427− (major1) < 0 || \
428− (major1) == 0 && (major2) < 68 || \
429− (major1) == 0 && (major2) == 68 && (minor) <= 5)
430−#endif /* MIN_TOOL_VERSION_hsc2hs */
431−
432−/* tool runghc-8.6.5 */
433−#ifndef TOOL_VERSION_runghc
434−#define TOOL_VERSION_runghc "8.6.5"
435−#endif /* TOOL_VERSION_runghc */
436−#ifndef MIN_TOOL_VERSION_runghc
437−#define MIN_TOOL_VERSION_runghc(major1,major2,minor) (\
438− (major1) < 8 || \
439− (major1) == 8 && (major2) < 6 || \
440− (major1) == 8 && (major2) == 6 && (minor) <= 5)
441−#endif /* MIN_TOOL_VERSION_runghc */
442−
443−#ifndef CURRENT_COMPONENT_ID
444−#define CURRENT_COMPONENT_ID "aeson-1.4.4.0-inplace-tests"
445−#endif /* CURRENT_COMPONENT_ID */
446−#ifndef CURRENT_PACKAGE_VERSION
447−#define CURRENT_PACKAGE_VERSION "1.4.4.0"
448−#endif /* CURRENT_PACKAGE_VERSION */
travis/install.sh−11
@@ -7,11 +7,6 @@ echo "$(ghc --version) [$(ghc --print-project-git-commit-id 2> /dev/null || echo
7stack --version
7stack --version
8
8
9case $BUILD in
9case $BUILD in
10− hlint)
11− echo "Downloading hlint"
12− curl -sSL https://raw.github.com/ndmitchell/hlint/master/misc/run.sh > hlint.sh
13− chmod +x hlint.sh
14− ;;
15 stack)
10 stack)
16 stack --no-terminal --skip-ghc-check setup
11 stack --no-terminal --skip-ghc-check setup
17 stack --no-terminal --skip-ghc-check test --only-snapshot
12 stack --no-terminal --skip-ghc-check test --only-snapshot
@@ -49,10 +44,4 @@ case $BUILD in
49 cp -a $HOME/.cabal/lib $HOME/.cabal/share $HOME/.cabal/bin installplan.txt $HOME/.cabsnap/;
44 cp -a $HOME/.cabal/lib $HOME/.cabal/share $HOME/.cabal/bin installplan.txt $HOME/.cabsnap/;
50 fi
45 fi
51 ;;
46 ;;
52− cabal2)
53− cabal v2-update -v
54− sed -i 's/^jobs:/-- jobs:/' ${HOME}/.cabal/config
55−
56− cabal v2-build --only-dependencies --enable-tests --enable-benchmarks --dry
57− ;;
58esac
47esac
travis/script.sh+2−4
@@ -19,11 +19,9 @@ case $BUILD in
19 SRC_TGZ=$(cabal info . | awk '{print $2;exit}').tar.gz &&
19 SRC_TGZ=$(cabal info . | awk '{print $2;exit}').tar.gz &&
20 (cd dist && cabal install --force-reinstalls "$SRC_TGZ")
20 (cd dist && cabal install --force-reinstalls "$SRC_TGZ")
21 ;;
21 ;;
22− cabal2)
23− cabal v2-build --enable-tests --enable-benchmarks
24− cabal v2-test --enable-tests --enable-benchmarks
25− ;;
26 hlint)
22 hlint)
23+ stack build --fast aeson --stack-yaml stack-lts8.yaml --system-ghc --no-terminal
24+ stack install hlint-2.0.5 --stack-yaml stack-lts8.yaml --system-ghc --no-terminal
27 make lint
25 make lint
28 ;;
26 ;;
29esac
27esac