packages feed

pipes-aeson 0.1.0.0 → 0.2.0

raw patch · 8 files changed

+283/−452 lines, 8 filesdep +transformersdep ~attoparsecdep ~pipesdep ~pipes-attoparsecPVP ok

version bump matches the API change (PVP)

Dependencies added: transformers

Dependency ranges changed: attoparsec, pipes, pipes-attoparsec, pipes-parse

API changes (from Hackage documentation)

- Control.Proxy.Aeson: Array :: !Array -> TopLevelValue
- Control.Proxy.Aeson: Object :: !Object -> TopLevelValue
- Control.Proxy.Aeson: ParserError :: ParsingError -> DecodingError
- Control.Proxy.Aeson: ValueError :: String -> DecodingError
- Control.Proxy.Aeson: data DecodingError
- Control.Proxy.Aeson: data TopLevelValue
- Control.Proxy.Aeson: decode :: (Monad m, Proxy p, FromJSON r) => EitherP DecodingError (StateP [ByteString] p) () (Maybe ByteString) y' y m r
- Control.Proxy.Aeson: decodeD :: (Monad m, Proxy p, FromJSON b) => () -> Pipe (EitherP DecodingError (StateP [ByteString] p)) (Maybe ByteString) b m ()
- Control.Proxy.Aeson: encode :: (Proxy p, Monad m) => TopLevelValue -> p x' x () ByteString m ()
- Control.Proxy.Aeson: encodeD :: (Proxy p, Monad m) => () -> Pipe p TopLevelValue ByteString m r
- Control.Proxy.Aeson: fromValue :: (Monad m, Proxy p, FromJSON r) => x -> EitherP String p x Value y' y m r
- Control.Proxy.Aeson: fromValueD :: (Monad m, Proxy p, FromJSON b) => x -> EitherP String p x Value x b m r
- Control.Proxy.Aeson: instance Eq TopLevelValue
- Control.Proxy.Aeson: instance FromJSON TopLevelValue
- Control.Proxy.Aeson: instance Show TopLevelValue
- Control.Proxy.Aeson: instance ToJSON TopLevelValue
- Control.Proxy.Aeson: parseValue :: (Monad m, Proxy p) => EitherP ParsingError (StateP [ByteString] p) () (Maybe ByteString) y' y m TopLevelValue
- Control.Proxy.Aeson: parseValueD :: (Monad m, Proxy p) => () -> Pipe (EitherP ParsingError (StateP [ByteString] p)) (Maybe ByteString) TopLevelValue m ()
- Control.Proxy.Aeson: toTopLevelValue :: ToJSON a => a -> Maybe TopLevelValue
- Control.Proxy.Aeson.Unsafe: ParserError :: ParsingError -> DecodingError
- Control.Proxy.Aeson.Unsafe: ValueError :: String -> DecodingError
- Control.Proxy.Aeson.Unsafe: data DecodingError
- Control.Proxy.Aeson.Unsafe: decode :: (Monad m, Proxy p, FromJSON r) => EitherP DecodingError (StateP [ByteString] p) () (Maybe ByteString) y' y m r
- Control.Proxy.Aeson.Unsafe: decodeD :: (Monad m, Proxy p, FromJSON b) => () -> Pipe (EitherP DecodingError (StateP [ByteString] p)) (Maybe ByteString) b m ()
- Control.Proxy.Aeson.Unsafe: encode :: (Proxy p, Monad m, ToJSON a) => a -> p x' x () ByteString m ()
- Control.Proxy.Aeson.Unsafe: encodeD :: (Proxy p, Monad m, ToJSON a) => () -> Pipe p a ByteString m r
- Control.Proxy.Aeson.Unsafe: parseValue :: (Monad m, Proxy p) => EitherP ParsingError (StateP [ByteString] p) () (Maybe ByteString) y' y m Value
- Control.Proxy.Aeson.Unsafe: parseValueD :: (Monad m, Proxy p) => () -> Pipe (EitherP ParsingError (StateP [ByteString] p)) (Maybe ByteString) Value m ()
+ Pipes.Aeson: ParserError :: ParsingError -> DecodingError
+ Pipes.Aeson: ValueError :: String -> DecodingError
+ Pipes.Aeson: data DecodingError
+ Pipes.Aeson: decode :: (Monad m, FromJSON b) => StateT (Producer ByteString m r) m (Either DecodingError (Int, b))
+ Pipes.Aeson: decodeMany :: (Monad m, FromJSON b) => Producer ByteString m r -> Producer (Int, b) m (Either (DecodingError, Producer ByteString m r) r)
+ Pipes.Aeson: encode :: Monad m => Either Object Array -> Producer ByteString m ()
+ Pipes.Aeson.Unsafe: decode :: (Monad m, FromJSON b) => StateT (Producer ByteString m r) m (Either DecodingError (Int, b))
+ Pipes.Aeson.Unsafe: decodeMany :: (Monad m, FromJSON b) => Producer ByteString m r -> Producer (Int, b) m (Either (DecodingError, Producer ByteString m r) r)
+ Pipes.Aeson.Unsafe: encode :: (Monad m, ToJSON a) => a -> Producer ByteString m ()

