{-# LANGUAGE CPP #-}
#if __GLASGOW_HASKELL__ >= 711
{-# LANGUAGE PatternSynonyms #-}
#endif

#ifndef MIN_VERSION_binary
#define MIN_VERSION_binary(x, y, z) 0
#endif

module Distribution.Compat.Binary
       ( decodeOrFailIO
       , decodeFileOrFail'
#if __GLASGOW_HASKELL__ >= 708 || MIN_VERSION_binary(0,7,0)
       , module Data.Binary
#else
       , Binary(..)
       , decode, encode, encodeFile
#endif
       ) where

import Control.Exception (ErrorCall (..), catch, evaluate)
import Data.ByteString.Lazy (ByteString)

#if __GLASGOW_HASKELL__ >= 708 || MIN_VERSION_binary(0,7,0)

import Data.Binary

-- | Lazily reconstruct a value previously written to a file.
decodeFileOrFail' :: Binary a => FilePath -> IO (Either String a)
decodeFileOrFail' :: forall a. Binary a => FilePath -> IO (Either FilePath a)
decodeFileOrFail' FilePath
f = ((ByteOffset, FilePath) -> Either FilePath a)
-> (a -> Either FilePath a)
-> Either (ByteOffset, FilePath) a
-> Either FilePath a
forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
either (FilePath -> Either FilePath a
forall a b. a -> Either a b
Left (FilePath -> Either FilePath a)
-> ((ByteOffset, FilePath) -> FilePath)
-> (ByteOffset, FilePath)
-> Either FilePath a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (ByteOffset, FilePath) -> FilePath
forall a b. (a, b) -> b
snd) a -> Either FilePath a
forall a b. b -> Either a b
Right (Either (ByteOffset, FilePath) a -> Either FilePath a)
-> IO (Either (ByteOffset, FilePath) a) -> IO (Either FilePath a)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
`fmap` FilePath -> IO (Either (ByteOffset, FilePath) a)
forall a.
Binary a =>
FilePath -> IO (Either (ByteOffset, FilePath) a)
decodeFileOrFail FilePath
f

#else

import Data.Binary.Get
import Data.Binary.Put
import qualified Data.ByteString.Lazy as BSL

import Distribution.Compat.Binary.Class
import Distribution.Compat.Binary.Generic ()

-- | Decode a value from a lazy ByteString, reconstructing the
-- original structure.
--
decode :: Binary a => ByteString -> a
decode = runGet get

-- | Encode a value using binary serialisation to a lazy ByteString.
--
encode :: Binary a => a -> ByteString
encode = runPut . put
{-# INLINE encode #-}

-- | Lazily reconstruct a value previously written to a file.
decodeFileOrFail' :: Binary a => FilePath -> IO (Either String a)
decodeFileOrFail' f = decodeOrFailIO =<< BSL.readFile f

-- | Lazily serialise a value to a file
encodeFile :: Binary a => FilePath -> a -> IO ()
encodeFile f = BSL.writeFile f . encode

#endif

decodeOrFailIO :: Binary a => ByteString -> IO (Either String a)
decodeOrFailIO :: forall a. Binary a => ByteString -> IO (Either FilePath a)
decodeOrFailIO ByteString
bs =
    IO (Either FilePath a)
-> (ErrorCall -> IO (Either FilePath a)) -> IO (Either FilePath a)
forall e a. Exception e => IO a -> (e -> IO a) -> IO a
catch (a -> IO a
forall a. a -> IO a
evaluate (ByteString -> a
forall a. Binary a => ByteString -> a
decode ByteString
bs) IO a -> (a -> IO (Either FilePath a)) -> IO (Either FilePath a)
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= Either FilePath a -> IO (Either FilePath a)
forall (m :: * -> *) a. Monad m => a -> m a
return (Either FilePath a -> IO (Either FilePath a))
-> (a -> Either FilePath a) -> a -> IO (Either FilePath a)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> Either FilePath a
forall a b. b -> Either a b
Right) ErrorCall -> IO (Either FilePath a)
forall {m :: * -> *} {b}.
Monad m =>
ErrorCall -> m (Either FilePath b)
handler
  where
#if MIN_VERSION_base(4,9,0)
    handler :: ErrorCall -> m (Either FilePath b)
handler (ErrorCallWithLocation FilePath
str FilePath
_) = Either FilePath b -> m (Either FilePath b)
forall (m :: * -> *) a. Monad m => a -> m a
return (Either FilePath b -> m (Either FilePath b))
-> Either FilePath b -> m (Either FilePath b)
forall a b. (a -> b) -> a -> b
$ FilePath -> Either FilePath b
forall a b. a -> Either a b
Left FilePath
str
#else
    handler (ErrorCall str) = return $ Left str
#endif