packages feed

waargonaut 0.6.2.0 → 0.8.0.0

raw patch · 42 files changed

+905/−284 lines, 42 filesdep +hw-json-standard-cursordep +records-sopdep −hw-jsondep ~digitdep ~hedgehogdep ~hedgehog-fnPVP ok

version bump matches the API change (PVP)

Dependencies added: hw-json-standard-cursor, records-sop

Dependencies removed: hw-json

Dependency ranges changed: digit, hedgehog, hedgehog-fn, hw-balancedparens, tasty-hedgehog, zippers

API changes (from Hackage documentation)

- Waargonaut.Decode: type SuccinctCursor = JsonCursor ByteString Poppy512 (SimpleBalancedParens (Vector Word64))
- Waargonaut.Decode.Types: instance HaskellWorks.Data.Json.Type.JsonTypeAt Waargonaut.Decode.Types.JCurs
- Waargonaut.Decode.Types: type SuccinctCursor = JsonCursor ByteString Poppy512 (SimpleBalancedParens (Vector Word64))
+ Waargonaut.Decode: type Cursor = GenericCursor ByteString CsPoppy1 RangeMin CsPoppy1
+ Waargonaut.Decode.Types: instance HaskellWorks.Data.Json.Standard.Cursor.Type.JsonTypeAt Waargonaut.Decode.Types.JCurs
+ Waargonaut.Decode.Types: type Cursor = GenericCursor ByteString CsPoppy1 RangeMin CsPoppy1
+ Waargonaut.Encode: onObj' :: Text -> b -> Encoder' b -> JObject WS Json -> JObject WS Json
+ Waargonaut.Generic: FieldNameAsKey :: NewtypeName
+ Waargonaut.Generic: gObjEncoder :: forall t a f xs. (Generic a, Applicative f, HasDatatypeInfo a, All2 (JsonEncode t) (Code a), IsRecord a xs) => Options -> Tagged t (ObjEncoder f a)
- Waargonaut.Decode: JCurs :: SuccinctCursor -> JCurs
+ Waargonaut.Decode: JCurs :: Cursor -> JCurs
- Waargonaut.Decode: [unJCurs] :: JCurs -> SuccinctCursor
+ Waargonaut.Decode: [unJCurs] :: JCurs -> Cursor
- Waargonaut.Decode: cursorRankL :: Lens' (JsonCursor s i p) Count
+ Waargonaut.Decode: cursorRankL :: Lens' Cursor Count
- Waargonaut.Decode.Types: JCurs :: SuccinctCursor -> JCurs
+ Waargonaut.Decode.Types: JCurs :: Cursor -> JCurs
- Waargonaut.Decode.Types: [unJCurs] :: JCurs -> SuccinctCursor
+ Waargonaut.Decode.Types: [unJCurs] :: JCurs -> Cursor

Files

