cereal-plus-0.4.0: library/CerealPlus/Deserialize.hs
-- |
-- A monad-transformer over "Data.Serialize.Get".
module CerealPlus.Deserialize
(
Deserialize,
runPartial,
Result(..),
liftGet,
throwError,
)
where
import CerealPlus.Prelude
import qualified Data.Serialize.Get as Cereal
-- | A deserialization monad transformer.
newtype Deserialize m a = Deserialize {
-- | Run on a chunk of data and get a partial result.
runPartial :: ByteString -> m (Result m a)
}
instance (Monad m) => Monad (Deserialize m) where
Deserialize runA >>= aToDeserializeTB = Deserialize $ \bs -> runA bs >>= aToMB where
aToMB a = case a of
Fail msg bs -> return $ Fail msg bs
Partial cont -> return $ Partial $ \bs -> cont bs >>= aToMB
Done a bs -> case aToDeserializeTB a of Deserialize runB -> runB bs
return a = Deserialize $ \bs -> return $ Done a bs
instance MonadTrans Deserialize where
lift m = Deserialize $ \bs -> m >>= \a -> return $ Done a bs
instance (MonadIO m) => MonadIO (Deserialize m) where
liftIO = lift . liftIO
instance (Monad m) => Applicative (Deserialize m) where
pure = return
(<*>) = ap
instance (Monad m) => Functor (Deserialize m) where
fmap = liftM
instance MFunctor Deserialize where
hoist nat (Deserialize k) = Deserialize (nat . liftM (hoist nat) . k)
-- | A partial result of deserialization.
data Result m a =
-- | A message describing the deserialization failure and a remaining chunk.
Fail Text ByteString |
-- | A continuation function, which should be supplied with the next chunk.
Partial (ByteString -> m (Result m a)) |
-- | A deserialized data structure and a remaining chunk.
Done a ByteString
instance MFunctor Result where
hoist nat (Fail str bs) = Fail str bs
hoist nat (Partial k) = Partial (nat . liftM (hoist nat) . k)
hoist nat (Done a bs) = Done a bs
-- | Run a `Cereal.Get` action of the \"cereal\" library.
liftGet :: Monad m => Cereal.Get a -> Deserialize m a
liftGet get = Deserialize $ \bs -> return $ convertResult $ Cereal.runGetPartial get bs
where
convertResult r = case r of
Cereal.Fail m bs -> Fail (packText m) bs
Cereal.Partial cont -> Partial $ \bs -> return $ convertResult $ cont bs
Cereal.Done a bs -> Done a bs
-- |
-- Fail with a message.
--
-- Since there's no consensus on how to implement 'catchError' of 'MonadError',
-- we'll go with just this function.
--
-- It is not implemented as 'fail' because 'fail' is pure evil.
throwError :: (Monad m) => Text -> Deserialize m a
throwError t = Deserialize $ \bs -> return $ Fail t bs