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.Foldable
import Data.Typeable
import qualified Data.Vector as V
import Control.Monad.Catch (MonadThrow(..))
data ParseError = ParseError
{ unconsumed :: ByteString
, offset :: ByteOffset
, content :: String
} deriving (Show, Typeable)
instance Exception ParseError
conduitDecode :: (Binary b, MonadThrow m) => ConduitT ByteString b m ()
conduitDecode = conduitGet get
conduitEncode :: (Binary b, MonadThrow m) => ConduitT b ByteString m ()
conduitEncode = CL.map put .| conduitPut
conduitMsgEncode :: Monad m => (Binary b) => ConduitT b ByteString m ()
conduitMsgEncode = CL.map put .| conduitMsg
conduitGet :: MonadThrow m => Get b -> ConduitT ByteString b m ()
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) = throwM (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 :: Monad m => ConduitT Put ByteString m ()
conduitPutGeneric(conduitPut, (traverse_ yield (LBS.toChunks $ runPut x)))
conduitMsg :: Monad m => ConduitT Put ByteString m ()
conduitPutGeneric(conduitMsg, (yield (LBS.toStrict $ runPut x)))
conduitPutLBS :: Monad m => ConduitT Put LBS.ByteString m ()
conduitPutGeneric(conduitPutLBS, yield (runPut x))
conduitPutList :: Monad m => ConduitT Put [ByteString] m ()
conduitPutGeneric(conduitPutList, yield (LBS.toChunks (runPut x)))
conduitPutMany :: Monad m => ConduitT Put (V.Vector ByteString) m ()
conduitPutGeneric(conduitPutMany, yield (V.fromList (LBS.toChunks (runPut x))))
sourcePut :: Monad m => Put -> ConduitT z ByteString m ()
sourcePut = CL.sourceList . LBS.toChunks . runPut
sinkGet :: MonadThrow m => Get b -> ConduitT ByteString z m b
sinkGet f = sink (runGetIncremental f)
where
sink (Done bs _ v) = leftover bs >> return v
sink (Fail u o e) = throwM (ParseError u o e)
sink (Partial next) = await >>= sink . next