module Data.Conduit.Serialization.Binary
( conduitDecode
, conduitEncode
, conduitMsgEncode
, conduitGet
, conduitPut
, conduitPutList
, conduitPutLBS
, conduitPutMany
, sourcePut
, sinkGet
, ParseError(..)
)
where
import Control.Exception
import Data.Binary
import Data.Binary.Get
import Data.Binary.Put
import Data.ByteString as BS
import qualified Data.ByteString.Lazy as LBS
import Data.Conduit
import qualified Data.Conduit.List as CL
import Data.Typeable
import qualified Data.Vector as V
import Control.Monad.Trans.Resource
(MonadThrow
, monadThrow)
data ParseError = ParseError
{ unconsumed :: ByteString
, offset :: ByteOffset
, content :: String
} deriving (Show, Typeable)
instance Exception ParseError
conduitDecode :: (Binary b, MonadThrow m) => Conduit ByteString m b
conduitDecode = conduitGet get
conduitEncode :: (Binary b, MonadThrow m) => Conduit b m ByteString
conduitEncode = CL.map put =$= conduitPut
conduitMsgEncode :: (Binary b, MonadThrow m) => Conduit b m ByteString
conduitMsgEncode = CL.map put =$= conduitMsg
conduitGet :: MonadThrow m => Get b -> Conduit ByteString m b
conduitGet g = start
where
start = do mx <- await
case mx of
Nothing -> return ()
Just x -> go (runGetIncremental g `pushChunk` x)
go (Done bs _ v) = do yield v
if BS.null bs
then start
else go (runGetIncremental g `pushChunk` bs)
go (Fail u o e) = monadThrow (ParseError u o e)
go (Partial n) = await >>= (go . n)
#define conduitPutGeneric(name,yi) \
name = conduit \
where \
conduit = do {mx <- await;\
case mx of;\
Nothing -> return ();\
Just x -> do { yi ; conduit}}
conduitPut :: MonadThrow m => Conduit Put m ByteString
conduitPutGeneric(conduitPut, (sourcePut x $$ CL.mapM_ yield))
conduitMsg :: MonadThrow m => Conduit Put m ByteString
conduitPutGeneric(conduitMsg, (yield (LBS.toStrict $ runPut x)))
conduitPutLBS :: MonadThrow m => Conduit Put m LBS.ByteString
conduitPutGeneric(conduitPutLBS, yield (runPut x))
conduitPutList :: MonadThrow m => Conduit Put m [ByteString]
conduitPutGeneric(conduitPutList, yield (LBS.toChunks (runPut x)))
conduitPutMany :: MonadThrow m => Conduit Put m (V.Vector ByteString)
conduitPutGeneric(conduitPutMany, yield (V.fromList (LBS.toChunks (runPut x))))
sourcePut :: MonadThrow m => Put -> Producer m ByteString
sourcePut = CL.sourceList . LBS.toChunks . runPut
sinkGet :: MonadThrow m => Get b -> Consumer ByteString m b
sinkGet f = sink (runGetIncremental f)
where
sink (Done bs _ v) = leftover bs >> return v
sink (Fail u o e) = monadThrow (ParseError u o e)
sink (Partial next) = await >>= sink . next