{-# LANGUAGE CPP #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE Trustworthy #-}
module Data.Binary.Tagged (
structuredEncode,
structuredEncodeFile,
structuredDecode,
structuredDecodeOrFailIO,
structuredDecodeFileOrFail,
Structured (structure),
structureHash,
structureBuilder,
genericStructure,
GStructured,
nominalStructure,
containerStructure,
Structure (..),
TypeName,
ConstructorName,
TypeVersion,
SopStructure,
hashStructure,
typeVersion,
typeName,
MD5,
showMD5,
md5,
md5FromInteger,
binaryPutMD5,
binaryGetMD5,
) where
import Data.Structured
import Data.Structured.Internal
import qualified Data.Binary as Binary
import qualified Data.Binary.Get as Binary
import qualified Data.Binary.Put as Binary
import qualified Data.ByteString.Lazy as LBS
import Control.Exception (ErrorCall (..), catch, evaluate)
import Data.Tagged (Tagged (..), untag)
import GHC.Fingerprint (Fingerprint (..))
data Tag a = Tag
instance Structured a => Binary.Binary (Tag a) where
get = do
actual <- binaryGetMD5
if actual == expected
then return Tag
else fail $ concat
[ "Non-matching structured hashes: "
, showMD5 actual
, "; expected: "
, showMD5 expected
]
where
expected = untag (structureHash' :: Tagged a MD5)
put _ = binaryPutMD5 expected
where
expected = untag (structureHash' :: Tagged a MD5)
structuredEncode
:: forall a. (Binary.Binary a, Structured a)
=> a -> LBS.ByteString
structuredEncode x = Binary.encode (Tag :: Tag a, x)
structuredEncodeFile :: (Binary.Binary a, Structured a) => FilePath -> a -> IO ()
structuredEncodeFile f = LBS.writeFile f . structuredEncode
structuredDecode
:: forall a. (Binary.Binary a, Structured a)
=> LBS.ByteString -> a
structuredDecode lbs = snd (Binary.decode lbs :: (Tag a, a))
structuredDecodeOrFailIO :: (Binary.Binary a, Structured a) => LBS.ByteString -> IO (Either String a)
structuredDecodeOrFailIO bs =
catch (evaluate (structuredDecode bs) >>= return . Right) handler
where
#if MIN_VERSION_base(4,9,0)
handler (ErrorCallWithLocation str _) = return $ Left str
#else
handler (ErrorCall str) = return $ Left str
#endif
structuredDecodeFileOrFail :: (Binary.Binary a, Structured a) => FilePath -> IO (Either String a)
structuredDecodeFileOrFail f = structuredDecodeOrFailIO =<< LBS.readFile f
binaryPutMD5 :: MD5 -> Binary.Put
binaryPutMD5 (Fingerprint a b) = do
Binary.putWord64le a
Binary.putWord64le b
binaryGetMD5 :: Binary.Get MD5
binaryGetMD5 = do
a <- Binary.getWord64le
b <- Binary.getWord64le
return (Fingerprint a b)