{-# LANGUAGE ScopedTypeVariables #-}
module Data.Aeson.Extra.Stream (
streamDecode,
) where
import Prelude ()
import Prelude.Compat
import Control.Applicative ((<|>), many)
import Data.Aeson.Compat (FromJSON, Result (..), Value, fromJSON)
import Data.Aeson.Parser (value)
import qualified Data.ByteString.Lazy as LBS
import qualified Data.Attoparsec.ByteString.Char8 as A8
import qualified Data.Attoparsec.ByteString.Lazy as A
streamParse :: LBS.ByteString -> ([Value], Maybe String)
streamParse = start
where
start bs = case A.parse (lexemeChar '[') bs of
A.Done bs' _ -> first bs'
A.Fail _ _ err -> ([], Just err)
first bs = case A.parse (lexemeChar ']') bs of
A.Done {} -> ([], Nothing)
A.Fail {} -> go bs
go bs = case A.parse valueEnd bs of
A.Done _ (r, False) -> ([r], Nothing)
A.Done bs' (r, True) -> case go bs' of
~(rs, end) -> (r:rs, end)
A.Fail _ _ err -> ([], Just err)
valueEnd = do
v <- value
c <- True <$ lexemeChar ',' <|> False <$ lexemeChar ']'
return (v, c)
lexemeChar c = many A8.space *> A8.char c <* many A8.space
streamDecode :: forall a. FromJSON a => LBS.ByteString -> ([a], Maybe String)
streamDecode bs = go values
where
(values, err) = streamParse bs
go :: [Value] -> ([a], Maybe String)
go [] = ([], err)
go (v:vs) = case fromJSON v of
Error err' -> ([], Just err')
Success x -> case go vs of
~(xs, err') -> (x:xs, err')