changelog.md view
@@ -1,5 +1,36 @@ # Revision history for waargonaut +## 0.8.0.0  -- 2019-09-04++* Add `onObj'` which is just `onObj` but specialised to `Identity`.++* Add `gObjEncoder` for deriving `ObjEncoder` structures for record types only. Using the+  `IsRecord` constraint from `record-sop` package. This makes it easier to leverage the+  Contravariant functionality of the `ObjEncoder` without losing the benefits of deriving+  more trivial encoders.++* Added the `FieldNameAsKey` option to the newtype options for generic derived enc/decoders.++* Fixes #69 by removing duplicate call to `_optionsFieldName` function. Added regression test.++* Improved the handling of newtype options for generic deriving to give a bit more+  flexibility and avoid strangeness with respect to some combinations of options.++* Change the building of escaped whitespace chars to actually use the+  `escapedWhitespaceChar` function, instead of incorrectly generating an unescaped+  character.+ +* Add haddock to gObjEncoder function++* Correctly bump version to 0.8.0.0 as this is a breaking change because of new+  constructors on an exported sum type.++* Remove some commented out code.++* Add a better failure message to "impossible" error case.++* Regenerate nix after cabal file changes+ ## 0.6.1.0  -- 2019-02-27  * Add `passKeysToValues` decoder for decoding JSON objects where the key should
src/Waargonaut.hs view
@@ -52,7 +52,7 @@ -- } -- @ ----- We'll need to import the 'Decode' module. You may of course use whatever import scheme you like,+-- We'll need to import the @Waargonaut.Decode@ module. You may of course use whatever import scheme you like, -- I prefer this method: -- -- @@@ -60,37 +60,39 @@ -- import qualified Waargonaut.Decode as D -- @ ----- The 'Waargonaut.Decode.Decoder' is based upon a data structure called a 'zipper'. This allows us+-- The 'Waargonaut.Decode.Decoder' is based upon a data structure called a "zipper". This allows us -- to move around the JSON structure using arbitrary movements. Such as -- 'Waargonaut.Decode.moveRight1' to move from a key on an object to the value at that key. Or -- 'Waargonaut.Decode.down' to move into the first element of an array or object. Waargonaut -- provides a suite of these functions to move around and dissect the JSON input. ----- This zipper is combined with a 'StateT' transformer that maintains a history of your movements.+-- This zipper is combined with a 'Control.Monad.State.StateT' transformer that maintains a history+-- of your movements.+-- -- So if the JSON input is not as your 'Waargonaut.Decode.Decoder' expects you are given a complete -- path to where things went awry. -- -- Decoding a JSON value is done by moving the cursor to specific points of interest, then focusing -- on that point with a 'Waargonaut.Decode.Decoder' of the desired value. ----- NB: The 'Monad' constraint is provided as a flexibility for more interesting and nefarious uses+-- NB: The "Monad" constraint is provided as a flexibility for more interesting and nefarious uses -- of 'Waargonaut.Decode.Decoder'. ----- Here is the 'Waargonaut.Decode.Decoder' for our 'Person' data type. It will help to turn on the--- 'OverloadedStrings' language pragma as these functions expect 'Data.Text.Text' input.+-- Here is the 'Waargonaut.Decode.Decoder' for our @Person@ data type. It will help to turn on the+-- @OverloadedStrings@ language pragma as these functions expect 'Data.Text.Text' input. -- -- @ -- personDecoder :: Monad f => Decoder f Person -- personDecoder = D.withCursor $ \\c -> do --   o     <- D.down c---   name  <- D.fromKey "name" D.text o---   age   <- D.fromKey "age" D.int o---   addr  <- D.fromKey "address" D.text o---   lotto <- D.fromKey "numbers" (D.list D.int) o+--   name  <- D.fromKey \"name\" D.text o+--   age   <- D.fromKey \"age\" D.int o+--   addr  <- D.fromKey \"address\" D.text o+--   lotto <- D.fromKey \"numbers\" (D.list D.int) o --   pure $ Person name age addr lotto -- @ ----- The 'Waargonaut.Decode.withCursor' function provides our cursor: 'c'. We then move+-- The 'Waargonaut.Decode.withCursor' function provides our cursor: "c". We then move -- 'Waargonaut.Decode.down' into the JSON object. The reasons for this are: -- -- * The initial cursor position is always at the very beginning of the input. On freshly indexed@@ -125,10 +127,10 @@ -- @ -- personDecoder2 :: Monad f => Decoder f Person -- personDecoder2 = Person---   \<$> D.atKey "name" D.text---   \<*> D.atKey "age" D.int---   \<*> D.atKey "address" D.text---   \<*> D.atKey "numbers" (D.list D.int)+--   \<$> D.atKey \"name\" D.text+--   \<*> D.atKey \"age\" D.int+--   \<*> D.atKey \"address\" D.text+--   \<*> D.atKey \"numbers\" (D.list D.int) -- @ -- -- Using the 'Waargonaut.Decode.atKey' function which tries to handle those basic movements for us@@ -140,10 +142,10 @@ -- @ -- -- The next part is being able to apply our 'Waargonaut.Decode.Decoder' to some input. Assuming we--- have some input 'in'. We want to pass it through our 'personDecoder' for a result. Waargonaut uses+-- have some input. We want to pass it through our @personDecoder@ for a result. Waargonaut uses -- the <https://hackage.haskell.org/package/parsers parsers> package to define its parser. This -- allows you to choose your own favourite parsing library to do the heavy lifting. Provided it--- implements the right typeclasses from 'parsers'.+-- implements the right typeclasses from the @parsers@ package. -- -- To apply a 'Waargonaut.Decode.Decoder' to some input you will need one of the -- decoder running functions from 'Waargonaut.Decode'. There are a few different@@ -164,8 +166,8 @@ -- -- -- As well as a parsing function from your parsing library of choice, that also--- has an implementation of the 'CharParsing' typeclass from 'parsers'. We will--- use 'attoparsec' in the examples below.+-- has an implementation of the 'Text.Parser.Char.CharParsing' typeclass from @parsers@. We will+-- use @attoparsec@ in the examples below. -- -- @ -- import qualified Data.Attoparsec.ByteString as AB@@ -175,14 +177,14 @@ -- decodeFromByteString AB.parseOnly personDecode inp -- @ ----- Which will run the 'personDecode' 'Waargonaut.Decode.Decoder' using the parsing function--- (@AB.parseOnly@), starting at the cursor from the top of the 'inp' input.+-- Which will run the @personDecode@ 'Waargonaut.Decode.Decoder' using the parsing function+-- (@AB.parseOnly@), starting at the cursor from the top of the @inp@ input. ----- Again the 'Monad' constraint is there so that you have more options available for utilising the+-- Again the 'Control.Monad.Monad' constraint is there so that you have more options available for utilising the -- 'Waargonaut.Decode.Decoder' in ways we haven't thought of. ----- Or if you don't need the 'Monad' constraint then you may use 'Waargonaut.Decode.pureDecodeFromByteString'.--- This function specialises the 'Monad' constraint to 'Data.Functor.Identity'.:+-- Or if you don't need the 'Control.Monad.Monad' constraint then you may use 'Waargonaut.Decode.pureDecodeFromByteString'.+-- This function specialises the 'Control.Monad.Monad' constraint to 'Data.Functor.Identity'.: -- -- @ -- pureDecodeFromByteString@@ -200,11 +202,11 @@ -- pureDecodeFromByteString AB.parseOnly personDecode inp -- @ ----- Waargonaut provides some default implementations using the <https://hackage.haskell.org/package/attoparsec attoparsec> package in the 'Waargonaut.Attoparsec' module. These functions have exactly the same behaviour as the functions above, without the need to provide the parsing function.+-- Waargonaut provides some default implementations using the <https://hackage.haskell.org/package/attoparsec attoparsec> package in the @Waargonaut.Attoparsec@ module. These functions have exactly the same behaviour as the functions above, without the need to provide the parsing function.  -- $basicencode ----- To create an 'Waargonaut.Encode.Encoder' for our 'Person' record, we will encode it as a "map+-- To create an 'Waargonaut.Encode.Encoder' for our @Person@ record, we will encode it as a "map -- like object", that is we have decided that there are no duplicate keys allowed. We can then use -- the following functions to build up the structure we want: --@@ -235,7 +237,7 @@ -- @ -- -- These types may seem pretty wild, but their usage is mundane. The 'Waargonaut.Encode.mapLikeObj'--- function is used when we want to encode some particular type 'i' as a JSON object. In such a way+-- function is used when we want to encode some particular type @i@ as a JSON object. In such a way -- as to prevent duplicate keys from appearing. The 'Waargonaut.Encode.atKey' function is designed -- such that it can be composed with itself to build up an object with multiple keys. --@@ -278,7 +280,7 @@ -- simplePureEncodeByteStringNoSpaces :: Encoder' a -> a -> ByteString -- @ ----- The latter functions specialise the 'f' to be 'Data.Functor.Identity'.+-- The latter functions specialise the @f@ to be 'Data.Functor.Identity'. -- -- Then, like the use of the 'Waargonaut.Decode.Decoder' you select the 'Waargonaut.Encode.Encoder' -- you wish to use and run it against a value of a matching type:
src/Waargonaut/Attoparsec.hs view
@@ -5,9 +5,9 @@ -- different parsing libraries, depending on your needs. This module provides -- some convenient defaults using the <https://hackage.haskell.org/package/attoparsec attoparsec> package. ----- These functions are implemented using the 'decodeFromX' functions in the--- 'Waargonaut.Decode' module. They use the @parseOnly@ function, from either--- the @Text@ or @ByteString@ attoparsec modules.+-- These functions are implemented using the @decodeFromX@ functions in the+-- @Waargonaut.Decode@ module. They use the @parseOnly@ function, from either+-- the 'Data.Attoparsec.Text' or 'Data.Attoparsec.ByteString' attoparsec modules. -- module Waargonaut.Attoparsec   ( -- * Decoders
src/Waargonaut/Decode.hs view
@@ -10,11 +10,182 @@ -- -- Types and Functions for turning JSON into Haskell. --+-- We will work through a basic example, using the following type:+--+-- @+-- data Person = Person+--   { _personName                    :: Text+--   , _personAge                     :: Int+--   , _personAddress                 :: Text+--   , _personFavouriteLotteryNumbers :: [Int]+--   }+--   deriving (Eq, Show)+-- @+--+-- Expect the following JSON as input:+--+-- @+-- { \"name\":    \"Krag\"+-- , \"age\":     88+-- , \"address\": \"Red House 4, Three Neck Lane, Greentown.\"+-- , \"numbers\": [86,3,32,42,73]+-- }+-- @+--+-- We'll need to import the 'Waargonaut.Decode' module. You may of course use whatever import scheme you like,+-- I prefer this method:+--+-- @+-- import Waargonaut.Decode (Decoder)+-- import qualified Waargonaut.Decode as D+-- @+--+-- The 'Waargonaut.Decode.Decoder' is based upon a data structure called a @zipper@. This allows us+-- to move around the JSON structure using arbitrary movements. Such as+-- 'Waargonaut.Decode.moveRight1' to move from a key on an object to the value at that key. Or+-- 'Waargonaut.Decode.down' to move into the first element of an array or object. Waargonaut+-- provides a suite of these functions to move around and dissect the JSON input.+--+-- This zipper is combined with a 'Control.Monad.State.StateT' transformer that maintains a history of your movements.+-- So if the JSON input is not as your 'Waargonaut.Decode.Decoder' expects you are given a complete+-- path to where things went awry.+--+-- Decoding a JSON value is done by moving the cursor to specific points of interest, then focusing+-- on that point with a 'Waargonaut.Decode.Decoder' of the desired value.+--+-- NB: The "Monad" constraint is provided as a flexibility for more interesting and nefarious uses+-- of 'Waargonaut.Decode.Decoder'.+--+-- Here is the 'Waargonaut.Decode.Decoder' for our @Person@ data type. It will help to turn on the+-- @OverloadedStrings@ language pragma as these functions expect 'Data.Text.Text' input.+--+-- @+-- personDecoder :: Monad f => Decoder f Person+-- personDecoder = D.withCursor $ \\c -> do+--   o     <- D.down c+--   name  <- D.fromKey "name" D.text o+--   age   <- D.fromKey "age" D.int o+--   addr  <- D.fromKey "address" D.text o+--   lotto <- D.fromKey "numbers" (D.list D.int) o+--   pure $ Person name age addr lotto+-- @+--+-- The 'Waargonaut.Decode.withCursor' function provides our cursor: @c@. We then move+-- 'Waargonaut.Decode.down' into the JSON object. The reasons for this are:+--+-- * The initial cursor position is always at the very beginning of the input. On freshly indexed+--   JSON input, using our example, the cursor will be at:+--+-- @+-- \<cursor\>{ \"name\": \"Krag\"+--         , \"age\": 88+--         ...+-- @+--+-- * Because of the above reason, our decoder makes an assumption about the placement of the cursor+--   on the JSON input. This sort of assumption is reasonable for reasons we will go over later.+--+-- The cursor output from 'Waargonaut.Decode.down' will located here:+--+-- @+-- { \<cursor\>\"name\": \"Krag\"+--   , \"age\": 88+--   ...+-- @+--+-- Then we use one of the helper functions, 'Waargonaut.Decode.fromKey' to find the "key - value"+-- pair that we're interested in and decode it for us:+--+-- @+-- fromKey :: Monad f => Text -> Decoder f b -> JCurs -> DecodeResult f b+-- @+--+-- We could also write this 'Waargonaut.Decode.Decoder' as:+--+-- @+-- personDecoder2 :: Monad f => Decoder f Person+-- personDecoder2 = Person+--   \<$> D.atKey \"name\" D.text+--   \<*> D.atKey \"age\" D.int+--   \<*> D.atKey \"address\" D.text+--   \<*> D.atKey \"numbers\" (D.list D.int)+-- @+--+-- Using the 'Waargonaut.Decode.atKey' function which tries to handle those basic movements for us+-- and has those assumptions included. Very useful for when the JSON input closely mirrors your data+-- structure.+--+-- @+-- atKey :: Monad f => Text -> Decoder f a -> Decoder f a+-- @+--+-- The next part is being able to apply our 'Waargonaut.Decode.Decoder' to some input. Assuming we+-- have some input. We want to pass it through our @personDecoder@ for a result. Waargonaut uses+-- the <https://hackage.haskell.org/package/parsers parsers> package to define its parser. This+-- allows you to choose your own favourite parsing library to do the heavy lifting. Provided it+-- implements the right typeclasses from the @parsers@ package.+--+-- To apply a 'Waargonaut.Decode.Decoder' to some input you will need one of the+-- decoder running functions from 'Waargonaut.Decode'. There are a few different+-- functions provided for some of the common input text-like types.:+--+-- @+-- decodeFromByteString+--   :: ( CharParsing f+--      , Monad f+--      , Monad g+--      , Show e+--      )+--   => (forall a. f a -> ByteString -> Either e a)+--   -> Decoder g x+--   -> ByteString+--   -> g (Either (DecodeError, CursorHistory) x)+-- @+--+--+-- As well as a parsing function from your parsing library of choice, that also+-- has an implementation of the 'Text.Parser.Char.CharParsing' typeclass from @parsers@. We will+-- use @attoparsec@ in the examples below.+--+-- @+-- import qualified Data.Attoparsec.ByteString as AB+-- @+--+-- @+-- decodeFromByteString AB.parseOnly personDecode inp+-- @+--+-- Which will run the @personDecode@ 'Waargonaut.Decode.Decoder' using the parsing function+-- (@AB.parseOnly@), starting at the cursor from the top of the @inp@ input.+--+-- Again the 'Control.Monad.Monad' constraint is there so that you have more options available for utilising the+-- 'Waargonaut.Decode.Decoder' in ways we haven't thought of.+--+-- Or if you don't need the 'Control.Monad.Monad' constraint then you may use 'Waargonaut.Decode.pureDecodeFromByteString'.+-- This function specialises the 'Control.Monad.Monad' constraint to 'Data.Functor.Identity'.:+--+-- @+-- pureDecodeFromByteString+--   :: ( Monad f+--      , CharParsing f+--      , Show e+--      )+--   => (forall a. f a -> ByteString -> Either e a)+--   -> Decoder Identity x+--   -> ByteString+--   -> Either (DecodeError, CursorHistory) x+-- @+--+-- @+-- pureDecodeFromByteString AB.parseOnly personDecode inp+-- @+--+-- Waargonaut provides some default implementations using the <https://hackage.haskell.org/package/attoparsec attoparsec> package in the @Waargonaut.Attoparsec@ module. These functions have exactly the same behaviour as the functions above, without the need to provide the parsing function. module Waargonaut.Decode   (     -- * Types     CursorHistory-  , SuccinctCursor+  , Cursor   , DecodeResult (..)   , Decoder (..)   , JCurs (..)@@ -165,8 +336,8 @@ import           HaskellWorks.Data.Bits                         ((.?.)) import           HaskellWorks.Data.TreeCursor                   (TreeCursor (..)) -import           HaskellWorks.Data.Json.Backend.Standard.Cursor (JsonCursor (..))-import qualified HaskellWorks.Data.Json.Backend.Standard.Cursor as JC+import           HaskellWorks.Data.Json.Standard.Cursor.Fast (Cursor)+import qualified HaskellWorks.Data.Json.Standard.Cursor.Generic as JC  import           Waargonaut.Decode.Error                        (AsDecodeError (..),                                                                  DecodeError (..),@@ -182,11 +353,10 @@                                                                  Decoder (..),                                                                  JCurs (..),                                                                  JsonType (..),-                                                                 SuccinctCursor,                                                                  jsonTypeAt,                                                                  mkCursor) --- | Function to define a 'Decoder' for a specific data type.+-- | Function to define a 'Waargonaut.Decode.Decoder' for a specific data type. -- -- For example, given the following data type: --@@ -200,8 +370,9 @@ --   } -- @ ----- We can use 'withCursor' to write a decoder that will be given a cursor that--- we can use to build the data types that we need.+-- We can use 'Waargonaut.Decode.withCursor' to write a decoder that+-- will be given a cursor that we can use to build the data types that+-- we need. -- -- @ -- imageDecoder :: Monad f => Decoder f Image@@ -214,7 +385,7 @@ -- @ -- -- It's up to you to provide a cursor that is at the correct position for a--- 'Decoder' to operate, but building decoders in this way simplifies creating+-- 'Waargonaut.Decode.Decoder' to operate, but building decoders in this way simplifies creating -- decoders for larger structures, as the smaller pieces contain fewer -- assumptions. This encourages greater reuse of decoders and simplifies the -- debugging process.@@ -228,10 +399,10 @@ -- | Lens for accessing the 'rank' of the 'JsonCursor'. The 'rank' forms part of -- the calculation that is the cursors current position in the index. ---cursorRankL :: Lens' (JsonCursor s i p) Count-cursorRankL = lens JC.cursorRank (\c r -> c { cursorRank = r })+cursorRankL :: Lens' Cursor Count+cursorRankL = lens JC.cursorRank (\c r -> c { JC.cursorRank = r }) --- | Execute the given function 'n' times'.+-- | Execute the given function @n@ times. manyMoves :: Monad m => Natural -> (b -> m b) -> b -> m b manyMoves i g = foldl (>=>) pure (replicate i g) @@ -245,7 +416,7 @@ -- successful. moveCursBasic   :: Monad f-  => (SuccinctCursor -> Maybe SuccinctCursor)+  => (Cursor -> Maybe Cursor)   -> ZipperMove   -> JCurs   -> DecodeResult f JCurs@@ -326,7 +497,7 @@   then pure $ c & _Wrapped . cursorRankL .~ newRank   else throwing _InputOutOfBounds newRank --- | Move the cursor rightwards 'n' times.+-- | Move the cursor rightwards @n@ times. -- -- Starting position: --@@ -374,7 +545,7 @@   in     setRank <$> BP.findOpen (JC.balancedParens c) prev <?> InputOutOfBounds prev --- | Move the cursor leftwards 'n' times.+-- | Move the cursor leftwards @n@ times. moveLeftN   :: Monad f   => Natural@@ -498,8 +669,8 @@ -- -- myRecDecoder :: Decoder f MyRec -- myRecDecoder = MyRec---   \<$> atKey "field_a" text---   \<*> atKey "field_b" int+--   \<$> atKey \"field_a\" text+--   \<*> atKey \"field_b\" int -- @ -- atKey@@ -534,7 +705,7 @@ -- it could be decoded as follows: -- -- @--- join \<$> atKeyOptional "key" (maybeOrNull text)+-- join \<$> atKeyOptional \"key\" (maybeOrNull text) -- @ atKeyOptional   :: Monad f@@ -568,7 +739,7 @@ -- withType JsonTypeArray d -- @ ----- 'd' will only be entered if the cursor at the current position is a JSON+-- @d@ will only be entered if the cursor at the current position is a JSON -- array: '[]'. -- withType@@ -675,9 +846,9 @@ -- -- decodeMyEnum :: Monad f => Decoder f MyEnum -- decodeMyEnum = D.oneOf D.text \"MyEnum\"---   [ ("a", A)---   , ("b", B)---   , ("c", C)+--   [ (\"a\", A)+--   , (\"b\", B)+--   , (\"c\", C) --   ] -- -- decodeMyEnumFromInt :: Monad f => Decoder f MyEnum@@ -704,7 +875,7 @@     err = throwing _ConversionFailure l  -- | From the current cursor position, move leftwards one position at a time and--- push each 'a' onto the front of some 'Cons' structure.+-- push each @a@ onto the front of some 'Cons' structure. leftwardCons   :: ( Monad f      , Cons s s a a@@ -717,7 +888,7 @@   foldCursor (flip cons) moveLeft1  -- | From the current cursor position, move rightwards one position at a time,--- and append the 'a' to some 'Snoc' structure.+-- and append the @a@ to some 'Snoc' structure. rightwardSnoc   :: ( Monad f      , Snoc s s a a@@ -815,7 +986,7 @@ bool :: Monad f => Decoder f Bool bool = atCursor "bool" DI.bool' --- | Given a 'Decoder' for 'a', attempt to decode a 'NonEmpty' list of 'a' at+-- | Given a 'Decoder' for @a@, attempt to decode a 'NonEmpty' list of @a@ at -- the current cursor position. nonemptyAt   :: Monad f@@ -832,7 +1003,7 @@ nonempty :: Monad f => Decoder f a -> Decoder f (NonEmpty a) nonempty d = withCursor (nonemptyAt d) --- | Like 'nonemptyAt', this takes a 'Decoder' of 'a' and at the given cursor+-- | Like 'nonemptyAt', this takes a 'Decoder' of @a@ and at the given cursor -- will try to decode a '[a]'. listAt   :: Monad f@@ -877,7 +1048,7 @@ withDefault def hasD =   fromMaybe def <$> hasD --- | Named to match it's 'Encoder' counterpart, this function will decode an+-- | Named to match it's 'Waargonaut.Encode.Encoder' counterpart, this function will decode an -- optional value. maybeOrNull   :: Monad f@@ -886,7 +1057,7 @@ maybeOrNull a =   (Just <$> a) <!> (Nothing <$ null) --- | Decode either an 'a' or a 'b', failing if neither 'Decoder' succeeds. The+-- | Decode either an @a@ or a @b@, failing if neither 'Decoder' succeeds. The -- 'Right' decoder is attempted first. either   :: Monad f
src/Waargonaut/Decode/Error.hs view
@@ -11,7 +11,7 @@  import           GHC.Word                     (Word64) -import           HaskellWorks.Data.Json.Type  (JsonType)+import           HaskellWorks.Data.Json.Standard.Cursor.Type  (JsonType)  import           Data.Text                    (Text) @@ -40,7 +40,7 @@   | ParseFailed Text   deriving (Show, Eq) --- | Describes the sorts of errors that may be treated as a 'DecodeError', for use with 'lens'.+-- | Describes the sorts of errors that may be treated as a 'DecodeError', for use with 'Control.Lens.Prism's. class AsDecodeError r where   _DecodeError       :: Prism' r DecodeError   _ConversionFailure :: Prism' r Text
src/Waargonaut/Decode/Internal.hs view
@@ -97,9 +97,6 @@                                                   DecodeError (..)) import           Waargonaut.Decode.ZipperMove    (ZipperMove (..), ppZipperMove) -import           Waargonaut.Encode.Builder       (bsBuilder)-import           Waargonaut.Encode.Builder.JChar (jCharBuilder)--- | -- Track the history of the cursor as we move around the zipper. -- -- It is indexed over the type of the index used to navigate the zipper.@@ -156,7 +153,7 @@ compressHistory = L.over _Wrapped (fromList . L.transform (combineLRMoves . rmKeyJumps) . F.toList)  -- |--- Pretty print the given 'CursorHistory'' to a more useful format compared to a 'Seq' of 'i'.+-- Pretty print the given 'CursorHistory'' to a more useful format compared to a 'Seq' of @i@. ppCursorHistory   :: CursorHistory' i   -> Doc a@@ -210,7 +207,7 @@           . runExceptT . runDecodeResult  -- |--- Wrapper type to describe a "Decoder" from something that has a 'Json'ish+-- Wrapper type to describe a "Decoder" from something that has a "Json"ish -- value @c@, to some representation of @a@. -- newtype Decoder' c i e f a = Decoder'@@ -236,7 +233,7 @@ -- Helper function for constructing a 'Decoder''. -- -- This function is used by the implemented decoders to simplify constructing a--- more specific 'Decoder' type.+-- more specific 'Decoder'' type. -- -- @ -- withCursor' $ \curs ->@@ -251,7 +248,7 @@   Decoder'  -- |--- Execute a given 'DecoderResultT'.+-- Execute a given 'Waargonaut.Decode.Internal.DecoderResultT'. -- -- If you're building your own decoder structure, this function will take care -- of the 'CursorHistory'' and error handling (via 'ExceptT').@@ -274,14 +271,14 @@ recordZipperMove dir i = L._Wrapped %= (`L.snoc` (dir, i))  -- |--- Attempt a 'Decoder' action that might fail and return a 'Maybe' value+-- Attempt a 'Waargonaut.Decode.Internal.Decoder' action that might fail and return a 'Maybe' value -- instead. -- try :: MonadError e m => m a -> m (Maybe a) try d = catchError (pure <$> d) (const (pure Nothing))  -- |--- Build the basis for a 'Decoder' based on a 'Prism''.+-- Build the basis for a 'Waargonaut.Decode.Internal.Decoder' based on a 'Control.Lens.Prism''. -- prismDOrFail'   :: ( AsDecodeError e@@ -295,24 +292,28 @@ prismDOrFail' e p d c =   runDecoder' (L.preview p <$> d) c <!?> e --- | Try to decode a 'Text' value from some 'Json' or value. This will fail if+-- | Try to decode a 'Text' value from some 'Waargonaut.Types.Json.Json' or value. This will fail if -- the input value is not a valid UTF-8 'Text' value, as checked by the -- 'Data.Text.Encoding.decodeUtf8'' function. text' :: AsJType a ws a => a -> Maybe Text text' = L.preview (_JStr . _1 . _JStringText) --- | Try to decode a 'String' value from some 'Json' or value.+-- | Try to decode a 'String' value from some 'Waargonaut.Types.Json.Json' or value. string' :: AsJType a ws a => a -> Maybe String string' = L.preview (_JStr . _1 . _Wrapped . L.to (V.toList . V.map jCharToChar)) --- | Try to decode a 'Data.ByteString.ByteString' value from some 'Json' or value.+-- | Try to decode a 'Data.ByteString.ByteString' value from some 'Waargonaut.Types.Json.Json' or value. strictByteString' :: AsJType a ws a => a -> Maybe ByteString strictByteString' = fmap BL.toStrict . lazyByteString' --- | Try to decode a 'Data.ByteString.Lazy.ByteString' value from some 'Json' or value.+-- | Try to decode a 'Data.ByteString.Lazy.ByteString' value from some 'Waargonaut.Types.Json.Json' or value. lazyByteString' :: AsJType a ws a => a -> Maybe BL.ByteString lazyByteString' = L.preview (_JStr . _1 . _Wrapped . L.to mkBS)-  where mkBS = BB.toLazyByteString . foldMap (jCharBuilder bsBuilder)+  -- This uses the 'Data.ByteString.Builder.char8' function as parsing has+  -- validated our inputs. If we use 'Data.ByteString.Builder.charUtf8' or+  -- 'Waargonaut.Builder.bsBuilder' the input will be incorrectly "double+  -- encoded" and everything will be wrong. +  where mkBS = BB.toLazyByteString . foldMap (BB.char8 . jCharToChar)  -- | Decoder for a 'Char' value that cannot contain values in the range U+D800 -- to U+DFFF. This decoder will fail if the 'Char' is outside of this range.@@ -324,27 +325,27 @@ unboundedChar' :: AsJType a ws a => a -> Maybe Char unboundedChar' = L.preview (_JStr . _1 . _Wrapped . L._head . L.to jCharToChar) --- | Try to decode a 'Scientific' value from some 'Json' or value.+-- | Try to decode a 'Scientific' value from some 'Waargonaut.Types.Json.Json' or value. scientific' :: AsJType a ws a => a -> Maybe Scientific scientific' = L.preview (_JNum . _1) >=> jNumberToScientific --- | Try to decode a bounded 'Integral n => n' value from some 'Json' value.+-- | Try to decode a bounded 'Integral n => n' value from some 'Waargonaut.Types.Json.Json' value. integral' :: (Bounded i , Integral i , AsJType a ws a) => a -> Maybe i integral' = scientific' >=> Sci.toBoundedInteger --- | Try to decode an 'Int' from some 'Json' value+-- | Try to decode an 'Int' from some 'Waargonaut.Types.Json.Json' value int' :: AsJType a ws a => a -> Maybe Int int' = integral' --- | Try to decode a 'Bool' from some 'Json' value+-- | Try to decode a 'Bool' from some 'Waargonaut.Types.Json.Json' value bool' :: AsJType a ws a => a -> Maybe Bool bool' = L.preview (_JBool . _1) --- | Try to decode a 'null' value from some 'Json' value+-- | Try to decode a 'null' value from some 'Waargonaut.Types.Json.Json' value null' :: AsJType a ws a => a -> Maybe () null' a = L.preview _JNull a $> () --- | Combined with another decoder function 'f', try to decode a list of 'a' values.+-- | Combined with another decoder function @f@, try to decode a list of @a@ values. -- -- @ -- array' int' :: Json -> [Int]@@ -379,9 +380,9 @@ -- -- Starting from the given cursor position, try to move in the direction -- specified by the given cursor function. Attempting to decode each item at each--- position using the given 'Decoder', until the movement is unsuccessful.+-- position using the given 'Waargonaut.Decode.Internal.Decoder', until the movement is unsuccessful. ----- The following could be used to leverage the 'Snoc' instance of '[]' to build '[Int]'.+-- The following could be used to leverage the 'Control.Lens.Snoc' instance of '[]' to build '[Int]'. -- -- @ -- intList :: Monad f => JCurs -> DecodeResult f [Int]
src/Waargonaut/Decode/Runners.hs view
@@ -1,4 +1,8 @@ {-# LANGUAGE RankNTypes #-}+-- |+--+-- Functions to execution of 'Waargonaut.Decode.Decoder's+-- module Waargonaut.Decode.Runners   (     -- * General over @f@@@ -48,14 +52,14 @@                                              Decoder (..), mkCursor)  -- | General decoding function that takes a given parsing function and some--- functions to handle the transition from the input of the 'JCurs' to the+-- functions to handle the transition from the input of the 'Waargonaut.Decoder.Types.JCurs' to the -- desired input type. The indexer and cursor requires a 'ByteString' to work -- efficiently, but this does not preclude the use of other text types, provided -- the right functions are present. -- -- There are some specialised versions of this function provided for 'Text', -- 'String', and 'ByteString'. They are implemented using this function, for--- example to work with 'Text' input and the 'attoparsec' package:+-- example to work with 'Text' input and the @attoparsec@ package: -- -- @ -- import qualified Data.Attoparsec.Text as AT@@ -210,7 +214,7 @@   pureDecodeWithInput decodeFromString  -- | Helper function to handle wrapping up a parse failure using the given--- parsing function. Intended to be used with the 'runDecode' or 'simpleDecode'+-- parsing function. Intended to be used with the 'Waargonaut.Decode.runDecode' or 'Waargonaut.Decode.Traversal.simpleDecode' -- functions. -- -- @
src/Waargonaut/Decode/Traversal.hs view
@@ -163,8 +163,8 @@  -- | This is an alias to help explain a type from the zipper that is used to move -- around the 'Json' data structure. 'JCursor h a' represents a "cursor" that is--- currently located on a thing of type 'a', having previously been on a thing--- of type 'h'.+-- currently located on a thing of type @a@, having previously been on a thing+-- of type @h@. -- -- This type will grow as a form of "breadcrumb" trail as the cursor moves -- through the data structure. It may be used interchangably with 'h :>> a' from@@ -281,7 +281,7 @@   a <- mCurs <?> FailedToMove dir   a <$ DR.recordZipperMove dir (Z.tooth a) --- | Using a given 'LensLike', try to step down into the 'Json' data structure+-- | Using a given 'Control.Lens.LensLike', try to step down into the 'Json' data structure -- to the location targeted by the lens. -- -- This can be used to move large steps over the data structure, or more@@ -325,7 +325,7 @@ up =   moveAndKeepHistory U . pure . Z.upward --- | From the current cursor location, try to move 'n' steps to the left.+-- | From the current cursor location, try to move @n@ steps to the left. moveLeftN   :: Monad f   => Natural@@ -334,7 +334,7 @@ moveLeftN n cur =   moveAndKeepHistory (L n) (Z.jerks Z.leftward (n ^. re _Natural) cur) --- | From the current cursor location, try to move 'n' steps to the right.+-- | From the current cursor location, try to move @n@ steps to the right. moveRightN   :: Monad f   => Natural@@ -359,7 +359,7 @@ moveRight1 =   moveRightN (successor' zero') --- | Provide a 'conversion' function and create a 'Decoder' that uses the+-- | Provide a @conversion@ function and create a 'Decoder' that uses the -- current cursor and runs the given function. Fails with 'ConversionFailure' and -- the given 'Text' description. atCursor@@ -423,8 +423,8 @@ -- -- myRecDecoder :: Decoder f MyRec -- myRecDecoder = MyRec---   <$> atKey "field_a" text---   <*> atKey "field_b" int+--   <$> atKey \"field_a\" text+--   <*> atKey \"field_b\" int -- @ -- atKey@@ -509,7 +509,7 @@     elemD  -- | Use the 'Cons' typeclass and move leftwards from the current cursor--- position, 'consing' the values to the 's' as it moves.+-- position, "consing" the values to the @s@ as it moves. leftwardCons   :: ( Monad f      , Cons s s a a@@ -525,7 +525,7 @@     elemD  -- | Use the 'Snoc' typeclass and move rightwards from the current cursor--- position, 'snocing' the values to the 's' as it moves.+-- position, "snocing" the values to the @s@ as it moves. rightwardSnoc   :: ( Monad f      , Snoc s s a a@@ -540,7 +540,7 @@     (unDecodeResult . moveRight1)     elemD --- | Decode a 'NonEmpty' list of 'a' at the given cursor position.+-- | Decode a 'NonEmpty' list of @a@ at the given cursor position. nonEmptyAt   :: Monad f   => Decoder f a@@ -566,7 +566,7 @@   try (moveAndKeepHistory D (Z.within WT.jsonTraversal c))   >>= Maybe.maybe (pure mempty) (rightwardSnoc mempty elemD) --- | Create a 'Decoder' for a list of 'a'+-- | Create a 'Decoder' for a list of @a@ list   :: Monad f   => Decoder f b@@ -584,8 +584,8 @@ withDefault def hasD =   withCursor (fmap (Maybe.fromMaybe def) . focus hasD) --- | Named to match it's 'Encoder' counterpart, this function will decode an--- optional value.+-- | Named to match it's 'Waargonaut.Encode.Encoder' counterpart, this+-- function will decode an optional value. maybeOrNull   :: Monad f   => Decoder f a@@ -593,7 +593,7 @@ maybeOrNull hasD =   withCursor (try . focus hasD) --- | Decode either an 'a' or a 'b', failing if neither 'Decoder' succeeds. The+-- | Decode either an @a@ or a @b@, failing if neither 'Decoder' succeeds. The -- 'Right' decoder is attempted first. either   :: Monad f
src/Waargonaut/Decode/Types.hs view
@@ -9,7 +9,7 @@ -- module Waargonaut.Decode.Types   ( ParseFn-  , SuccinctCursor+  , Cursor   , CursorHistory   , Decoder (..)   , DecodeResult (..)@@ -33,18 +33,13 @@ import           Data.Functor.Alt                               (Alt (..)) import qualified Data.Text                                      as Text -import           GHC.Word                                       (Word64)- import           Data.ByteString                                (ByteString)-import           Data.Vector.Storable                           (Vector) -import           HaskellWorks.Data.BalancedParens               (SimpleBalancedParens)-import           HaskellWorks.Data.Json.Backend.Standard.Cursor (JsonCursor (..))-import           HaskellWorks.Data.Json.Backend.Standard.Fast   (makeCursor)-import           HaskellWorks.Data.Json.Type                    (JsonType (..),+import           HaskellWorks.Data.Json.Standard.Cursor.Fast   (Cursor,fromByteStringViaBlanking)+import           HaskellWorks.Data.Json.Standard.Cursor.Generic (cursorRank)+import           HaskellWorks.Data.Json.Standard.Cursor.Type                    (JsonType (..),                                                                  JsonTypeAt (..)) import           HaskellWorks.Data.Positioning                  (Count)-import           HaskellWorks.Data.RankSelect.Poppy512          (Poppy512)  import           Waargonaut.Decode.Internal                     (CursorHistory', DecodeError (..),                                                                  DecodeResultT (..),@@ -57,11 +52,7 @@ type CursorHistory =   CursorHistory' Count --- | Convenience alias defined for the concrete 'JsonCursor' type.-type SuccinctCursor =-  JsonCursor ByteString Poppy512 (SimpleBalancedParens (Vector Word64))---- Another convenience alias for the type of the function we will use to parse+-- | Convenience alias for the type of the function we will use to parse -- the input string into the 'Json' structure. type ParseFn =   ByteString -> Either DecodeError Json@@ -100,19 +91,19 @@  -- | Wrapper type for the 'SuccinctCursor' newtype JCurs = JCurs-  { unJCurs :: SuccinctCursor+  { unJCurs :: Cursor   } deriving JsonTypeAt  instance JCurs ~ t => Rewrapped JCurs t  instance Wrapped JCurs where-  type Unwrapped JCurs = SuccinctCursor+  type Unwrapped JCurs = Cursor   _Wrapped' = iso unJCurs JCurs  -- | Take a 'ByteString' input and build an index of the JSON structure inside -- mkCursor :: ByteString -> JCurs-mkCursor = JCurs . makeCursor+mkCursor = JCurs . fromByteStringViaBlanking  -- | Provide some of the type parameters that the underlying 'DecodeResultT' -- requires. This contains the state and error management as we walk around our
src/Waargonaut/Encode.hs view
@@ -7,6 +7,103 @@ {-# LANGUAGE TupleSections         #-} {-# LANGUAGE TypeFamilies          #-} -- | Types and functions to encode your data types to 'Json'.+--+-- We will work through a basic example, using the following type:+--+-- @+-- data Person = Person+--   { _personName                    :: Text+--   , _personAge                     :: Int+--   , _personAddress                 :: Text+--   , _personFavouriteLotteryNumbers :: [Int]+--   }+--   deriving (Eq, Show)+-- @+--+-- To create an 'Waargonaut.Encode.Encoder' for our @Person@ record, we will encode it as a "map+-- like object", that is we have decided that there are no duplicate keys allowed. We can then use+-- the following functions to build up the structure we want:+--+-- @+-- mapLikeObj+--   :: ( AsJType Json ws a+--      , Semigroup ws         -- This library supports GHC 7.10.3 and 'Semigroup' wasn't a superclass of 'Monoid' then.+--      , Monoid ws+--      , Applicative f+--      )+--   => (i -> MapLikeObj ws a -> MapLikeObj ws a)+--   -> Encoder f i+-- @+--+-- And:+--+-- @+-- atKey'+--   :: ( At t+--      , IxValue t ~ Json+--      )+--   => Index t+--   -> Encoder' a+--   -> a+--   -> t+--   -> t+-- @+--+-- These types may seem pretty wild, but their usage is mundane. The 'Waargonaut.Encode.mapLikeObj'+-- function is used when we want to encode some particular type @i@ as a JSON object. In such a way+-- as to prevent duplicate keys from appearing. The 'Waargonaut.Encode.atKey'' function is designed+-- such that it can be composed with itself to build up an object with multiple keys.+--+-- @+-- import Waargonaut.Encode (Encoder)+-- import qualified Waargonaut.Encode as E+-- @+--+-- @+-- personEncoder :: Applicative f => Encoder f Person+-- personEncoder = E.mapLikeObj $ \\p ->+--   E.atKey' \"name\" E.text (_personName p) .+--   E.atKey' \"age\" E.int (_personAge p) .+--   E.atKey' \"address\" E.text (_personAddress p) .+--   E.atKey' \"numbers\" (E.list E.int) (_personFavouriteLotteryNumbers p)+-- @+--+-- The JSON RFC leaves the handling of duplicate keys on an object as a choice. It is up to the+-- implementor of a JSON handling package to decide what they will do. Waargonaut passes on this+-- choice to you. In both encoding and decoding, the handling of duplicate keys is up to you.+-- Waargonaut provides functionality to support /both/ use cases.+--+-- To then turn these values into JSON output:+--+-- @+-- simpleEncodeText         :: Applicative f => Encoder f a -> a -> f Text+-- simpleEncodeTextNoSpaces :: Applicative f => Encoder f a -> a -> f Text+--+-- simpleEncodeByteString         :: Applicative f => Encoder f a -> a -> f ByteString+-- simpleEncodeByteStringNoSpaces :: Applicative f => Encoder f a -> a -> f ByteString+-- @+--+-- Or+--+-- @+-- simplePureEncodeText         :: Encoder' a -> a -> Text+-- simplePureEncodeTextNoSpaces :: Encoder' a -> a -> Text+--+-- simplePureEncodeByteString         :: Encoder' a -> a -> ByteString+-- simplePureEncodeByteStringNoSpaces :: Encoder' a -> a -> ByteString+-- @+--+-- The latter functions specialise the @f@ to be 'Data.Functor.Identity'.+--+-- Then, like the use of the 'Waargonaut.Decode.Decoder' you select the 'Waargonaut.Encode.Encoder'+-- you wish to use and run it against a value of a matching type:+--+-- @+-- simplePureEncodeTextNoSpaces personEncoder (Person \"Krag\" 33 \"Red House 4, Three Neck Lane, Greentown.\" [86,3,32,42,73])+-- =+-- "{\"name\":\"Krag\",\"age\":88,\"address\":\"Red House 4, Three Neck Lane, Greentown.\",\"numbers\":[86,3,32,42,73]}"+-- @+-- module Waargonaut.Encode   (     -- * Encoder type@@ -93,6 +190,7 @@   , keyValuesAsObj'   , json'   , asJson'+  , onObj'   , generaliseEncoder   ) where @@ -160,7 +258,8 @@ import           Waargonaut.Encode.Builder.Types      (Builder) import           Waargonaut.Encode.Builder.Whitespace (wsBuilder, wsRemover) --- | Create an 'Encoder'' for 'a' by providing a function from 'a -> f Json'.++-- | Create an 'Encoder'' for @a@ by providing a function from 'a -> f Json'. encodeA :: (a -> f Json) -> Encoder f a encodeA = jsonEncoder @@ -235,7 +334,7 @@ simplePureEncodeWith builder buildRunner wsB enc =   runIdentity . simpleEncodeWith builder buildRunner wsB enc --- | As per 'simpleEncodeText' but specialised the 'f' to 'Data.Functor.Identity'.+-- | As per 'simpleEncodeText' but specialised the @f@ to 'Data.Functor.Identity'. simplePureEncodeText   :: Encoder Identity a   -> a@@ -243,7 +342,7 @@ simplePureEncodeText enc =   runIdentity . simpleEncodeText enc --- | As per 'simpleEncodeTextNoSpaces' but specialised the 'f' to 'Data.Functor.Identity'.+-- | As per 'simpleEncodeTextNoSpaces' but specialised the @f@ to 'Data.Functor.Identity'. simplePureEncodeTextNoSpaces   :: Encoder Identity a   -> a@@ -251,7 +350,7 @@ simplePureEncodeTextNoSpaces enc =   runIdentity . simpleEncodeTextNoSpaces enc --- | As per 'simpleEncodeByteString' but specialised the 'f' to 'Data.Functor.Identity'.+-- | As per 'simpleEncodeByteString' but specialised the @f@ to 'Data.Functor.Identity'. simplePureEncodeByteString   :: Encoder Identity a   -> a@@ -259,7 +358,7 @@ simplePureEncodeByteString enc =   runIdentity . simpleEncodeByteString enc --- | As per 'simpleEncodeByteStringNoSpaces' but specialised the 'f' to 'Data.Functor.Identity'.+-- | As per 'simpleEncodeByteStringNoSpaces' but specialised the @f@ to 'Data.Functor.Identity'. simplePureEncodeByteStringNoSpaces   :: Encoder Identity a   -> a@@ -359,7 +458,7 @@   . Either.either (runEncoder eA)   . runEncoder --- | Encode some 'Traversable' of 'a' into a JSON array.+-- | Encode some 'Traversable' of @a@ into a JSON array. traversable   :: ( Applicative f      , Traversable t@@ -397,39 +496,39 @@ list =   traversable --- | As per 'json' but with the 'f' specialised to 'Data.Functor.Identity'.+-- | As per 'json' but with the @f@ specialised to 'Data.Functor.Identity'. json' :: Encoder' Json json' = json --- | As per 'int' but with the 'f' specialised to 'Data.Functor.Identity'.+-- | As per 'int' but with the @f@ specialised to 'Data.Functor.Identity'. int' :: Encoder' Int int' = int --- | As per 'integral' but with the 'f' specialised to 'Data.Functor.Identity'.+-- | As per 'integral' but with the @f@ specialised to 'Data.Functor.Identity'. integral' :: Integral n => Encoder' n integral' = integral --- | As per 'scientific' but with the 'f' specialised to 'Data.Functor.Identity'.+-- | As per 'scientific' but with the @f@ specialised to 'Data.Functor.Identity'. scientific' :: Encoder' Scientific scientific' = scientific --- | As per 'bool' but with the 'f' specialised to 'Data.Functor.Identity'.+-- | As per 'bool' but with the @f@ specialised to 'Data.Functor.Identity'. bool' :: Encoder' Bool bool' = bool --- | As per 'string' but with the 'f' specialised to 'Data.Functor.Identity'.+-- | As per 'string' but with the @f@ specialised to 'Data.Functor.Identity'. string' :: Encoder' String string' = string --- | As per 'text' but with the 'f' specialised to 'Data.Functor.Identity'.+-- | As per 'text' but with the @f@ specialised to 'Data.Functor.Identity'. text' :: Encoder' Text text' = text --- | As per 'null' but with the 'f' specialised to 'Data.Functor.Identity'.+-- | As per 'null' but with the @f@ specialised to 'Data.Functor.Identity'. null' :: Encoder' () null' = null --- | As per 'maybe' but with the 'f' specialised to 'Data.Functor.Identity'.+-- | As per 'maybe' but with the @f@ specialised to 'Data.Functor.Identity'. maybe'   :: Encoder' ()   -> Encoder' a@@ -437,14 +536,14 @@ maybe' =   maybe --- | As per 'maybeOrNull' but with the 'f' specialised to 'Data.Functor.Identity'.+-- | As per 'maybeOrNull' but with the @f@ specialised to 'Data.Functor.Identity'. maybeOrNull'   :: Encoder' a   -> Encoder' (Maybe a) maybeOrNull' =   maybeOrNull --- | As per 'either' but with the 'f' specialised to 'Data.Functor.Identity'.+-- | As per 'either' but with the @f@ specialised to 'Data.Functor.Identity'. either'   :: Encoder' a   -> Encoder' b@@ -452,21 +551,21 @@ either' =   either --- | As per 'nonempty' but with the 'f' specialised to 'Data.Functor.Identity'.+-- | As per 'nonempty' but with the @f@ specialised to 'Data.Functor.Identity'. nonempty'   :: Encoder' a   -> Encoder' (NonEmpty a) nonempty' =   traversable --- | As per 'list' but with the 'f' specialised to 'Data.Functor.Identity'.+-- | As per 'list' but with the @f@ specialised to 'Data.Functor.Identity'. list'   :: Encoder' a   -> Encoder' [a] list' =   traversable --- | Encode some 'a' that is contained with another 't' structure.+-- | Encode some @a@ that is contained with another @t@ structure. encodeWithInner   :: ( Applicative f      , Traversable t@@ -477,7 +576,7 @@ encodeWithInner f g =   jsonEncoder $ fmap f . traverse (runEncoder g) --- | As per 'traversable' but with the 'f' specialised to 'Data.Functor.Identity'.+-- | As per 'traversable' but with the @f@ specialised to 'Data.Functor.Identity'. traversable'   :: Traversable t   => Encoder' a@@ -485,7 +584,7 @@ traversable' =   traversable --- | Using the given function to convert the 'k' type keys to a 'Text' value,+-- | Using the given function to convert the @k@ type keys to a 'Text' value, -- encode a 'Map' as a JSON object. mapToObj'   :: Encoder' a@@ -508,6 +607,7 @@ atKey k enc v t =   (\v' -> t & at k ?~ v') <$> runEncoder enc v +-- | Optionally encode an @a@ if it is a @Just a@. A @Nothing@ will result in the key being absent from the object. atOptKey   :: ( At t      , IxValue t ~ Json@@ -521,7 +621,7 @@ atOptKey k enc =   Maybe.maybe pure (atKey k enc) --- | Encode an 'a' at the given index on the JSON object.+-- | Encode an @a@ at the given index on the JSON object. atKey'   :: ( At t      , IxValue t ~ Json@@ -539,8 +639,8 @@ -- -- @ -- encoder = E.mapLikeObj \$ \\a ->---   atKey' "A" E.text (_getterA a)---   atOptKey' "B" E.int (_maybeB a)+--   atKey' \"A\" E.text (_getterA a)+--   atOptKey' \"B\" E.int (_maybeB a) -- -- simplePureEncodeByteString encoder (Foo "bob" (Just 33)) = "{\"A\":\"bob\",\"B\":33}" --@@ -588,7 +688,7 @@ boolAt =   flip atKey' bool --- | Encode a 'Foldable' of 'a' at the given index on a JSON object.+-- | Encode a 'Foldable' of @a@ at the given index on a JSON object. traversableAt   :: ( At t      , Traversable f@@ -709,9 +809,9 @@     floopObj = fromMapLikeObj . f . fst . toMapLikeObj  -- |--- Given encoders for things that are represented in JSON as 'objects', and a--- way to get to the 'b' and 'c' from the 'a'. This function lets you create an--- encoder for 'a'. The two objects are combined to make one single JSON object.+-- Given encoders for things that are represented in JSON as @objects@, and a+-- way to get to the @b@ and @c@ from the @a@. This function lets you create an+-- encoder for @a@. The two objects are combined to make one single JSON object. -- -- Given --@@ -723,7 +823,7 @@ -- @ -- -- We can use this function to utilise our already defined 'ObjEncoder'--- structures to give us an encoder for 'A':+-- structures to give us an encoder for @A@: -- -- @ -- combineObjects (\aRecord -> (_foo aRecord, _bar aRecord)) encodeFoo encodeBar :: ObjEncoder f Bar@@ -750,6 +850,16 @@ onObj k b encB o = (\j -> o & _Wrapped L.%~ L.cons j)   . JAssoc (_JStringText # k) mempty mempty <$> asJson encB b +-- | As per 'onObj' but the @f@ is specialised to 'Identity'.+onObj'+  :: Text+  -> b+  -> Encoder' b+  -> JObject WS Json+  -> JObject WS Json+onObj' k b encB o = (\j -> o & _Wrapped L.%~ L.cons j)+  . JAssoc (_JStringText # k) mempty mempty $ asJson' encB b+ -- | Encode key value pairs as a JSON object, allowing duplicate keys. keyValuesAsObj   :: ( Foldable g@@ -771,7 +881,7 @@ keyValueTupleFoldable eA = encodeA $   fmap (\v -> _JObj # (v,mempty)) . foldrM (\(k,v) o -> onObj k v eA o) (_Empty # ()) --- | As per 'keyValuesAsObj' but with the 'f' specialised to 'Identity'.+-- | As per 'keyValuesAsObj' but with the @f@ specialised to 'Identity'. keyValuesAsObj'   :: ( Foldable g      , Functor g
src/Waargonaut/Encode/Builder.hs view
@@ -1,4 +1,8 @@ {-# LANGUAGE RankNTypes #-}+-- |+--+-- Builder structures to help with turning 'Json' into a textual encoding.+-- module Waargonaut.Encode.Builder where  import           Data.String                       (IsString, fromString)@@ -11,6 +15,7 @@  import           Data.ByteString                   (ByteString) import qualified Data.ByteString.Builder           as B+import qualified Data.ByteString.Builder.Prim      as BP  import           Waargonaut.Types.Json             (JType (..), Json (..)) import           Waargonaut.Types.Whitespace       (WS)@@ -21,18 +26,22 @@ import           Waargonaut.Encode.Builder.JString (jStringBuilder) import           Waargonaut.Encode.Builder.Types   (Builder (..)) +-- | A 'T.Text' builder textBuilder :: Builder Text T.Builder textBuilder = Builder   T.singleton   T.fromText   T.decimal +-- | A 'B.ByteString' builder bsBuilder :: Builder ByteString B.Builder bsBuilder = Builder-  B.charUtf8+  (BP.primBounded BP.charUtf8)   B.byteString   B.intDec +-- | A general builder function for working with 'JType' values.+-- jTypesBuilder   :: ( IsString t      , Monoid b
src/Waargonaut/Encode/Builder/CommaSep.hs view
@@ -1,4 +1,8 @@ {-# LANGUAGE RankNTypes #-}+-- |+--+-- Builder functions for 'CommaSeparated' values.+-- module Waargonaut.Encode.Builder.CommaSep (commaSeparatedBuilder) where  import           Data.Monoid                     ((<>))@@ -24,7 +28,7 @@ commaTrailingBuilder bldr wsB =   foldMap ((commaBuilder bldr <>) . (wsB bldr) . snd) --- | Using the given builders for the whitespace and elements ('a'), create a+-- | Using the given builders for the whitespace and elements (@a@), create a -- builder for a 'CommaSeparated'. commaSeparatedBuilder   :: forall ws a t b. Monoid b
src/Waargonaut/Encode/Builder/JArray.hs view
@@ -1,3 +1,7 @@+-- |+--+-- Builder function for 'JArray'+-- module Waargonaut.Encode.Builder.JArray (jArrayBuilder) where  import           Waargonaut.Types.JArray            (JArray (..))
src/Waargonaut/Encode/Builder/JChar.hs view
@@ -1,3 +1,7 @@+-- |+--+-- Builder structures for 'JChar's+-- module Waargonaut.Encode.Builder.JChar (jCharBuilder) where  import           Control.Lens                     (review)
src/Waargonaut/Encode/Builder/JNumber.hs view
@@ -1,3 +1,7 @@+-- | +--+-- Builders for 'JNumber'+-- module Waargonaut.Encode.Builder.JNumber   ( jNumberBuilder   ) where
src/Waargonaut/Encode/Builder/JObject.hs view
@@ -1,3 +1,7 @@+-- |+--+-- Builder structures for 'JObject's+-- module Waargonaut.Encode.Builder.JObject (jObjectBuilder) where  import           Data.Monoid                        ((<>))
src/Waargonaut/Encode/Builder/JString.hs view
@@ -1,3 +1,7 @@+-- |+--+-- Builder structures for 'JString's+-- module Waargonaut.Encode.Builder.JString (jStringBuilder) where  import           Data.Monoid                     ((<>))
src/Waargonaut/Encode/Builder/Types.hs view
@@ -1,7 +1,12 @@+-- |+--+-- The structure used to contain the required character and related functions for running a "builder".+-- module Waargonaut.Encode.Builder.Types (Builder (..)) where +-- | The builder data type. data Builder t b = Builder-  { fromChar  :: Char -> b-  , fromChunk :: t -> b-  , fromInt   :: Int -> b+  { fromChar  :: Char -> b -- ^ Create a builder from a Haskell 'Char'+  , fromChunk :: t -> b -- ^ Create a builder from a chunk or piece of @t@+  , fromInt   :: Int -> b  -- ^ Create a builder from a Haskell 'Int'   }
src/Waargonaut/Encode/Builder/Whitespace.hs view
@@ -1,3 +1,7 @@+-- |+--+-- Builder structures for 'Whitespace'+-- module Waargonaut.Encode.Builder.Whitespace   ( whitespaceBuilder   , wsBuilder
src/Waargonaut/Encode/Types.hs view
@@ -121,7 +121,7 @@ jsonEncoder = EncoderFns id {-# INLINE jsonEncoder #-} --- | Helper function for creating a JSON 'object' 'Encoder'. Provides the+-- | Helper function for creating a JSON @object@ 'Encoder'. Provides the -- default 'finaliseEncoding' function for completing the 'JObject' to the -- necessary 'Json' type. objEncoder :: (a -> f (JObject WS Json)) -> EncoderFns (JObject WS Json) f a
src/Waargonaut/Generic.hs view
@@ -3,7 +3,6 @@ {-# LANGUAGE FlexibleContexts      #-} {-# LANGUAGE FlexibleInstances     #-} {-# LANGUAGE GADTs                 #-}-{-# LANGUAGE KindSignatures        #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE OverloadedStrings     #-} {-# LANGUAGE PolyKinds             #-}@@ -46,6 +45,7 @@     -- * Creation   , gEncoder   , gDecoder+  , gObjEncoder      -- * Reexports   , module Data.Tagged@@ -54,16 +54,21 @@   ) where  import           Generics.SOP+import           Generics.SOP.Record           (IsRecord) -import           Control.Lens                  (findOf, folded, isn't, _Left)+import           Control.Lens                  (findOf, folded, isn't, ( # ),+                                                _Empty, _Left) import           Control.Monad                 ((>=>)) import           Control.Monad.Except          (lift, throwError) import           Control.Monad.Reader          (runReaderT) import           Control.Monad.State           (modify)  import qualified Data.Char                     as Char+import           Data.Function                 ((&)) import           Data.Maybe                    (fromMaybe) +import           Data.Foldable                 (foldl')+ import           Data.List.NonEmpty            (NonEmpty)  import           Data.ByteString               (ByteString)@@ -77,11 +82,11 @@ import qualified Data.Tagged                   as T  import           Waargonaut                    (Json)+import           Waargonaut.Types              (JObject, WS)  import           Waargonaut.Encode             (Encoder, Encoder') import qualified Waargonaut.Encode             as E - import           HaskellWorks.Data.Positioning (Count)  import           Waargonaut.Decode             (Decoder)@@ -276,6 +281,18 @@   -- Will be encoded as: @ {"Foo":"Fred"} @   --   | ConstructorNameAsKey++  -- | Encode the newtype value as an object, treaing the field accessor as the "key", and+  -- passing that field name through the '_optionsFieldName' function.+  --+  -- @+  -- newtype Foo = Foo { deFoo :: Text }+  --+  -- let x = Foo "Fred"+  -- @+  --+  -- Will be encoded as: @ {"deFoo":"Fred"} @+  | FieldNameAsKey   deriving (Show, Eq)  -- | The configuration options for creating 'Generic' encoder or decoder values.@@ -335,7 +352,7 @@ -- instance JsonEncode GWaarg Foo where --   mkEncoder = Tagged fooEncoderIWroteEarlier -- @---+ class JsonEncode t a where   mkEncoder :: Applicative f => Tagged t (Encoder f a) @@ -406,14 +423,7 @@   JsonZero :: ConstructorName -> JsonInfo '[]   JsonOne  :: Tag -> JsonInfo '[a]   JsonMul  :: SListI xs => Tag -> JsonInfo xs-  JsonRec  :: SListI xs => Tag -> NP (K String) xs -> JsonInfo xs--modFieldName-  :: Options-  -> String-  -> Text-modFieldName opts =- Text.pack . _optionsFieldName opts+  JsonRec  :: SListI xs => Tag -> NP (K Text) xs -> JsonInfo xs  inObj :: Encoder' a -> String -> Encoder' a inObj en t = E.mapLikeObj' (E.atKey' (Text.pack t) en)@@ -455,8 +465,8 @@ jInfoFor opts _ tag (Record n fs) =   JsonRec (tag n) (hliftA fname fs)   where-    fname :: FieldInfo a -> K String a-    fname (FieldInfo name) = K $ _optionsFieldName opts name+    fname :: FieldInfo a -> K Text a+    fname (FieldInfo name) = K . Text.pack $ _optionsFieldName opts name  jsonInfo   :: forall a.@@ -468,13 +478,13 @@   -> NP JsonInfo (Code a) jsonInfo opts pa =   case datatypeInfo pa of-    Newtype _ _ c  -> JsonOne (newtypename (constructorName c)) :* Nil-    ADT     _ n cs -> hliftA (jInfoFor opts n (tag cs)) cs-  where-    newtypename n = case _optionsNewtypeWithConsName opts of-      Unwrap               -> NoTag-      ConstructorNameAsKey -> Tag (_optionsFieldName opts n)+    Newtype _ n c -> case _optionsNewtypeWithConsName opts of+      Unwrap               -> JsonOne NoTag :* Nil+      ConstructorNameAsKey -> JsonOne (Tag $ _optionsFieldName opts n) :* Nil+      FieldNameAsKey       -> jInfoFor opts n (Tag . _optionsFieldName opts) c :* Nil +    ADT _ n cs -> hliftA (jInfoFor opts n (tag cs)) cs+  where     tag :: NP ConstructorInfo (Code a) -> ConstructorName -> Tag     tag (_ :* Nil) = const NoTag     tag _          = Tag@@ -514,6 +524,65 @@     pjE = Proxy :: Proxy (JsonEncode t)     pt  = Proxy :: Proxy t +-- | Create a 'Tagged' 'ObjEncoder' for type @ a @, tagged by @ t @.+--+-- This isn't compatible with the 'JsonEncode' typeclass because it creates an+-- 'ObjEncoder' and for consistency reasons the 'JsonEncode' typeclass produces+-- 'Encoder's.+--+-- However it lets you more easily access the 'Data.Functor.Contravariant.Contravariant'+-- functionality that is part of the 'ObjEncoder' type.+--+-- @+-- data Foo = Foo { fooA :: Text, fooB :: Int } deriving (Eq, Show)+-- deriveGeneric ''Foo+--+-- objEncFoo :: Applicative f => ObjEncoder f Foo+-- objEncFoo = untag $ gObjEncoder (defaultOps { _optionsFieldName = drop 3 })+--+-- @+--+-- NB: This function overrides the newtype options to use the 'FieldNameAsKey' option to+-- be consistent with the behaviour of the record encoding.+--+gObjEncoder+  :: forall t a f xs.+     ( Generic a+     , Applicative f+     , HasDatatypeInfo a+     , All2 (JsonEncode t) (Code a)+     , IsRecord a xs+     )+  => Options+  -> Tagged t (E.ObjEncoder f a)+gObjEncoder opts = Tagged . E.objEncoder $ \a -> hcollapse $ hcliftA2+  (Proxy :: Proxy (All (JsonEncode t)))+  createObject+  (jsonInfo (opts { _optionsNewtypeWithConsName = FieldNameAsKey }) (Proxy :: Proxy a))+  (unSOP $ from a)+  where+    createObject :: ( All (JsonEncode t) ys+                    , Applicative f+                    )+                 => JsonInfo ys+                 -> NP I ys+                 -> K (f (JObject WS Json)) ys+    createObject (JsonRec _ fields) cs = K . pure .+      foldl' (&) (_Empty # ()) . hcollapse $ hcliftA2 pjE toObj fields cs++    createObject (JsonOne (Tag t)) (I a :* Nil) = K . pure $+      E.onObj' (Text.pack t) (E.asJson' (T.proxy mkEncoder pt) a) E.json (_Empty # ())++    -- IsRecord constraint should make this impossible.+    createObject _ _ =+      error "The impossible has happened. Please report this as a bug: https://github.com/qfpl/waargonaut"++    toObj :: JsonEncode t x => K Text x -> I x -> K (JObject WS Json -> JObject WS Json) x+    toObj f a = K $ E.onObj' (unK f) (E.asJson' (T.proxy mkEncoder pt) (unI a)) E.json++    pt = Proxy :: Proxy t+    pjE = Proxy :: Proxy (JsonEncode t)+ gEncoder'   :: forall xs f t.      ( All (JsonEncode t) xs@@ -537,11 +606,11 @@     ik :: JsonEncode t x => I x -> K Json x     ik = K . E.asJson' (T.proxy mkEncoder pT) . unI -gEncoder' p pT opts (JsonRec tag fields) cs    =+gEncoder' p pT _ (JsonRec tag fields) cs    =   tagVal tag . enc . hcollapse $ hcliftA2 p tup fields cs   where-    tup :: JsonEncode t x => K String x -> I x -> K (Text, Json) x-    tup f a = K ( modFieldName opts (unK f)+    tup :: JsonEncode t x => K Text x -> I x -> K (Text, Json) x+    tup f a = K ( unK f                 , E.asJson' (T.proxy mkEncoder pT) (unI a)                 ) @@ -657,10 +726,10 @@   where     err = throwError (ConversionFailure "Generic List Decode Failed") -mkGDecoder2 opts pJDec cursor (JsonRec tag fields) = do+mkGDecoder2 _ pJDec cursor (JsonRec tag fields) = do   c' <- D.down cursor   hsequenceK $ hcliftA pJDec (mapKK (decodeAtKey c')) fields   where     decodeAtKey c k = unTagVal tag (-      D.withCursor $ D.fromKey (modFieldName opts k) D.rank+      D.withCursor $ D.fromKey k D.rank       ) c
src/Waargonaut/Lens.hs view
@@ -4,6 +4,10 @@ {-# LANGUAGE RankNTypes            #-} {-# LANGUAGE TupleSections         #-} {-# LANGUAGE TypeFamilies          #-}+-- |+--+-- Some high level prisms for interacting with something that could be JSON.+-- module Waargonaut.Lens   (     -- * Prisms
src/Waargonaut/Prettier.hs view
@@ -77,9 +77,9 @@ newtype IndentStep = IndentStep Natural   deriving (Eq, Show) --- | Encode an @a@ directly to a 'ByteString' using the provided 'Encoder', the--- output will have newlines and indentation added based on the 'InlineOption' and--- 'NumSpaces'.+-- | Encode an @a@ directly to a 'Data.Text' using+-- the provided 'Encoder', the output will have newlines and+-- indentation added based on the 'InlineOption' and 'NumSpaces'. -- -- @ -- let two = successor' $ successor' zero'@@ -167,5 +167,5 @@       where         longestKey = maybe 1 (+1) $ L.maximumOf (objelems . L.folded . jsonAssocKey . _Wrapped . L.to length) j -    setnested = objelems . CS.elemsLast . CS.elemVal . jsonAssocVal %~+    setnested = objelems . traverse . jsonAssocVal %~       prettyJson inlineOpt (IndentStep step) (NumSpaces $ w <> step)
src/Waargonaut/Test.hs view
@@ -18,7 +18,7 @@ import qualified Waargonaut.Decode       as D import           Waargonaut.Decode.Error (DecodeError) --- | Test a 'Encoder' and 'Decoder' pair are able to maintain the 'round trip'+-- | Test a 'Encoder' and 'Decoder' pair are able to maintain the "round trip" -- property. That is, if you encode a given value, and then decode it, you should -- have the exact same value that you started with. roundTripSimple
src/Waargonaut/Types/CommaSep.hs view
@@ -160,19 +160,19 @@  instance Monoid ws => Witherable (CommaSeparated ws) where --- | Isomorphism between the internal pieces of a 'CommaSeparated' element.+-- | Isomorphism between the internal pieces of a 'Waargonaut.Types.CommaSep.CommaSeparated' element. _CommaSeparated :: Iso (CommaSeparated ws a) (CommaSeparated ws' b) (ws, Maybe (Elems ws a)) (ws', Maybe (Elems ws' b)) _CommaSeparated = iso (\(CommaSeparated ws a) -> (ws,a)) (uncurry CommaSeparated) {-# INLINE _CommaSeparated #-} --- | Cons elements onto a 'CommaSeparated' with provided whitespace information.+-- | Cons elements onto a 'Waargonaut.Types.CommaSep.CommaSeparated' with provided whitespace information. -- If you don't need explicit whitespace then the 'Cons' instance is more straightforward. consCommaSep :: Monoid ws => ((Comma,ws),a) -> CommaSeparated ws a -> CommaSeparated ws a consCommaSep (ews,a) = over (_CommaSeparated . _2) (pure . maybe new (consElems (ews,a)))   where new = Elems mempty (Elem a Nothing) {-# INLINE consCommaSep #-} --- | Attempt to "uncons" elements from the front of a 'CommaSeparated' without+-- | Attempt to "uncons" elements from the front of a 'Waargonaut.Types.CommaSep.CommaSeparated' without -- discarding the elements' whitespace information. If you don't need explicit -- whitespace then the 'Cons' instance is more straightforward. unconsCommaSep :: Monoid ws => CommaSeparated ws a -> Maybe ((Maybe (Comma,ws), a), CommaSeparated ws a)@@ -195,17 +195,18 @@     then es & elemsLast . traverse %%~ f     else es & elemsElems . ix i . traverse %%~ f --- | Convert a list of 'a' to a 'CommaSeparated' list, with no whitespace.+-- | Convert a list of @a@ to a 'Waargonaut.Types.CommaSep.CommaSeparated' list, with no whitespace. fromList :: (Monoid ws, Semigroup ws) => [a] -> CommaSeparated ws a fromList = foldr cons mempty {-# INLINE fromList #-} --- | Convert a 'CommaSeparated' of 'a' to @[a]@, discarding whitespace.+-- | Convert a 'Waargonaut.Types.CommaSep.CommaSeparated' of @a@ to @[a]@, discarding whitespace. toList :: CommaSeparated ws a -> [a] toList = maybe [] g . (^. _CommaSeparated . _2) where   g e = snoc (e ^.. elemsElems . traverse . elemVal) (e ^. elemsLast . elemVal) {-# INLINE toList #-} +-- | Attempt convert a 'CommaSeparated' to some other value using the given functions. fromCommaSep   :: Traversal' j (CommaSeparated ws x)   -> v@@ -223,7 +224,7 @@       $ traverse decoder els   -- Try to decode the values {-# INLINE fromCommaSep #-} --- | Parse a 'CommaSeparated' data structure.+-- | Parse a 'Waargonaut.Types.CommaSep.CommaSeparated' data structure. -- -- >>> testparse (parseCommaSeparated (char '[') (char ']') parseWhitespace charWS) "[]" -- Right (CommaSeparated (WS []) Nothing)
src/Waargonaut/Types/CommaSep/Elem.hs view
@@ -5,6 +5,11 @@ {-# LANGUAGE FunctionalDependencies #-} {-# LANGUAGE MultiParamTypeClasses  #-} {-# LANGUAGE NoImplicitPrelude      #-}+-- |+--+-- Data structures and functions for managing a single element in a+-- 'Waargonaut.Types.CommaSep.CommaSeparated' structure.+-- module Waargonaut.Types.CommaSep.Elem   (     -- * Types@@ -78,8 +83,8 @@ parseCommaTrailingMaybe =   C.optional . liftA2 (,) parseComma --- | Data type to represent a single element in a 'CommaSeparated' list. Carries--- information about it's own trailing whitespace. Denoted by the 'f'.+-- | Data type to represent a single element in a 'Waargonaut.Types.CommaSep.CommaSeparated' list. Carries+-- information about it's own trailing whitespace. Denoted by the @f@. data Elem f ws a = Elem   { _elemVal      :: a   , _elemTrailing :: f (Comma, ws)@@ -127,6 +132,7 @@ floopId :: Monoid ws => Iso' (Identity (Comma,ws)) (Maybe (Comma,ws)) floopId = iso (Just . runIdentity) (pure . fromMaybe (Comma, mempty)) +-- | 'Control.Lens.Iso' between an 'Elem' that is not on the trailing element and one that is. _ElemTrailingIso   :: ( Monoid ws      , Monoid ws'
src/Waargonaut/Types/CommaSep/Elems.hs view
@@ -7,6 +7,10 @@ {-# LANGUAGE MultiParamTypeClasses  #-} {-# LANGUAGE NoImplicitPrelude      #-} {-# LANGUAGE TupleSections          #-}+-- |+--+-- Data structures and functions for handling the elements contained in a 'Waargonaut.Types.CommaSep.CommaSeparated' structure.+-- module Waargonaut.Types.CommaSep.Elems   (     -- * Types@@ -110,10 +114,12 @@   (<>) (Elems as alast) (Elems bs blast) =     Elems (snoc as (alast ^. from _ElemTrailingIso) <> bs) blast +-- | Add a value to the beginning of the 'Elems' consElems :: Monoid ws => ((Comma,ws), a) -> Elems ws a -> Elems ws a consElems (ews,a) e = e & elemsElems %~ cons (Elem a (Identity ews)) {-# INLINE consElems #-} +-- | Attempt to remove the initial value off the front of an 'Elems' unconsElems :: Monoid ws => Elems ws a -> ((Maybe (Comma,ws), a), Maybe (Elems ws a)) unconsElems e = maybe (e', Nothing) (\(em, ems) -> (idT em, Just $ e & elemsElems .~ ems)) es'   where@@ -122,7 +128,7 @@     idT x = (x ^. elemTrailing . to (Just . runIdentity), x ^. elemVal) {-# INLINE unconsElems #-} --- | Parse the elements of a 'CommaSeparated' list, handling the optional trailing comma and its whitespace.+-- | Parse the elements of a 'Waargonaut.Types.CommaSep.CommaSeparated' list, handling the optional trailing comma and its whitespace. -- -- >>> testparse (parseCommaSeparatedElems parseWhitespace alphaNum) "a, b, c, d" -- Right (Elems {_elemsElems = [Elem {_elemVal = 'a', _elemTrailing = Identity (Comma,WS [Space])},Elem {_elemVal = 'b', _elemTrailing = Identity (Comma,WS [Space])},Elem {_elemVal = 'c', _elemTrailing = Identity (Comma,WS [Space])}], _elemsLast = Elem {_elemVal = 'd', _elemTrailing = Nothing}})
src/Waargonaut/Types/JArray.hs view
@@ -6,7 +6,10 @@ {-# LANGUAGE NoImplicitPrelude     #-} {-# LANGUAGE RankNTypes            #-} {-# LANGUAGE TypeFamilies          #-}--- | JSON Array representation and functions.+-- |+--+-- JSON Array representation and functions.+-- module Waargonaut.Types.JArray   (     -- * Types
src/Waargonaut/Types/JChar.hs view
@@ -117,10 +117,12 @@ -- instance AsJChar Char HeXDigit where -- Don't implement this, it's not a lawful prism. +-- | Convert a 'JChar' to a Haskell 'Char' jCharToChar :: JChar HeXDigit -> Char jCharToChar (UnescapedJChar uejc) = review _Unescaped uejc jCharToChar (EscapedJChar ejc)    = escapedToChar ejc +-- | Attempt to convert a Haskell 'Char' to a JSON acceptable 'JChar' charToJChar :: Char -> Maybe (JChar HeXDigit) charToJChar c =   (UnescapedJChar <$> preview _Unescaped c) <|>@@ -131,7 +133,7 @@                | otherwise                    = Nothing  -- | Convert a 'Char' to 'JChar HexDigit' and replace any invalid values with--- @U+FFFD@ as per the 'Text' documentation.+-- @U+FFFD@ as per the 'Data.Text.Text' documentation. -- -- Refer to <https://hackage.haskell.org/package/text/docs/Data-Text.html#g:2 'Text'> documentation for more info. --@@ -140,7 +142,7 @@   where scalarReplacement = EscapedJChar (Hex (HexDigit4 D.xf D.xf D.xf D.xd)) {-# INLINE utf8CharToJChar #-} --- | Try to convert a 'JChar' to a 'Text' safe 'Char' value. Refer to the link for more info:+-- | Try to convert a 'JChar' to a 'Data.Text.Text' safe 'Char' value. Refer to the link for more info: -- https://hackage.haskell.org/package/text-1.2.3.0/docs/Data-Text-Internal.html#v:safe jCharToUtf8Char :: JChar HeXDigit -> Maybe Char jCharToUtf8Char = utf8SafeChar . jCharToChar
src/Waargonaut/Types/JChar/Escaped.hs view
@@ -47,7 +47,7 @@                                                    hexDigit4ToChar,                                                    parseHexDigit4) import           Waargonaut.Types.Whitespace      (Whitespace (..),-                                                   unescapedWhitespaceChar,+                                                   escapedWhitespaceChar,                                                    _WhitespaceChar)  -- $setup@@ -177,15 +177,17 @@   in     char '\\' *> (z <|> h) +-- | Convert an 'Escaped' character to a Haskell 'Char' escapedToChar :: Escaped HeXDigit -> Char escapedToChar = \case   QuotationMark  -> '"'   ReverseSolidus -> '\\'   Solidus        -> '/'   Backspace      -> '\b'-  WhiteSpace wc  -> unescapedWhitespaceChar wc+  WhiteSpace wc  -> escapedWhitespaceChar wc   Hex hd         -> hexDigit4ToChar hd +-- | Attempt to convert a Haskell 'Char' to an 'Escaped' JSON character charToEscaped :: Char -> Maybe (Escaped HeXDigit) charToEscaped c = case c of   '"'  -> Just QuotationMark
src/Waargonaut/Types/JChar/HexDigit4.hs view
@@ -6,7 +6,10 @@ {-# LANGUAGE LambdaCase             #-} {-# LANGUAGE MultiParamTypeClasses  #-} {-# LANGUAGE NoImplicitPrelude      #-}--- | Types and functions for handling \\u0000 values in JSON.+-- |+--+-- Types and functions for handling \\u0000 values in JSON.+-- module Waargonaut.Types.JChar.HexDigit4   (     -- * Types@@ -91,7 +94,7 @@ hexDigit4ToChar (HexDigit4 a b c d) = chr (D._HeXDigitsIntegral (Right $ a :| [b,c,d]))  -- | Try to convert a Haskell 'Char' to a JSON acceptable character. NOTE: This--- cannot preserve the upper or lower casing of any original 'Json' data structure+-- cannot preserve the upper or lower casing of any original 'Waargonaut.Types.Json.Json' data structure -- inputs that may have been used to create this 'Char'. Also the JSON RFC -- specifies a "limited" range of @U+0000@ to @U+FFFF@ as permissible as a six -- character sequence: @\u0000@.
src/Waargonaut/Types/JChar/Unescaped.hs view
@@ -1,4 +1,7 @@--- | Types and functions for handling valid unescaped characters in JSON.+-- |+--+-- Types and functions for handling valid unescaped characters in JSON.+-- module Waargonaut.Types.JChar.Unescaped   (     -- * Types
src/Waargonaut/Types/JNumber.hs view
@@ -3,7 +3,10 @@ {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE NoImplicitPrelude     #-} {-# LANGUAGE TypeFamilies          #-}--- | Representation of a JSON number and its various components.+-- | +--+-- Representation of a JSON number and its various components.+-- module Waargonaut.Types.JNumber   (     -- * Types@@ -87,7 +90,7 @@   | JIntInt digit [DecDigit]   deriving (Eq, Ord, Show) --- | Type alias to allow us to constrain the first 'digit' type.+-- | Type alias to allow us to constrain the first @digit@ type. type JInt = JInt' DecDigit  -- | Prism for JSON zeroes.
src/Waargonaut/Types/JObject.hs view
@@ -127,7 +127,7 @@   bitraverse f g (JObject c) = JObject <$> bitraverse f (bitraverse f g) c  -- | Without having an obviously correct "first" or "last" decision on which--- 'JString' key is the "right" one to use, a 'JObject' can only be indexed by a+-- 'Waargonaut.Types.JString' key is the "right" one to use, a 'JObject' can only be indexed by a -- numeric value. instance Monoid ws => Ixed (JObject ws a) where   ix i f (JObject cs) = JObject <$> ix i (traverse f) cs@@ -149,7 +149,7 @@   deriving (Eq, Show, Functor, Foldable, Traversable)  -- |--- 'Prism' for working with a 'JObject' as a 'MapLikeObj'. This optic will keep+-- 'Control.Lens.Prism' for working with a 'JObject' as a 'MapLikeObj'. This optic will keep -- the first unique key on a given 'JObject' and this information is not -- recoverable. If you want to create a 'MapLikeObj' from a 'JObject' and keep -- what is removed, then use the 'toMapLikeObj' function.
src/Waargonaut/Types/Json.hs view
@@ -176,8 +176,8 @@                _         -> Left x        ) --- | Top level Json type, we specialise the whitespace to 'WS' and the 'digit'--- type to 'Digit'. Also defining that our structures can recursively only contain+-- | Top level Json type, we specialise the whitespace to 'WS' and the @digit@+-- type to 'Data.Digit.Digit'. Also defining that our structures can recursively only contain -- 'Json' types. newtype Json   = Json (JType WS Json)@@ -210,7 +210,7 @@ jtypeTraversal = bitraverse pure  -- |--- A 'Control.Lens.Traversal'' over the 'a' at the given 'Text' key on a JSON object.+-- A 'Control.Lens.Traversal'' over the @a@ at the given 'Text' key on a JSON object. -- -- >>> E.simplePureEncodeTextNoSpaces E.json (obj & oat "c" ?~ E.asJson' E.int 33) -- "{\"c\":33,\"a\":33,\"b\":\"Fred\"}"@@ -221,7 +221,7 @@ oat k = _JObj . _1 . _MapLikeObj . at k  -- |--- A 'Control.Lens.Traversal'' over the 'a' at the given 'Int' position in a JSON object.+-- A 'Control.Lens.Traversal'' over the @a@ at the given 'Int' position in a JSON object. -- -- >>> E.simplePureEncodeTextNoSpaces E.json (obj & oix 0 .~ E.asJson' E.int 1) -- "{\"a\":1,\"b\":\"Fred\"}"@@ -231,7 +231,7 @@ oix i = _JObj . _1 . ix i  -- |--- A 'Control.Lens.Traversal'' over the 'a' at the given 'Int' position in a JSON array.+-- A 'Control.Lens.Traversal'' over the @a@ at the given 'Int' position in a JSON array. -- -- >>> E.simplePureEncodeTextNoSpaces E.json ((E.asJson' (E.list E.int) [1,2,3]) & aix 0 .~ E.asJson' E.int 99) -- "[99,2,3]"@@ -261,7 +261,7 @@   <$ text "null"   <*> ws --- | Parse a 'true' or 'false'.+-- | Parse a @true@ or @false@. -- -- >>> testparse (parseJBool (return ())) "true" -- Right (JBool True ())
src/Waargonaut/Types/Whitespace.hs view
@@ -1,7 +1,10 @@ {-# LANGUAGE FlexibleInstances     #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE TypeFamilies          #-}--- | Parsers and builders for whitespace characters in our JSON.+-- | +--+-- Parsers and builders for whitespace characters in our JSON.+-- module Waargonaut.Types.Whitespace   (     Whitespace (..)
+ test/Generics.hs view
@@ -0,0 +1,31 @@+{-# LANGUAGE OverloadedStrings #-}+module Generics+  ( genericsTests+  ) where++import           Test.Tasty         (TestTree, testGroup)+import           Test.Tasty.HUnit   (Assertion, testCase, (@?=))++import           Waargonaut.Encode  (Encoder)+import qualified Waargonaut.Encode  as E++import qualified Types.Common       as C++import qualified Waargonaut.Generic as G++testGenericFieldNameFunctionUsedOnce :: Assertion+testGenericFieldNameFunctionUsedOnce = do+  let+    encFudge :: Applicative f => Encoder f C.Fudge+    encFudge = G.untag . G.gEncoder $ C.fudgeJsonOpts+      { G._optionsFieldName = tail+      }++    expected = "{\"udge\":\"Chocolate\"}"++  E.simplePureEncodeTextNoSpaces encFudge C.testFudge @?= expected++genericsTests :: TestTree+genericsTests = testGroup "Generics"+  [ testCase "Options fieldName function is used only once" testGenericFieldNameFunctionUsedOnce+  ]
test/Golden.hs view
@@ -9,6 +9,7 @@ import           System.FilePath            (FilePath, takeBaseName) import           System.IO                  (IO, print) +import Data.Semigroup ((<>)) import           Data.Either                (either) import           Data.Function              (($)) @@ -22,6 +23,8 @@ import qualified Waargonaut.Decode          as D import qualified Waargonaut.Encode          as E +import qualified Prettier.NestedObjs as WaargP+ readAndEncodeFile :: FilePath -> IO ByteString readAndEncodeFile = readFile   >=> D.decodeFromByteString parseOnly D.json . toStrict@@ -34,4 +37,6 @@   pure . testGroup "Golden Tests" $     [ goldenVsString (takeBaseName input) input (readAndEncodeFile input)     | input <- fs+    ] <>+    [ WaargP.testGoldenPrettyNested     ]
test/Main.hs view
@@ -31,6 +31,7 @@ import qualified Encoder import qualified Encoder.Laws import qualified Golden+import qualified Generics import qualified Json import qualified Properties @@ -89,6 +90,8 @@      , Decoder.decoderTests     , Encoder.encoderTests++    , Generics.genericsTests      , goldens     ]
+ test/Prettier/NestedObjs.hs view
@@ -0,0 +1,124 @@+{-# LANGUAGE NoImplicitPrelude #-}+{-# LANGUAGE OverloadedStrings #-}++-- | Test case from+--+-- https://gist.github.com/tonymorris/420fff469463037f5ed3f76339cb51e2+--+module Prettier.NestedObjs where++import           Data.Functor.Identity+import           Prelude++import qualified Data.Text.Lazy.Encoding as TLE++import           Data.Text.Lazy+import           Natural++import           Waargonaut.Encode+import           Waargonaut.Prettier++import           Test.Tasty              (TestTree)+import           Test.Tasty.Golden       (goldenVsString)++data Wibble =+  Wibble {+    _a1 :: String+  , _a2 :: String+  , _a3 :: String+  , _a4 :: String+  } deriving (Eq, Ord, Show)++data Wobble =+  Wobble {+    _b1 :: Wibble+  , _b2 :: Wibble+  } deriving (Eq, Ord, Show)++testWibble1 ::+  Wibble+testWibble1 =+  Wibble+    "hi1"+    "bye1"+    "hello1"+    "goodbye1"++testWibble2 ::+  Wibble+testWibble2 =+  Wibble+    "hi2"+    "bye2"+    "hello2"+    "goodbye2"++testWobble ::+  Wobble+testWobble =+  Wobble+    testWibble1+    testWibble2++wibbleEncoder ::+  Applicative f =>+  Encoder f Wibble+wibbleEncoder =+  mapLikeObj $ \w ->+    atKey' "a1" string (_a1 w) .+    atKey' "a2" string (_a2 w) .+    atKey' "a3" string (_a3 w) .+    atKey' "a4" string (_a4 w)++wobbleEncoder ::+  Applicative f =>+  Encoder f Wobble+wobbleEncoder =+  mapLikeObj $ \w ->+    atKey' "b1" wibbleEncoder (_b1 w) .+    atKey' "b2" wibbleEncoder (_b2 w)++prettyied :: Text+prettyied =+  let two = successor' (successor' zero')+  in  runIdentity (simpleEncodePretty ArrayOnly (IndentStep two) (NumSpaces two) wobbleEncoder testWobble)++-- Expected this+{-+{+  "b1": {+    "a1": "hi1",+    "a2": "bye1",+    "a3": "hello1",+    "a4": "goodbye1"+  },+  "b2": {+    "a1": "hi2",+    "a2": "bye2",+    "a3": "hello2",+    "a4": "goodbye2"+  }+}+-}+--+-- Got this+{-+{+  "b1": {+  "a1": "hi1",+  "a2": "bye1",+  "a3": "hello1",+  "a4": "goodbye1"+},+  "b2": {+    "a1": "hi2",+    "a2": "bye2",+    "a3": "hello2",+    "a4": "goodbye2"+  }+}+-}+testGoldenPrettyNested :: TestTree+testGoldenPrettyNested = goldenVsString "'encodePretty' simple nested objects"+  "test/Prettier/pretty_nested_objs.json"+  (pure $ TLE.encodeUtf8 prettyied)
test/Types/Common.hs view
@@ -23,6 +23,7 @@    , testImageDataType   , testFudge+  , fudgeJsonOpts   , imageDecodeGeneric   , imageDecodeSuccinct @@ -132,7 +133,7 @@ instance JsonEncode GWaarg Image where mkEncoder = gEncoder imageOpts instance JsonDecode GWaarg Image where mkDecoder = gDecoder imageOpts -newtype Fudge = Fudge Text+newtype Fudge = Fudge { unCrepe :: Text }   deriving (Eq, Show, GHC.Generic)  instance Generic Fudge
waargonaut.cabal view
@@ -10,7 +10,7 @@ -- PVP summary:      +-+------- breaking API changes --                   | | +----- non-breaking API additions --                   | | | +--- code changes with no API change-version:             0.6.2.0+version:             0.8.0.0  -- A short (one-line) description of the package. synopsis:            JSON wrangling@@ -143,41 +143,40 @@   ghc-options:         -Wall    -- Other library packages from which modules are imported.-  build-depends:       base                 >= 4.9     && < 4.13-                     , lens                 >= 4.15    && < 4.18-                     , mtl                  >= 2.2.2   && < 2.3-                     , text                 >= 1.2     && < 1.3-                     , bytestring           >= 0.10.6  && < 0.11-                     , parsers              >= 0.12    && < 0.13-                     , digit                >= 0.7     && < 0.8-                     , semigroups           >= 0.8.4   && < 0.20-                     , scientific           >= 0.3     && < 0.4-                     , distributive         >= 0.5     && < 0.7-                     , nats                 >= 1       && <1.2-                     , zippers              >= 0.2     && < 0.3-                     , vector               >= 0.12    && < 0.13-                     , errors               >= 2.2     && < 2.4-                     , hoist-error          >= 0.2     && < 0.3-                     , containers           >= 0.5.6   && < 0.7-                     , witherable           >= 0.2     && < 0.4-                     , generics-sop         >= 0.4     && < 0.5-                     , mmorph               >= 1.1     && < 1.2-                     , transformers         >= 0.4     && < 0.6-                     , bifunctors           >= 5       && < 5.6-                     , contravariant        >= 1.4     && < 1.6-                     , wl-pprint-annotated  >= 0.1     && < 0.2--                     , hw-json              >= 1.0.0.2 && < 1.2-                     , hw-prim              >= 0.6     && < 0.7-                     , hw-balancedparens    >= 0.2     && < 0.3-                     , hw-rankselect        >= 0.13    && < 0.14-                     , hw-bits              >= 0.7     && < 0.8--                     , tagged               >= 0.8.5   && < 0.9-                     , semigroupoids        >= 5.2.2   && < 5.4-                     , natural              >= 0.3     && < 0.4-                     , unordered-containers >= 0.2.9   && < 0.3-                     , attoparsec           >= 0.13    && < 0.15+  build-depends:       base                    >= 4.9     && < 4.13+                     , lens                    >= 4.15    && < 4.18+                     , mtl                     >= 2.2.2   && < 2.3+                     , text                    >= 1.2     && < 1.3+                     , bytestring              >= 0.10.6  && < 0.11+                     , parsers                 >= 0.12    && < 0.13+                     , digit                   >= 0.7     && < 0.9+                     , semigroups              >= 0.8.4   && < 0.20+                     , scientific              >= 0.3     && < 0.4+                     , distributive            >= 0.5     && < 0.7+                     , nats                    >= 1       && <1.2+                     , zippers                 >= 0.2     && < 0.4+                     , vector                  >= 0.12    && < 0.13+                     , errors                  >= 2.2     && < 2.4+                     , hoist-error             >= 0.2     && < 0.3+                     , containers              >= 0.5.6   && < 0.7+                     , witherable              >= 0.2     && < 0.4+                     , generics-sop            >= 0.4     && < 0.5+                     , records-sop             >= 0.1     && < 0.2+                     , mmorph                  >= 1.1     && < 1.2+                     , transformers            >= 0.4     && < 0.6+                     , bifunctors              >= 5       && < 5.6+                     , contravariant           >= 1.4     && < 1.6+                     , wl-pprint-annotated     >= 0.1     && < 0.2+                     , hw-json-standard-cursor >= 0.2.1.1 && < 0.3+                     , hw-prim                 >= 0.6     && < 0.7+                     , hw-balancedparens       >= 0.2     && < 0.4+                     , hw-rankselect           >= 0.13    && < 0.14+                     , hw-bits                 >= 0.7     && < 0.8+                     , tagged                  >= 0.8.5   && < 0.9+                     , semigroupoids           >= 5.2.2   && < 5.4+                     , natural                 >= 0.3     && < 0.4+                     , unordered-containers    >= 0.2.9   && < 0.3+                     , attoparsec              >= 0.13    && < 0.15    -- Directories containing source files.   hs-source-dirs:      src@@ -192,9 +191,9 @@   hs-source-dirs:      test    build-depends:       base             >= 4.9  && < 4.13-                     , hedgehog         >= 0.6  && < 0.7+                     , hedgehog         >= 0.6  && < 1.2                      , text             >= 1.2  && < 1.3-                     , digit            >= 0.7  && < 0.8+                     , digit            >= 0.7  && < 0.9                      , attoparsec       >= 0.13 && < 0.15                      , doctest          >= 0.9.7                      , filepath         >= 1.3@@ -214,8 +213,10 @@                      , Types.Json                      , Types.Whitespace +                     , Prettier.NestedObjs                      , Properties                      , Golden+                     , Generics                      , Laws                      , Utils                      , Encoder@@ -228,39 +229,38 @@   main-is:             Main.hs   hs-source-dirs:      test -  build-depends:       base                   >= 4.9    && < 4.13-                     , tasty                  >= 0.11   && < 1.3-                     , tasty-hunit            >= 0.10   && < 0.11-                     , tasty-expected-failure >= 0.11   && < 0.12-                     , hedgehog               >= 0.6    && < 0.7-                     , hedgehog-fn            >= 0.6    && < 0.7-                     , tasty-hedgehog         >= 0.2    && < 0.3-                     , lens                   >= 4.15   && < 4.18-                     , distributive           >= 0.5    && < 0.7-                     , bytestring             >= 0.10.6 && < 0.11-                     , digit                  >= 0.7    && < 0.8-                     , text                   >= 1.2    && < 1.3-                     , semigroups             >= 0.8.4  && < 0.20-                     , zippers                >= 0.2    && < 0.3-                     , vector                 >= 0.12   && < 0.13-                     , generics-sop           >= 0.4    && < 0.5-                     , attoparsec             >= 0.13   && < 0.15-                     , scientific             >= 0.3    && < 0.4-                     , tagged                 >= 0.8.5  && < 0.9-                     , mtl                    >= 2.2.2  && < 2.3-                     , semigroupoids          >= 5.2.2  && < 5.6-                     , containers             >= 0.5.6  && < 0.7-                     , natural                >= 0.3    && < 0.4-                     , contravariant          >= 1.4    && < 1.6-                     , filepath               >= 1.4    && < 1.5-                     , tasty-golden           >= 2.3    && < 2.4-                     , unordered-containers   >= 0.2.9  && < 0.3--                     , hw-balancedparens    >= 0.2     && < 0.3-                     , hw-rankselect        >= 0.13    && < 0.14-                     , hw-bits              >= 0.7     && < 0.8-                     , hw-json              >= 1.0.0.2 && < 1.2-                     , hw-prim              >= 0.6     && < 0.7+  build-depends:       base                    >= 4.9    && < 4.13+                     , tasty                   >= 0.11   && < 1.3+                     , tasty-hunit             >= 0.10   && < 0.11+                     , tasty-expected-failure  >= 0.11   && < 0.12+                     , hedgehog                >= 0.6    && < 1.2+                     , hedgehog-fn             >= 0.6    && < 2+                     , tasty-hedgehog          >= 0.2    && < 1.2+                     , lens                    >= 4.15   && < 4.18+                     , distributive            >= 0.5    && < 0.7+                     , bytestring              >= 0.10.6 && < 0.11+                     , digit                   >= 0.7    && < 0.9+                     , text                    >= 1.2    && < 1.3+                     , semigroups              >= 0.8.4  && < 0.20+                     , zippers                 >= 0.2    && < 0.4+                     , vector                  >= 0.12   && < 0.13+                     , generics-sop            >= 0.4    && < 0.5+                     , attoparsec              >= 0.13   && < 0.15+                     , scientific              >= 0.3    && < 0.4+                     , tagged                  >= 0.8.5  && < 0.9+                     , mtl                     >= 2.2.2  && < 2.3+                     , semigroupoids           >= 5.2.2  && < 5.6+                     , containers              >= 0.5.6  && < 0.7+                     , natural                 >= 0.3    && < 0.4+                     , contravariant           >= 1.4    && < 1.6+                     , filepath                >= 1.4    && < 1.5+                     , tasty-golden            >= 2.3    && < 2.4+                     , unordered-containers    >= 0.2.9  && < 0.3+                     , hw-balancedparens       >= 0.2     && < 0.4+                     , hw-rankselect           >= 0.13    && < 0.14+                     , hw-bits                 >= 0.7     && < 0.8+                     , hw-json-standard-cursor >= 0.2.1.1 && < 0.3+                     , hw-prim                 >= 0.6     && < 0.7                       , waargonaut