packages feed

waargonaut 0.5.0.0 → 0.5.1.0

raw patch · 6 files changed

+148/−24 lines, 6 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

+ Waargonaut.Decode: JsonTypeArray :: JsonType
+ Waargonaut.Decode: JsonTypeBool :: JsonType
+ Waargonaut.Decode: JsonTypeNull :: JsonType
+ Waargonaut.Decode: JsonTypeNumber :: JsonType
+ Waargonaut.Decode: JsonTypeObject :: JsonType
+ Waargonaut.Decode: JsonTypeString :: JsonType
+ Waargonaut.Decode: data JsonType
+ Waargonaut.Decode: jsonTypeAt :: JsonTypeAt a => a -> Maybe JsonType
+ Waargonaut.Decode: objectAsKeyValues :: Monad f => Decoder f k -> Decoder f v -> Decoder f [(k, v)]
+ Waargonaut.Decode: objectAsKeyValuesAt :: Monad f => Decoder f k -> Decoder f v -> JCurs -> DecodeResult f [(k, v)]
+ Waargonaut.Decode: withType :: Monad f => JsonType -> (JCurs -> DecodeResult f a) -> JCurs -> DecodeResult f a
+ Waargonaut.Decode.Error: TypeMismatch :: JsonType -> DecodeError
+ Waargonaut.Decode.Error: _TypeMismatch :: AsDecodeError r => Prism' r JsonType
+ Waargonaut.Decode.Internal: TypeMismatch :: JsonType -> DecodeError
+ Waargonaut.Decode.Internal: _TypeMismatch :: AsDecodeError r => Prism' r JsonType
+ Waargonaut.Decode.Types: JsonTypeArray :: JsonType
+ Waargonaut.Decode.Types: JsonTypeBool :: JsonType
+ Waargonaut.Decode.Types: JsonTypeNull :: JsonType
+ Waargonaut.Decode.Types: JsonTypeNumber :: JsonType
+ Waargonaut.Decode.Types: JsonTypeObject :: JsonType
+ Waargonaut.Decode.Types: JsonTypeString :: JsonType
+ Waargonaut.Decode.Types: data JsonType
+ Waargonaut.Decode.Types: instance HaskellWorks.Data.Json.Type.JsonTypeAt Waargonaut.Decode.Types.JCurs
+ Waargonaut.Decode.Types: jsonTypeAt :: JsonTypeAt a => a -> Maybe JsonType

Files

