-- | This module exports a template haskell function to create -- Serialize instances based on the SafeCopy instance, and an -- alternative decode function that puts the decode type in the error -- message. It also re-exports all other Data.Serialize symbols {-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE StandaloneDeriving #-} {-# LANGUAGE TemplateHaskell #-} module Extra.SafeCopy ( module Data.SafeCopy , DecodeError(..) , encodeSafe , decodeAllSafe #if 0 , decodeMSafe , decodeMSafe' #endif , decodePrismSafe , encodeGetterSafe ) where import Control.Exception (ErrorCall(..)) import Control.Lens (Getter, Prism', prism, re) import Control.Monad.Catch (catch, MonadCatch) import Control.Monad.Except (MonadError) import Data.ByteString as B (ByteString, null) import Data.Data (Proxy(Proxy), Typeable) import Data.SafeCopy (base, SafeCopy, safeGet, safePut) #if !MIN_VERSION_base(4,11,0) import Data.Semigroup (Semigroup((<>))) #endif import Data.Serialize hiding (decode, encode) import Data.UUID.Orphans () --import Extra.Errors (Member, OneOf, throwMember) import Extra.Orphans () import Extra.Serialize (DecodeError(..), fakeTypeRep) encodeSafe :: SafeCopy a => a -> ByteString encodeSafe = runPut . safePut -- Version of decode that errors if all input is not consumed. decodeAllSafe :: forall a. (SafeCopy a) => ByteString -> Either String a decodeAllSafe b = case runGetState safeGet b 0 of Left s -> Left s Right (a, more) | B.null more -> Right a Right (_, more) -> Left ("decode " <> show b <> " failed to consume " <> show more) #if 0 -- | Monadic version of decode. decodeMSafe :: forall a e m. (SafeCopy a, Typeable a, Member DecodeError e, MonadError (OneOf e) m) => ByteString -> m a decodeMSafe bs = case decodeAllSafe bs of Left s -> throwMember (DecodeError bs (fakeTypeRep (Proxy @a)) s) Right a -> return a -- | Like 'decodeM', but also catches any ErrorCall thrown and lifts -- it into the MonadError instance. I'm not sure whether this can -- actually happen. What I'm seeing is probably an error call from -- outside the serialize package, in which case this (and decode') are -- pointless. decodeMSafe' :: forall e m a. (SafeCopy a, Typeable a, Member DecodeError e, MonadError (OneOf e) m, MonadCatch m) => ByteString -> m a decodeMSafe' bs = go `catch` handle where go = case decodeAllSafe bs of Left s -> throwMember (DecodeError bs (fakeTypeRep (Proxy @a)) s) Right a -> return a handle :: ErrorCall -> m a handle (ErrorCall s) = throwMember (DecodeError bs (fakeTypeRep (Proxy @a)) s) #endif -- | Serialize/deserialize prism. decodePrismSafe :: forall a. (SafeCopy a) => Prism' ByteString a decodePrismSafe = prism encodeSafe (\s -> either (\_ -> Left s) Right (decodeAllSafe s :: Either String a)) -- | Inverting a prism turns it into a getter. encodeGetterSafe :: forall a. (SafeCopy a) => Getter a ByteString encodeGetterSafe = re decodePrismSafe