Files

NEWS view
@@ -1,3 +1,11 @@+# 0.2.0++* Version compatible with `pipes-4.0.0` and `pipes-parse-2.0.0`.++* API radically changed. Removed `parseValue`, `fromValue` and+  `TopLevelValue`. Other things renamed.++ # 0.1.0.0  * First version.
pipes-aeson.cabal view
@@ -1,5 +1,5 @@ name:               pipes-aeson-version:            0.1.0.0+version:            0.2.0 license:            BSD3 license-file:       LICENSE copyright:          Copyright (c) Renzo Carbonara 2013@@ -12,11 +12,10 @@ category:           Pipes, Parser build-type:         Simple cabal-version:      >=1.8-synopsis:           Encode and decode JSON streams using aeson and pipes.+synopsis:           Encode and decode JSON streams using Aeson and Pipes. extra-source-files: README.md PEOPLE NEWS description:-  Utilities to encode and decode JSON streams using @aeson@ and @pipes@,-  possibly interleaving other stream effects too.+  Utilities to encode and decode Pipes streams of JSON.   .   See the @NEWS@ file in the source distribution to learn about any   important changes between version.@@ -27,16 +26,17 @@  library   hs-source-dirs:  src-  exposed-modules: Control.Proxy.Aeson-                   Control.Proxy.Aeson.Unsafe-  other-modules:   Control.Proxy.Aeson.Internal+  exposed-modules: Pipes.Aeson+                   Pipes.Aeson.Unsafe+  other-modules:   Pipes.Aeson.Internal   build-depends:       aeson            (>=0.6.1 && < 0.7)-    , attoparsec-    , base             (>=4.5   && < 5.0)-    , pipes            (>=3.3   && <3.4)-    , pipes-attoparsec (>=0.2   && <0.3)-    , pipes-parse      (>=1.0   && <1.1)+    , attoparsec       (>=0.10  && <0.11)+    , base             (>=4.5   && <5.0)+    , pipes            (>=4.0   && <4.1)+    , pipes-attoparsec (>=0.3   && <0.4)+    , pipes-parse      (>=2.0   && <2.1)     , bytestring       (>=0.9.2.1)+    , transformers     (>=0.2   && <0.4)   ghc-options: -Wall -O2   ghc-prof-options: -fprof-auto
− src/Control/Proxy/Aeson.hs
@@ -1,274 +0,0 @@--- | This module allows you to encode and decode JSON values flowing downstream--- through Pipes streams, possibly interleaving other stream effects.------ This module builds on top of the @pipes-parse@ and @pipes-attoparsec@--- packages and assumes you have read "Control.Proxy.Parse.Tutorial".--module Control.Proxy.Aeson-  ( -- * Top level JSON values-    -- $top-level-value-    TopLevelValue(..)-  , toTopLevelValue-    -- * Encoding-    -- $encoding-  , encode-  , encodeD-    -- * Decoding-    -- $decoding-  , decode-  , decodeD-    -- ** Lower level parsing-  , parseValue-  , parseValueD-  , fromValue-  , fromValueD-    -- * Types-  , I.DecodingError(..)-  ) where---import           Control.Monad                 (unless)-import qualified Control.Proxy                 as P-import qualified Control.Proxy.Aeson.Internal  as I-import qualified Control.Proxy.Aeson.Unsafe    as U-import qualified Control.Proxy.Attoparsec      as PA-import qualified Control.Proxy.Trans.Either    as P-import qualified Control.Proxy.Trans.State     as P-import qualified Data.Aeson                    as Ae-import qualified Data.ByteString.Char8         as B-import           Data.Maybe                    (fromJust)------------------------------------------------------------------------------------- $top-level-value------ The JSON RFC-4627 standard only allows 'Ae.Array' or 'Ae.Object' values as--- top-level. The 'TopLevelValue' type used throughout this module in--- replacement of Aesons's 'Ae.Value' enforces that restricion in a type-safe--- manner.------ If you want to ignore the standard and encode or decode any 'Ae.Value', then--- use the facilities exported by the "Control.Proxy.Aeson.Unsafe" module.------ * You may use the 'toTopLevelValue' function to convert any 'Ae.ToJSON'--- instance to a 'TopLevelValue', if possible. Remember that 'Ae.Value' is--- one such instance.------ * Use the 'Ae.toJSON' method on a 'TopLevelValue' to obtain its underlying--- 'Ae.Value' representation.----- | A JSON top-level value must be an 'Ae.Object' or an 'Ae.Array', according--- to RFC-4627.-data TopLevelValue-  = Object !Ae.Object-  | Array  !Ae.Array-  deriving (Show, Eq)--instance Ae.ToJSON TopLevelValue where-  toJSON (Object o) = Ae.Object o-  toJSON (Array  a) = Ae.Array  a--instance Ae.FromJSON TopLevelValue where-  parseJSON (Ae.Object o) = return (Object o)-  parseJSON (Ae.Array a)  = return (Array a)-  parseJSON _             = fail "Not a valid top-level value"---- | Converts the given 'Ae.ToJSON' instance to a 'TopLevelValue' as long as its--- 'Ae.Value' representation is one of 'Ae.Object' or 'Ae.Array', otherwise--- 'Nothing'. Remember that 'Ae.Value' itself is a 'Ae.ToJSON' instance.-toTopLevelValue :: Ae.ToJSON a => a -> Maybe TopLevelValue-toTopLevelValue = \a ->-    case Ae.toJSON a of-      Ae.Object x -> Just (Object x)-      Ae.Array  x -> Just (Array  x)-      _           -> Nothing-{-# INLINABLE toTopLevelValue #-}------------------------------------------------------------------------------------- $encoding------ There are two different JSON encoding facilities exported by this module, and--- choosing between them is easy: If you need to interleave JSON encoding--- with other stream effects you must use 'encode', otherwise you may use the--- simpler 'encodeD'.------ Both encoding proxies enforce the JSON RFC-4627 requirement that top-level--- values are either 'Ae.Array's or 'Ae.Object's, as witnessed by the--- 'TopLevelValue' type. However, if you need to ignore this requirement you may--- use the similar encoding proxies exported by the "Control.Proxy.Aeson.Unsafe"--- module.---- | Encodes the given 'TopLevelValue' as JSON and sends it downstream, possibly--- in more than one 'BS.ByteString' chunk.-encode :: (P.Proxy p, Monad m) => TopLevelValue -> p x' x () B.ByteString m ()-encode = U.encode-{-# INLINABLE encode #-}---- | Encodes 'TopLevelValue's flowing downstream as JSON, each in possibly more--- than one 'BS.ByteString' chunk, and sends each chunk downstream.-encodeD :: (P.Proxy p, Monad m) => () -> P.Pipe p TopLevelValue B.ByteString m r-encodeD = U.encodeD-{-# INLINABLE encodeD #-}------------------------------------------------------------------------------------- $decoding------ Decoding a JSON value as a Haskell type in involves two different steps:------ * Parsing a raw JSON 'B.ByteString' into a 'TopLevelValue'.------ * Converting the obtained 'TopLevelValue' to the desired type, which must be--- a 'Ae.FromJSON' instance.------ Any of those steps can fail, and in case of errors, the 'I.DecodingError'--- type explicitly states at which the step the error happened.------ There are two different JSON decoding facilities exported by this module,--- both perform those steps at once. Choosing between them is easy: If you--- need to interleave JSON decoding with other stream effects you must use--- 'decode', otherwise you may use the simpler 'decodeD'.------ These proxies use the 'P.EitherP' proxy transformer to report decoding--- errors, you might use any of the facilities exported by--- "Control.Proxy.Trans.Either" to recover from them.------ If you prefer to perform each of the decoding steps separately, you--- could use instead the 'parseValue', 'parseValueD', 'fromValue' or--- 'fromValueD' proxies.----- | Decodes one JSON value flowing downstream.------ * In case of decoding errors, a 'I.DecodingError' exception is thrown in--- the 'Pe.EitherP' proxy transformer.------ * Requests more input from upstream using 'Pa.draw' when needed.------ * /Do not/ use this proxy if your stream has leading empty chunks or--- whitespace, otherwise you may get unexpected parsing errors.------ Here is an example parsing loop that allows interleaving stream effects--- together with 'decode':------ @---   loop = do---       -- Skip any leading whitespace and check that we haven't reached EOF.---       eof &#x3c;- 'P.liftP' $ 'Control.Proxy.ByteString.dropWhile' 'Data.Char.isSpace' >> 'PA.isEndOfParserInput'---       'unless' eof $ do---           -- 1. Possibly perform some stream effects here.---           -- 2. Decode one JSON element from the stream.---           exampleElement <- 'decode'---           -- 3. Do something with exampleElement and possibly perform---           --    some more stream effects.---           -- 4. Start all over again.---           loop--- @-decode-  :: (Monad m, P.Proxy p, Ae.FromJSON r)-  => P.EitherP I.DecodingError (P.StateP [B.ByteString] p)-     () (Maybe B.ByteString) y' y m r-decode = do-    ev <- P.liftP . P.runEitherP $ PA.parse Ae.json'-    case ev of-      Left e  -> P.throw (I.ParserError e)-      Right v ->-        case Ae.fromJSON v of-          Ae.Error e   -> P.throw (I.ValueError e)-          Ae.Success r -> return r-{-# INLINABLE decode #-}----- | Decodes consecutive JSON values flowing downstream until end of input.------ * In case of decoding errors, a 'I.DecodingError' exception is thrown in--- the 'Pe.EitherP' proxy transformer.------ * Requests more input from upstream using 'Pa.draw' when needed.------ * Empty input chunks flowing downstream and whitespace in between JSON--- values will be discarded.-decodeD-  :: (Monad m, P.Proxy p, Ae.FromJSON b)-  => ()-  -> P.Pipe (P.EitherP I.DecodingError (P.StateP [B.ByteString] p))-     (Maybe B.ByteString) b m ()-decodeD = \() -> loop where-    loop = do-        eof <- P.liftP $ I.skipSpace >> PA.isEndOfParserInput-        unless eof $ decode >>= P.respond >> loop-{-# INLINABLE decodeD #-}-------------------------------------------------------------------------------------- | Parses a JSON value flowing downstream into a 'TopLevelValue'.------ * In case of parsing errors, a 'PA.ParsingError' exception is thrown in--- the 'Pe.EitherP' proxy transformer.------ * Requests more input from upstream using 'Pa.draw' when needed.------ * /Do not/ use this proxy if your stream has leading empty chunks or--- whitespace, otherwise you may get unexpected parsing errors.------ See the documentation of 'decode' for an example of how to interleave--- other stream effects together with this proxy.-parseValue-  :: (Monad m, P.Proxy p)-  => P.EitherP PA.ParsingError (P.StateP [B.ByteString] p)-     () (Maybe B.ByteString) y' y m TopLevelValue-parseValue = return . fromJust . toTopLevelValue =<< PA.parse Ae.json'-{-# INLINABLE parseValue #-}----- | Parses consecutive JSON values flowing downstream as 'TopLevelValue's,--- until end of input.------ * In case of parsing errors, a 'I.DecodingError' exception is thrown in--- the 'Pe.EitherP' proxy transformer.------ * Requests more input from upstream using 'Pa.draw' when needed.------ * Empty input chunks flowing downstream and whitespace in between JSON--- values will be discarded.-parseValueD-  :: (Monad m, P.Proxy p)-  => ()-  -> P.Pipe (P.EitherP PA.ParsingError (P.StateP [B.ByteString] p))-     (Maybe B.ByteString) TopLevelValue m ()-parseValueD = \() -> loop where-    loop = do-        eof <- P.liftP $ I.skipSpace >> PA.isEndOfParserInput-        unless eof $ parseValue >>= P.respond >> loop-{-# INLINABLE parseValueD #-}-------------------------------------------------------------------------------------- | Converts any 'Ae.Value' flowing downstream to a 'Ae.FromJSON' instance.------ * In case of parsing errors, a 'String' exception holding the value provided--- by Aeson's 'Ae.Error' is thrown in the 'Pe.EitherP' proxy transformer.------ See the documentation of 'decode' for an example of how to interleave--- other stream effects together with this proxy.-fromValue-  :: (Monad m, P.Proxy p, Ae.FromJSON r)-  => x -> P.EitherP String p x Ae.Value y' y m r-fromValue = \x -> do-    v <- P.request x-    case Ae.fromJSON v of-      Ae.Error e   -> P.throw e-      Ae.Success r -> return r-{-# INLINABLE fromValue #-}----- | Converts any 'Ae.Value's flowing downstream to 'Ae.FromJSON' instances and--- forwards them downstream.------ * In case of parsing errors, a 'String' exception holding the value provided--- by Aeson's 'Ae.Error' is thrown in the 'Pe.EitherP' proxy transformer.-fromValueD-  :: (Monad m, P.Proxy p, Ae.FromJSON b)-  => x -> P.EitherP String p x Ae.Value x b m r-fromValueD = fromValue P.\>\ P.pull-{-# INLINABLE fromValueD #-}-
− src/Control/Proxy/Aeson/Internal.hs
@@ -1,63 +0,0 @@-{-# LANGUAGE DeriveDataTypeable #-}---- | This module provides internal utilities and it is likely--- to be modified in backwards-incompatible ways in the future.------ Use the stable API exported by the "Control.Proxy.Aeson" module instead.-module Control.Proxy.Aeson.Internal-  ( DecodingError(..)-  , skipSpace-  , fromLazy-  ) where--import qualified Control.Proxy.Attoparsec      as PA-import qualified Control.Proxy.Trans.State     as P-import           Control.Exception             (Exception)-import qualified Control.Proxy                 as P-import qualified Control.Proxy.Parse           as Pp-import qualified Data.ByteString.Char8         as B-import qualified Data.ByteString.Lazy.Internal as BLI-import qualified Data.Char                     as Char-import           Data.Data                     (Data, Typeable)-import           Data.Function                 (fix)-------------------------------------------------------------------------------------- | An error while decoding a JSON value.-data DecodingError-  = ParserError PA.ParsingError-    -- ^An Attoparsec error that happened while parsing the raw JSON string.-  | ValueError String-    -- ^An Aeson error that happened while trying to convert a-    -- 'Data.Aeson.Value' to  an 'A.FromJSON' instance, as reported by-    -- 'Data.Aeson.Error'.-  deriving (Show, Eq, Data, Typeable)--instance Exception DecodingError------------------------------------------------------------------------------------- XXX we define the following proxies here until 'pipes-bytestring' is released---- | Consumes and discards leading 'I.ParserInput' characters from upstream as--- long as the given predicate holds 'True'.-skipSpace-  :: (Monad m, P.Proxy p)-  => P.StateP [B.ByteString] p () (Maybe B.ByteString) y' y m ()-skipSpace = fix $ \loop -> do-    ma <- Pp.draw-    case ma of-      Nothing -> return ()-      Just a  -> do-        let a' = B.dropWhile Char.isSpace a-        if B.null a'-           then loop-           else Pp.unDraw a'-{-# INLINABLE skipSpace #-}---- Sends each of the 'BLI.ByteString''s strict chunks downstream.-fromLazy-  :: (Monad m, P.Proxy p)-  => BLI.ByteString -> p x' x y' B.ByteString m ()-fromLazy =-    P.runIdentityP . BLI.foldrChunks (\e a -> P.respond e >> a) (return ())-{-# INLINABLE fromLazy #-}
− src/Control/Proxy/Aeson/Unsafe.hs
@@ -1,103 +0,0 @@--- | This module exports facilities similar to those exported by the--- "Control.Proxy.Aeson" module, except they do not restrict the 'Ae.Value's--- that might be encoded or decoded to be just valid top-level values. That is,--- not only 'Ae.Object's or 'Ae.Array's, according to to the RFC-4627 JSON--- standard.--module Control.Proxy.Aeson.Unsafe-  (-- * Encoding-    -- $encoding-    encode-  , encodeD-    -- * Decoding-    -- $decoding-  , decode-  , decodeD-    -- ** Lower level parsing-  , parseValue-  , parseValueD-    -- * Types-  , I.DecodingError(..)-  ) where--import           Control.Monad                 (unless)-import qualified Control.Proxy                 as P-import qualified Control.Proxy.Aeson.Internal  as I-import qualified Control.Proxy.Attoparsec      as PA-import qualified Control.Proxy.Trans.Either    as P-import qualified Control.Proxy.Trans.State     as P-import qualified Data.Aeson                    as Ae-import qualified Data.Aeson.Parser             as Ae (value')-import qualified Data.ByteString.Char8         as B-------------------------------------------------------------------------------------- | Like 'Control.Proxy.Aeson.encode', except it accepts any 'Ae.ToJSON'--- instance.-encode-  :: (P.Proxy p, Monad m, Ae.ToJSON a) => a -> p x' x () B.ByteString m ()-encode = I.fromLazy . Ae.encode-{-# INLINABLE encode #-}---- | Like 'Control.Proxy.Aeson.encodeD', except it accepts any 'Ae.ToJSON'--- instance.-encodeD-  :: (P.Proxy p, Monad m, Ae.ToJSON a) => () -> P.Pipe p a B.ByteString m r-encodeD = P.pull P./>/ encode-{-# INLINABLE encodeD #-}-------------------------------------------------------------------------------------- | Like 'Control.Proxy.Aeson.decode', except it will decode any 'Ae.ToJSON'--- instance.-decode-  :: (Monad m, P.Proxy p, Ae.FromJSON r)-  => P.EitherP I.DecodingError (P.StateP [B.ByteString] p)-     () (Maybe B.ByteString) y' y m r-decode = do-    ev <- P.liftP . P.runEitherP $ PA.parse Ae.value'-    case ev of-      Left e  -> P.throw (I.ParserError e)-      Right v ->-        case Ae.fromJSON v of-          Ae.Error e   -> P.throw (I.ValueError e)-          Ae.Success r -> return r-{-# INLINABLE decode #-}---- | Like 'Control.Proxy.Aeson.decodeD', except it will decode any 'Ae.ToJSON'--- instance.-decodeD-  :: (Monad m, P.Proxy p, Ae.FromJSON b)-  => ()-  -> P.Pipe (P.EitherP I.DecodingError (P.StateP [B.ByteString] p))-     (Maybe B.ByteString) b m ()-decodeD = \() -> loop where-    loop = do-        eof <- P.liftP $ I.skipSpace >> PA.isEndOfParserInput-        unless eof $ decode >>= P.respond >> loop-{-# INLINABLE decodeD #-}-------------------------------------------------------------------------------------- | Like 'Control.Proxy.Aeson.parseValue', except it will parse into any--- 'Ae.Value'.-parseValue-  :: (Monad m, P.Proxy p)-  => P.EitherP PA.ParsingError (P.StateP [B.ByteString] p)-     () (Maybe B.ByteString) y' y m Ae.Value-parseValue = PA.parse Ae.value'-{-# INLINABLE parseValue #-}---- | Like 'Control.Proxy.Aeson.parseValueD', except it will parse into any--- 'Ae.Value'.-parseValueD-  :: (Monad m, P.Proxy p)-  => ()-  -> P.Pipe (P.EitherP PA.ParsingError (P.StateP [B.ByteString] p))-     (Maybe B.ByteString) Ae.Value m ()-parseValueD = \() -> loop where-    loop = do-        eof <- P.liftP $ I.skipSpace >> PA.isEndOfParserInput-        unless eof $ parseValue >>= P.respond >> loop-{-# INLINABLE parseValueD #-}-
+ src/Pipes/Aeson.hs view
@@ -0,0 +1,99 @@+{-# LANGUAGE RankNTypes #-}++-- | This module allows you to encode and decode JSON values flowing downstream+-- through Pipes streams.+--+-- This module builds on top of the @aeson@, @pipes@ and @pipes-parse@+-- libraries, and assumes you know how to use them.++module Pipes.Aeson+  ( -- * Encoding+    encode+    -- * Decoding+    -- $decoding+  , decode+  , decodeMany+    -- * Types+  , I.DecodingError(..)+  ) where++import           Pipes+import qualified Pipes.Aeson.Internal             as I+import qualified Pipes.Aeson.Unsafe               as U+import qualified Pipes.Attoparsec                 as PA+import qualified Control.Monad.Trans.State.Strict as S+import qualified Data.Aeson                       as Ae+import qualified Data.ByteString.Char8            as B++--------------------------------------------------------------------------------++-- | Encode an 'Ae.Array' or 'Ae.Object' as JSON and send it downstream,+-- possibly in more than one 'B.ByteString' chunk.+--+-- /Note:/ The JSON RFC-4627 standard only allows arrays or objects as top-level+-- entities, which is why this function restricts its input to them. If you+-- prefer to ignore the standard and encode any 'Ae.Value', then use 'U.encode'+-- from the "Pipes.Aeson.Unsafe" module.+encode :: Monad m => Either Ae.Object Ae.Array -> Producer B.ByteString m ()+encode = either U.encode U.encode+{-# INLINABLE encode #-}++--------------------------------------------------------------------------------+-- $decoding+--+-- Decoding JSON as a Haskell value in involves two different steps:+--+-- * Parsing a raw JSON 'B.ByteString' into an 'Ae.Object' or an 'Ae.Array'.+--+-- * Converting the obtained 'Ae.Object' or 'Ae.Array' to the desired+-- 'Ae.FromJSON' instance.+--+-- Any of those steps can fail, in which case a 'I.DecodingError' will report+-- the precise error and at which the step it appened.+++-- | Decodes an 'Ae.Object' or 'Ae.Array' JSON value from the underlying state.+--+-- /Do not/ use this function if the underlying 'Producer' has leading empty+-- chunks or whitespace, otherwise you may get unexpected parsing errors.+--+-- /Note:/ The JSON RFC-4627 standard only allows arrays or objects as top-level+-- entities, which is why this 'Producer' restricts its output to them. If you+-- prefer to ignore the standard and decode any 'Ae.Value', then use 'U.decode'+-- from the "Pipes.Aeson.Unsafe" module.+decode+  :: (Monad m, Ae.FromJSON b)+  => S.StateT (Producer B.ByteString m r) m (Either I.DecodingError (Int, b))+decode = do+    ev <- PA.parse Ae.json'+    return $ do+      case ev of+        Left  e        -> Left (I.ParserError e)+        Right (len, v) -> do+          case Ae.fromJSON v of+            Ae.Error   e -> Left  (I.ValueError e)+            Ae.Success b -> Right (len, b)+{-# INLINABLE decode #-}++-- | Continuously 'decode' the JSON output from the given 'Producer', sending+-- downstream pairs of each successfully decoded entity together with the number+-- of bytes consumed in order to produce it. Whitespace in between JSON content+-- is ignored.+--+-- This 'Producer' runs until it either runs out of input or until a decoding+-- failure occurs, in which case it returns 'Left' with a 'I.DecodingError' and+-- a 'Producer' with any leftovers. You can use 'P.errorP' to turn the 'Either'+-- return value into an 'Control.Monad.Trans.Error.ErrorT' monad transformer.+--+-- /Note:/ The JSON RFC-4627 standard only allows arrays or objects as top-level+-- entities, which is why this 'Producer' restricts its output to them. If you+-- prefer to ignore the standard and decode any 'Ae.Value', then use+-- 'U.decodeMany' from the "Pipes.Aeson.Unsafe" module.+decodeMany+  :: (Monad m, Ae.FromJSON b)+  => Producer B.ByteString m r  -- ^Producer from which to draw JSON.+  -> Producer (Int, b) m+              (Either (I.DecodingError, Producer B.ByteString m r) r)+decodeMany = I.consecutively decode+{-# INLINABLE decodeMany #-}+
+ src/Pipes/Aeson/Internal.hs view
@@ -0,0 +1,104 @@+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE FlexibleContexts #-}++-- | This module provides internal utilities and it is likely+-- to be modified in backwards-incompatible ways in the future.+--+-- Use the stable API exported by the "Pipes.Aeson" module instead.+module Pipes.Aeson.Internal+  ( DecodingError(..)+  , consecutively+  , skipSpace+  , fromLazy+  ) where++import           Control.Exception                (Exception)+import qualified Control.Monad.Trans.Error        as E+import qualified Control.Monad.Trans.State.Strict as S+import qualified Data.Aeson                       as Ae+import qualified Data.ByteString.Char8            as B+import qualified Data.ByteString.Lazy.Internal    as BLI+import qualified Data.Char                        as Char+import           Data.Data                        (Data, Typeable)+import           Pipes+import qualified Pipes.Attoparsec                 as PA+import qualified Pipes.Parse                      as Pp+import qualified Pipes.Lift                       as P++--------------------------------------------------------------------------------++-- | An error while decoding a JSON value.+data DecodingError+  = ParserError PA.ParsingError+    -- ^An Attoparsec error that happened while parsing the raw JSON string.+  | ValueError String+    -- ^An Aeson error that happened while trying to convert a+    -- 'Data.Aeson.Value' to  an 'A.FromJSON' instance, as reported by+    -- 'Data.Aeson.Error'.+  deriving (Show, Eq, Data, Typeable)++instance Exception DecodingError+instance E.Error DecodingError+instance Monad m => E.Error (DecodingError, Producer a m r)++--------------------------------------------------------------------------------++-- | Consecutively parse 'b' elements from the given 'Producer' using the given+-- parser (such as 'Pipes.Aeson.decode' or 'Pipes.Aeson.parseValue'), skipping+-- any leading whitespace each time.+--+-- This 'Producer' runs until it either runs out of input or until a decoding+-- failure occurs, in which case it returns 'Left' with a 'I.DecodingError' and+-- a 'Producer' with any leftovers. You can use 'P.errorP' to turn the 'Either'+-- return value into an 'Control.Monad.Trans.Error.ErrorT' monad transformer.+consecutively+  :: (Monad m, Ae.FromJSON b)+  => S.StateT (Producer B.ByteString m r) m (Either DecodingError (Int, b))+  -> Producer B.ByteString m r  -- ^Producer from which to draw JSON.+  -> Producer (Int, b) m+              (Either (DecodingError, Producer B.ByteString m r) r)+consecutively parser = \src -> do+    (er, src') <- P.runStateP src prod+    return $ case er of+      Left  e  -> Left  (e, src')+      Right r  -> Right r+  where+    prod = do+        eof <- lift (skipSpace >> PA.isEndOfParserInput)+        if eof+          then do+            ra <- lift Pp.draw+            case ra of+              Left  r -> return (Right r)+              Right _ -> error "Pipes.Aeson.parseMany: impossible!!"+          else do+            eb <- lift parser+            case eb of+              Left  e -> return (Left e)+              Right b -> yield b >> prod+{-# INLINABLE consecutively #-}++--------------------------------------------------------------------------------++-- XXX we define the following proxies here until 'pipes-bytestring' is released++-- | Consumes and discards leading 'I.ParserInput' characters from upstream as+-- long as the given predicate holds 'True'.+skipSpace :: Monad m => S.StateT (Producer B.ByteString m r) m ()+skipSpace = do+    ma <- Pp.draw+    case ma of+      Left  _ -> return ()+      Right a -> do+        let a' = B.dropWhile Char.isSpace a+        if B.null a'+           then skipSpace+           else Pp.unDraw a'+{-# INLINABLE skipSpace #-}++-- Sends each of the 'BLI.ByteString''s strict chunks downstream.+fromLazy :: Monad m => BLI.ByteString -> Producer' B.ByteString m ()+fromLazy = BLI.foldrChunks (\e a -> yield e >> a) (return ())+{-# INLINABLE fromLazy #-}
+ src/Pipes/Aeson/Unsafe.hs view
@@ -0,0 +1,60 @@+{-# LANGUAGE RankNTypes #-}++-- | This module exports facilities similar to those exported by the+-- "Pipes.Aeson" module, except they do not restrict the 'Ae.Value's+-- that might be encoded or decoded to be just valid top-level values. That is,+-- not only 'Ae.Object's or 'Ae.Array's, according to to the RFC-4627 JSON+-- standard.++module Pipes.Aeson.Unsafe+  ( -- * Encoding+    encode+    -- * Decoding+  , decode+  , decodeMany+  ) where++import           Pipes+import qualified Pipes.Aeson.Internal             as I+import qualified Pipes.Attoparsec                 as PA+import qualified Control.Monad.Trans.State.Strict as S+import qualified Data.Aeson                       as Ae+import qualified Data.Aeson.Parser                as Ae (value')+import qualified Data.ByteString                  as B++--------------------------------------------------------------------------------++-- | Like 'Pipes.Aeson.encode', except it accepts any 'Ae.ToJSON' instance,+-- not just 'Ae.Array' or 'Ae.Object'.+encode :: (Monad m, Ae.ToJSON a) => a -> Producer B.ByteString m ()+encode = I.fromLazy . Ae.encode+{-# INLINABLE encode #-}++--------------------------------------------------------------------------------++-- | Like 'Pipes.Aeson.decode', except it will decode any 'Ae.ToJSON' instance,+-- not just 'Ae.Array' or 'Ae.Object'.+decode+  :: (Monad m, Ae.FromJSON b)+  => S.StateT (Producer B.ByteString m r) m (Either I.DecodingError (Int, b))+decode = do+    ev <- PA.parse Ae.value'+    return $ do+      case ev of+        Left  e        -> Left (I.ParserError e)+        Right (len, v) -> do+          case Ae.fromJSON v of+            Ae.Error e   -> Left (I.ValueError e)+            Ae.Success b -> Right (len, b)+{-# INLINABLE decode #-}++-- | Like 'Pipes.Aeson.decodeMany', except it will decode any 'Ae.ToJSON'+-- instance, not just 'Ae.Array' or 'Ae.Object'.+decodeMany+  :: (Monad m, Ae.FromJSON b)+  => Producer B.ByteString m r  -- ^Producer from which to draw JSON.+  -> Producer (Int, b) m+              (Either (I.DecodingError, Producer B.ByteString m r) r)+decodeMany = I.consecutively decode+{-# INLINABLE decodeMany #-}+