jsonl-0.2.0.0: src/JSONL.hs
{-# LANGUAGE RankNTypes #-}
{-# language DeriveGeneric #-}
{-# language OverloadedStrings #-}
-- | JSON Lines https://jsonlines.org/
module JSONL (
-- * Encode
jsonlWriteFile, jsonlToLBS
, jsonlBuilder
, jsonLine
-- * Decode
, jsonlFromLBS
) where
import System.IO (Handle, IOMode(..), withBinaryFile)
-- aeson
import Data.Aeson (ToJSON(..), FromJSON(..), encode, eitherDecode' )
-- bytestring
import qualified Data.ByteString.Builder as BBS (toLazyByteString, Builder, lazyByteString, string7)
import qualified Data.ByteString.Builder.Internal as BBS (hPut, putBuilder)
import qualified Data.ByteString.Internal as BS (c2w)
import qualified Data.ByteString.Lazy as LBS (ByteString, span)
import Prelude hiding (writeFile)
-- | Parse a JSONL-encoded collection of objects from a `LBS.ByteString`
--
-- If parsing fails, returns the first parsing error in a Left
jsonlFromLBS :: FromJSON a => LBS.ByteString -> Either String [a]
jsonlFromLBS = sequence . jsonlFromLBS_
jsonlFromLBS_ :: FromJSON a => LBS.ByteString -> [Either String a]
jsonlFromLBS_ = go mempty
where
go :: FromJSON a => [Either String a] -> LBS.ByteString -> [Either String a]
go acc lbs = let
(s, srest) = LBS.span (== BS.c2w '\n') lbs
in go (eitherDecode' s : acc) srest
-- | Write a collection of objects to a JSONL-encoded file
jsonlWriteFile :: (Foldable t, ToJSON a) => FilePath -> t a -> IO ()
jsonlWriteFile fpath xs = writeFile fpath (jsonlBuilder xs)
-- | Render a collection of objects to a JSONL-encoded `LBS.ByteString`
jsonlToLBS :: (Foldable t, ToJSON a) => t a -> LBS.ByteString
jsonlToLBS xs = BBS.toLazyByteString $ jsonlBuilder xs
jsonlBuilder :: (Foldable t, ToJSON a) => t a -> BBS.Builder
jsonlBuilder = foldMap jsonLine
-- | Render a single JSONL line (together with its newline)
--
-- @since 0.2
jsonLine :: ToJSON a => a -> BBS.Builder
jsonLine x = jsonToBuilder x <> BBS.string7 "\n"
jsonToBuilder :: ToJSON a => a -> BBS.Builder
jsonToBuilder = BBS.lazyByteString . encode
-- vendored from bytestring < 0.11.2
hPutBuilder :: Handle -> BBS.Builder -> IO ()
hPutBuilder h = BBS.hPut h . BBS.putBuilder
modifyFile :: IOMode -> FilePath -> BBS.Builder -> IO ()
modifyFile mode f bld = withBinaryFile f mode (`hPutBuilder` bld)
writeFile :: FilePath -> BBS.Builder -> IO ()
writeFile = modifyFile WriteMode