11{-# LANGUAGE DeriveDataTypeable #-}
2- {-# LANGUAGE GeneralizedNewtypeDeriving #-}
32{-# LANGUAGE OverloadedStrings #-}
43{-# LANGUAGE ScopedTypeVariables #-}
54{-# LANGUAGE TemplateHaskell #-}
5+ {-# LANGUAGE ConstraintKinds #-}
66-- | Tag a Binary instance with the stack version number to ensure we're
77-- reading a compatible format.
88module Data.Binary.VersionTagged
99 ( taggedDecodeOrLoad
1010 , taggedEncodeFile
1111 , Binary (.. )
12- , BinarySchema (.. )
12+ , BinarySchema
13+ , HasStructuralInfo
14+ , HasSemanticVersion
1315 , decodeFileOrFailDeep
14- , encodeFile
1516 , NFData (.. )
1617 , genericRnf
1718 ) where
@@ -21,50 +22,26 @@ import Control.Exception (Exception)
2122import Control.Monad.Catch (MonadThrow (.. ))
2223import Control.Monad.IO.Class (MonadIO , liftIO )
2324import Control.Monad.Logger
24- import Data.Binary (Binary (.. ), encodeFile , decodeFileOrFail , putWord8 , getWord8 )
25+ import Data.Binary (Binary (.. ))
2526import Data.Binary.Get (ByteOffset )
27+ import Data.Binary.Tagged (HasStructuralInfo , HasSemanticVersion )
28+ import qualified Data.Binary.Tagged as BinaryTagged
2629import Data.Typeable (Typeable )
2730import Control.Exception.Enclosed (tryAnyDeep )
2831import System.FilePath (takeDirectory )
2932import System.Directory (createDirectoryIfMissing )
30- import qualified Data.ByteString as S
31- import Data.ByteString (ByteString )
32- import Control.Monad (forM_ , when )
33- import Data.Proxy
3433import qualified Data.Text as T
3534
36- magic :: ByteString
37- magic = " stack"
35+ type BinarySchema a = (Binary a , NFData a , HasStructuralInfo a , HasSemanticVersion a )
3836
39- -- | A @Binary@ instance that also has a schema version
40- class (Binary a , NFData a ) => BinarySchema a where
41- binarySchema :: Proxy a -> Int
42-
43- newtype WithTag a = WithTag a
44- deriving NFData
45- instance forall a . BinarySchema a => Binary (WithTag a ) where
46- get = do
47- forM_ (S. unpack magic) $ \ w -> do
48- w' <- getWord8
49- when (w /= w')
50- $ fail " Mismatched magic string, forcing a recompute"
51- tag' <- get
52- if binarySchema (Proxy :: Proxy a ) == tag'
53- then fmap WithTag get
54- else fail " Mismatched tags, forcing a recompute"
55- put (WithTag x) = do
56- mapM_ putWord8 $ S. unpack magic
57- put (binarySchema (Proxy :: Proxy a ))
58- put x
59-
60- -- | Write to the given file, with a version tag.
37+ -- | Write to the given file, with a binary-tagged tag.
6138taggedEncodeFile :: (BinarySchema a , MonadIO m )
6239 => FilePath
6340 -> a
6441 -> m ()
6542taggedEncodeFile fp x = liftIO $ do
6643 createDirectoryIfMissing True $ takeDirectory fp
67- encodeFile fp $ WithTag x
44+ BinaryTagged. taggedEncodeFile fp x
6845
6946-- | Read from the given file. If the read fails, run the given action and
7047-- write that back to the file. Always starts the file off with the version
@@ -82,18 +59,18 @@ taggedDecodeOrLoad fp mx = do
8259 x <- mx
8360 taggedEncodeFile fp x
8461 return x
85- Right ( WithTag x) -> do
62+ Right x -> do
8663 $ logDebug $ T. pack $ " Success decoding " ++ fp
8764 return x
8865
8966-- | Ensure that there are no lurking exceptions deep inside the parsed
9067-- value... because that happens unfortunately. See
9168-- https://github.com/commercialhaskell/stack/issues/554
92- decodeFileOrFailDeep :: (Binary a , NFData a , MonadIO m , MonadThrow n )
69+ decodeFileOrFailDeep :: (BinarySchema a , MonadIO m , MonadThrow n )
9370 => FilePath
9471 -> m (n a )
9572decodeFileOrFailDeep fp = liftIO $ fmap (either throwM return ) $ tryAnyDeep $ do
96- eres <- decodeFileOrFail fp
73+ eres <- BinaryTagged. taggedDecodeFileOrFail fp
9774 case eres of
9875 Left (offset, str) -> throwM $ DecodeFileFailure fp offset str
9976 Right x -> return x
0 commit comments