changelog.md view
@@ -1,5 +1,11 @@ # Revision history for waargonaut +## 0.5.1.0  -- 2019-01-02++* Fix order of `either` decoder to match documentation, `Right` decoder was not being attempted first.+* Expose functionality to check the 'type' of the JSON at the current cursor position.+* Update list decoder to check that we're at an array before attempting to decode. It will now fail when attempting to decode a list and something of type list is not found. Previously it would return an empty list when a number was present.+ ## 0.5.0.0  -- 2018-12-18  * Changed internal builder from `ByteString` to `Text` builder.
src/Waargonaut/Decode.hs view
@@ -20,6 +20,7 @@   , JCurs (..)   , ParseFn   , Err (..)+  , JsonType (..)      -- * Runners   , runDecode@@ -58,6 +59,10 @@   , fromKeyOptional   , atKeyOptional +    -- * Inspection+  , withType+  , jsonTypeAt+     -- * Provided Decoders   , leftwardCons   , rightwardSnoc@@ -81,6 +86,8 @@   , nonempty   , listAt   , list+  , objectAsKeyValuesAt+  , objectAsKeyValues   , withDefault   , maybeOrNull   , either@@ -97,6 +104,7 @@                                                             traverseOf, view,                                                             ( # ), (.~), (^.),                                                             _Left, _Wrapped)+import           Control.Monad.Error.Lens                  (throwing)  import           Prelude                                   (Bool, Bounded, Char,                                                             Eq, Int, Integral,@@ -111,8 +119,7 @@ import           Control.Monad.Morph                       (embed, generalize)  import           Control.Monad.Except                      (catchError, lift,-                                                            liftEither,-                                                            throwError)+                                                            liftEither) import           Control.Monad.Reader                      (ReaderT (..), ask,                                                             local, runReaderT) import           Control.Monad.State                       (MonadState)@@ -120,6 +127,7 @@ import           Control.Error.Util                        (note) import           Control.Monad.Error.Hoist                 ((<!?>), (<?>)) +import           Data.Bool                                 (Bool (..)) import           Data.Either                               (Either (..)) import qualified Data.Either                               as Either (either) import           Data.Foldable                             (Foldable, foldl,@@ -171,8 +179,11 @@ import           Waargonaut.Decode.Types                   (CursorHistory,                                                             DecodeResult (..),                                                             Decoder (..),-                                                            JCurs (..), ParseFn,-                                                            SuccinctCursor)+                                                            JCurs (..),+                                                            JsonType (..),+                                                            ParseFn,+                                                            SuccinctCursor,+                                                            jsonTypeAt)  -- | Function to define a 'Decoder' for a specific data type. --@@ -268,8 +279,17 @@ -- @ [*1,2,3] @ -- -- This function is essential when dealing with the inner elements of objects or--- arrays. As you must first move 'down' into the focus.+-- arrays. As you must first move 'down' into the focus. However, you cannot+-- move down into an empty list or empty object. The reason for this is that+-- there will be nothing in the index for the element at the first position.+-- Thus the movement will be considered invalid. --+-- These will fail if you attempt to move 'down':+--+-- @ *[] @+--+-- @ *{} @+-- down   :: Monad f   => JCurs@@ -308,7 +328,7 @@ moveToRankN newRank c =   if JC.balancedParens (c ^. _Wrapped) .?. Pos.lastPositionOf newRank   then pure $ c & _Wrapped . cursorRankL .~ newRank-  else throwError $ InputOutOfBounds newRank+  else throwing _InputOutOfBounds newRank  -- | Move the cursor rightwards 'n' times. --@@ -384,7 +404,7 @@    if JC.balancedParens c .?. Pos.lastPositionOf rnk     then liftEither (p cursorTxt)-    else throwError (InputOutOfBounds rnk)+    else throwing _InputOutOfBounds rnk  -- Internal function to record the current rank of the cursor into the zipper history recordRank@@ -506,7 +526,7 @@ fromKeyOptional k d c =   focus' =<< catchError (pure <$> moveToKey k c) (\de -> case de of     KeyNotFound _ -> pure Nothing-    _             -> throwError de)+    _             -> throwing _DecodeError de)   where     focus' = maybe (pure Nothing) (fmap Just . focus d) @@ -543,6 +563,28 @@   jsonAtCursor p curs >>=     liftEither . note (ConversionFailure m) . c +-- | Attempt to work with a 'JCurs' provided the type of 'Json' at the current+-- position matches your expectations.+--+-- Such as:+--+-- @+-- withType JsonTypeArray d+-- @+--+-- 'd' will only be entered if the cursor at the current position is a JSON+-- array: '[]'.+--+withType+  :: Monad f+  => JsonType+  -> (JCurs -> DecodeResult f a)+  -> JCurs+  -> DecodeResult f a+withType t d c =+  if maybe False (== t) $ jsonTypeAt (unJCurs c) then d c+  else throwing _TypeMismatch t+ -- | Higher order function for combining a folding function with repeated cursor -- movements. This lets you combine arbitrary cursor movements with an accumulating -- function.@@ -586,14 +628,14 @@ --   | C -- -- decodeMyEnum :: Monad f => Decoder f MyEnum--- decodeMyEnum = D.oneOf D.text "MyEnum"+-- decodeMyEnum = D.oneOf D.text \"MyEnum\" --   [ ("a", A) --   , ("b", B) --   , ("c", C) --   ] -- -- decodeMyEnumFromInt :: Monad f => Decoder f MyEnum--- decodeMyEnumFromInt = D.oneOf D.int "MyEnum"+-- decodeMyEnumFromInt = D.oneOf D.int \"MyEnum\" --   [ (1, A) --   , (2, B) --   , (3, C)@@ -613,7 +655,7 @@   foldr (\i x -> g i <!> x) err   where     g (a,b) = d >>= \t -> if t == a then pure b else err-    err = throwError (ConversionFailure l)+    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.@@ -779,7 +821,7 @@   -> Decoder f a   -> Decoder f b prismDOrFail' e p d = withCursor $-  focus d >=> Either.either (throwError . e) pure . matching p+  focus d >=> Either.either (throwing _DecodeError . e) pure . matching p  -- | Decode an 'Int'. int :: Monad f => Decoder f Int@@ -833,7 +875,7 @@   => Decoder f a   -> JCurs   -> DecodeResult f (NonEmpty a)-nonemptyAt elemD = down >=> \curs -> do+nonemptyAt elemD = withType JsonTypeArray $ down >=> \curs -> do   h <- focus elemD curs   DI.try (moveRight1 curs) >>= maybe     (pure $ h :| [])@@ -850,14 +892,34 @@   => Decoder f a   -> JCurs   -> DecodeResult f [a]-listAt elemD curs = DI.try (down curs) >>= maybe-  (pure mempty)-  (rightwardSnoc mempty elemD)+listAt elemD = withType JsonTypeArray $ \c ->+  DI.try (down c) >>= maybe (pure mempty) (rightwardSnoc mempty elemD)  -- | Helper function to simplify writing a '[]' decoder. list :: Monad f => Decoder f a -> Decoder f [a] list d = withCursor (listAt d) +-- | Try to decode an object using the given key and value 'Decoder's at the+-- given cursor.+objectAsKeyValuesAt+  :: Monad f+  => Decoder f k+  -> Decoder f v+  -> JCurs+  -> DecodeResult f [(k,v)]+objectAsKeyValuesAt keyD valueD = withType JsonTypeObject $ \curs ->+  DI.try (down curs) >>= maybe+    (pure mempty)+    (foldCursor snoc (moveRight1 >=> moveRight1) mempty (withCursor $ \c -> do+      k <- focus keyD c+      v <- moveRight1 c >>= focus valueD+      pure (k,v)+    ))++-- | Helper function to simplify writing a '{}' decoder.+objectAsKeyValues :: Monad f => Decoder f k -> Decoder f v -> Decoder f [(k,v)]+objectAsKeyValues k v = withCursor (objectAsKeyValuesAt k v)+ -- | Try to decode an optional value, returning the given default value if -- 'Nothing' is returned. withDefault@@ -875,7 +937,7 @@   => Decoder f a   -> Decoder f (Maybe a) maybeOrNull a =-  (Nothing <$ null) <!> (Just <$> a)+  (Just <$> a) <!> (Nothing <$ null)  -- | Decode either an 'a' or a 'b', failing if neither 'Decoder' succeeds. The -- 'Right' decoder is attempted first.@@ -885,4 +947,4 @@   -> Decoder f b   -> Decoder f (Either a b) either leftD rightD =-  (Left <$> leftD) <!> (Right <$> rightD)+  (Right <$> rightD) <!> (Left <$> leftD)
src/Waargonaut/Decode/Error.hs view
@@ -11,6 +11,8 @@  import           GHC.Word                     (Word64) +import           HaskellWorks.Data.Json.Type  (JsonType)+ import           Data.Text                    (Text)  import           Waargonaut.Decode.ZipperMove (ZipperMove)@@ -29,6 +31,7 @@ -- data DecodeError   = ConversionFailure Text+  | TypeMismatch JsonType   | KeyDecodeFailed   | KeyNotFound Text   | FailedToMove ZipperMove@@ -41,6 +44,7 @@ class AsDecodeError r where   _DecodeError       :: Prism' r DecodeError   _ConversionFailure :: Prism' r Text+  _TypeMismatch      :: Prism' r JsonType   _KeyDecodeFailed   :: Prism' r ()   _KeyNotFound       :: Prism' r Text   _FailedToMove      :: Prism' r ZipperMove@@ -49,6 +53,7 @@   _ParseFailed       :: Prism' r Text    _ConversionFailure = _DecodeError . _ConversionFailure+  _TypeMismatch      = _DecodeError . _TypeMismatch   _KeyDecodeFailed   = _DecodeError . _KeyDecodeFailed   _KeyNotFound       = _DecodeError . _KeyNotFound   _FailedToMove      = _DecodeError . _FailedToMove@@ -64,6 +69,13 @@         (\x -> case x of             ConversionFailure y -> Right y             _                   -> Left x+        )++  _TypeMismatch+    = L.prism TypeMismatch+        (\x -> case x of+            TypeMismatch y -> Right y+            _              -> Left x         )    _KeyDecodeFailed
src/Waargonaut/Decode/Types.hs view
@@ -14,6 +14,8 @@   , Decoder (..)   , DecodeResult (..)   , JCurs (..)+  , jsonTypeAt+  , JsonType(..)   ) where  import           Control.Lens                          (Rewrapped, Wrapped (..),@@ -36,6 +38,7 @@  import           HaskellWorks.Data.BalancedParens      (SimpleBalancedParens) import           HaskellWorks.Data.Json.Cursor         (JsonCursor (..))+import           HaskellWorks.Data.Json.Type           (JsonType (..), JsonTypeAt (..)) import           HaskellWorks.Data.Positioning         (Count) import           HaskellWorks.Data.RankSelect.Poppy512 (Poppy512) @@ -95,7 +98,7 @@ -- | Wrapper type for the 'SuccinctCursor' newtype JCurs = JCurs   { unJCurs :: SuccinctCursor-  }+  } deriving JsonTypeAt  instance JCurs ~ t => Rewrapped JCurs t 
test/Decoder.hs view
@@ -20,7 +20,6 @@ import qualified Data.ByteString            as BS import qualified Data.Either                as Either import           Data.Function              (const, ($))-import           Data.Functor.Alt           ((<!>)) import           Data.Maybe                 (Maybe (Just, Nothing)) import           Data.Semigroup             (Semigroup ((<>))) import qualified Data.Sequence              as Seq@@ -53,7 +52,9 @@   , testCase "Using Alt (Error) - Records BranchFail" decodeAltError   , testCase "List Decoder" listDecoder   , testCase "NonEmpty List Decoder" nonEmptyDecoder+  , testCase "Object Decoder" objectAsKeyValuesDecoder   , testCase "Absent Key Decoder" absentKeyDecoder+  , testCase "Either decoding order - Right first" decodeEitherRightFirst   ]  nonEmptyDecoder :: Assertion@@ -64,11 +65,18 @@     ok = "[1]"     notOkay = "[]" +    badTypeObj = "{}"+    badTypeText = "\"test\""+    badTypeNum = "3"+     badElem = "[1, \"fred\"]"    assertBool "NonEmpty Decoder - fail! non-empty list decoder BROKEN. Start panicking" (Either.isRight (dec ok))   assertBool "NonEmpty Decoder - empty list shouldn't succeed" (Either.isLeft (dec notOkay))   assertBool "NonEmpty Decoder - invalid element decoder accepted" (Either.isLeft (dec badElem))+  assertBool "NonEmpty Decoder - invalid type accepted - object" (Either.isLeft (dec badTypeObj))+  assertBool "NonEmpty Decoder - invalid type accepted - text" (Either.isLeft (dec badTypeText))+  assertBool "NonEmpty Decoder - invalid type accepted - num" (Either.isLeft (dec badTypeNum))  listDecoder :: Assertion listDecoder = do@@ -78,14 +86,40 @@     ok = "[1,2,3]"     okE = "[]" -    badShape = "{}"+    badTypeObj = "{}"+    badTypeText = "\"test\""+    badTypeNum = "3"     badElem = "[\"fred\", \"susan\"]"    assertBool "List Decoder - fail! List Decoder BROKEN. Start panicking." (Either.isRight (dec ok))   assertBool "List Decoder - empty list fail" (Either.isRight (dec okE))-  assertBool "List Decoder - move down should return empty list" (Either.isRight (dec badShape))+  assertBool "List Decoder - invalid type accepted - object" (Either.isLeft (dec badTypeObj))+  assertBool "List Decoder - invalid type accepted - text" (Either.isLeft (dec badTypeText))+  assertBool "List Decoder - invalid type accepted - num" (Either.isLeft (dec badTypeNum))   assertBool "List Decoder - invalid element decoder accepted" (Either.isLeft (dec badElem)) +objectAsKeyValuesDecoder :: Assertion+objectAsKeyValuesDecoder = do+  let+    dec = D.runPureDecode (D.objectAsKeyValues D.text D.int) parseBS . D.mkCursor++    ok = "{\"1\":1,\"2\":2,\"3\":3}"+    okE = "{}"++    badTypeArray = "[]"+    badTypeText = "\"test\""+    badTypeNum = "3"+    badKey = "{1:1}"+    badValue = "{\"1\":\"fred\", \"2\":\"susan\"}"++  assertBool "Object Decoder - fail! Object Decoder BROKEN. Start panicking." (Either.isRight (dec ok))+  assertBool "Object Decoder - empty list fail" (Either.isRight (dec okE))+  assertBool "Object Decoder - invalid type accepted - object" (Either.isLeft (dec badTypeArray))+  assertBool "Object Decoder - invalid type accepted - text" (Either.isLeft (dec badTypeText))+  assertBool "Object Decoder - invalid type accepted - num" (Either.isLeft (dec badTypeNum))+  assertBool "Object Decoder - invalid key decoder accepted" (Either.isLeft (dec badKey))+  assertBool "Object Decoder - invalid value decoder accepted" (Either.isLeft (dec badValue))+ decodeTestMissingObjKey :: Assertion decodeTestMissingObjKey = do   let@@ -170,7 +204,14 @@     (const (assertFailure "Should not succeed"))  decodeEitherAlt :: Monad f => D.Decoder f (Either.Either Text Int)-decodeEitherAlt = (Either.Left <$> D.text) <!> (Either.Right <$> D.int)+decodeEitherAlt = D.either D.text D.int++decodeEitherRightFirst :: Assertion+decodeEitherRightFirst = do+  let d = D.runPureDecode (D.either D.scientific D.int) parseBS . D.mkCursor++  d "44e333" @?= Either.Right (Either.Left 44e333)+  d "33" @?= Either.Right (Either.Right 33)  decodeAlt :: Assertion decodeAlt = do
waargonaut.cabal view
@@ -10,7 +10,7 @@ -- PVP summary:      +-+------- breaking API changes --                   | | +----- non-breaking API additions --                   | | | +--- code changes with no API change-version:             0.5.0.0+version:             0.5.1.0  -- A short (one-line) description of the package. synopsis:            JSON wrangling