{-# LANGUAGE CPP #-}
{-# LANGUAGE DefaultSignatures #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}

-- |
--
-- Copyright: (c) 2019 Oleg Grenrus
--
-- Structurally tag binary serialisation stream.
-- Useful when most 'Binary' instances are 'Generic' derived.
--
-- Say you have a data type
--
-- @
-- data Record = Record
--   { _recordFields  :: HM.HashMap Text (Integer, ByteString)
--   , _recordEnabled :: Bool
--   }
--   deriving (Eq, Show, Generic)
--
-- instance 'Binary' Record
-- instance 'Structured' Record
-- @
--
-- then you can serialise and deserialise @Record@ values with a structure tag by simply
--
-- @
-- 'structuredEncode' record :: 'LBS.ByteString'
-- 'structuredDecode' lbs :: IO Record
-- @
--
-- If structure of @Record@ changes in between, deserialisation will fail early.
--
-- Technically, 'Structured' is not related to 'Binary', and may
-- be useful in other uses.
module Distribution.Utils.Structured
  ( -- * Encoding and decoding

    -- | These functions operate like @binary@'s counterparts,
    -- but the serialised version has a structure hash in front.
    structuredEncode
  , structuredEncodeFile
  , structuredDecode
  , structuredDecodeOrFailIO
  , structuredDecodeFileOrFail

    -- * Structured class
  , Structured (structure)
  , MD5
  , structureHash
  , structureBuilder
  , genericStructure
  , GStructured
  , nominalStructure
  , containerStructure

    -- * Structure type
  , Structure (..)
  , Tag (..)
  , TypeName
  , ConstructorName
  , TypeVersion
  , SopStructure
  , hashStructure
  , typeVersion
  , typeName
  ) where

import Data.Int (Int16, Int32, Int64, Int8)
import Data.List.NonEmpty (NonEmpty)
import Data.Proxy (Proxy (..))
import Data.Ratio (Ratio)
import Data.Word (Word, Word16, Word32, Word64, Word8)

import qualified Control.Monad.Trans.State.Strict as State

import Control.Exception (ErrorCall (..), catch, evaluate)

import GHC.Generics

import qualified Data.ByteString as BS
import qualified Data.ByteString.Lazy as LBS
#if MIN_VERSION_bytestring(0,10,4)
import qualified Data.ByteString.Builder      as Builder
#else
import qualified Data.ByteString.Lazy.Builder as Builder
#endif
import qualified Data.IntMap as IM
import qualified Data.IntSet as IS
import qualified Data.Map as Map
import qualified Data.Sequence as Seq
import qualified Data.Set as Set
import qualified Data.Text as T
import qualified Data.Text.Lazy as LT
import qualified Data.Time as Time
import qualified Distribution.Compat.Binary as Binary

#ifdef MIN_VERSION_aeson
import qualified Data.Aeson as Aeson
#endif

import Data.Kind (Type)

import Distribution.Compat.Typeable (TypeRep, Typeable, typeRep)
import Distribution.Utils.MD5

import Data.Monoid (mconcat)

import qualified Data.Foldable
import qualified Data.Semigroup

-------------------------------------------------------------------------------
-- Types
-------------------------------------------------------------------------------

type TypeName = String
type ConstructorName = String

-- | A semantic version of a data type. Usually 0.
type TypeVersion = Word32

-- | Structure of a datatype.
--
-- It can be infinite, as far as 'TypeRep's involved are finite.
-- (e.g. polymorphic recursion might cause troubles).
data Structure
  = -- | nominal, yet can be parametrised by other structures.
    Nominal !TypeRep !TypeVersion TypeName [Structure]
  | -- | a newtype wrapper
    Newtype !TypeRep !TypeVersion TypeName Structure
  | -- | sum-of-products structure
    Structure !TypeRep !TypeVersion TypeName SopStructure
  deriving (Structure -> Structure -> Bool
(Structure -> Structure -> Bool)
-> (Structure -> Structure -> Bool) -> Eq Structure
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: Structure -> Structure -> Bool
== :: Structure -> Structure -> Bool
$c/= :: Structure -> Structure -> Bool
/= :: Structure -> Structure -> Bool
Eq, Eq Structure
Eq Structure =>
(Structure -> Structure -> Ordering)
-> (Structure -> Structure -> Bool)
-> (Structure -> Structure -> Bool)
-> (Structure -> Structure -> Bool)
-> (Structure -> Structure -> Bool)
-> (Structure -> Structure -> Structure)
-> (Structure -> Structure -> Structure)
-> Ord Structure
Structure -> Structure -> Bool
Structure -> Structure -> Ordering
Structure -> Structure -> Structure
forall a.
Eq a =>
(a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
$ccompare :: Structure -> Structure -> Ordering
compare :: Structure -> Structure -> Ordering
$c< :: Structure -> Structure -> Bool
< :: Structure -> Structure -> Bool
$c<= :: Structure -> Structure -> Bool
<= :: Structure -> Structure -> Bool
$c> :: Structure -> Structure -> Bool
> :: Structure -> Structure -> Bool
$c>= :: Structure -> Structure -> Bool
>= :: Structure -> Structure -> Bool
$cmax :: Structure -> Structure -> Structure
max :: Structure -> Structure -> Structure
$cmin :: Structure -> Structure -> Structure
min :: Structure -> Structure -> Structure
Ord, Int -> Structure -> ShowS
[Structure] -> ShowS
Structure -> String
(Int -> Structure -> ShowS)
-> (Structure -> String)
-> ([Structure] -> ShowS)
-> Show Structure
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> Structure -> ShowS
showsPrec :: Int -> Structure -> ShowS
$cshow :: Structure -> String
show :: Structure -> String
$cshowList :: [Structure] -> ShowS
showList :: [Structure] -> ShowS
Show, (forall x. Structure -> Rep Structure x)
-> (forall x. Rep Structure x -> Structure) -> Generic Structure
forall x. Rep Structure x -> Structure
forall x. Structure -> Rep Structure x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cfrom :: forall x. Structure -> Rep Structure x
from :: forall x. Structure -> Rep Structure x
$cto :: forall x. Rep Structure x -> Structure
to :: forall x. Rep Structure x -> Structure
Generic)

type SopStructure = [(ConstructorName, [Structure])]

-- | A MD5 hash digest of 'Structure'.
hashStructure :: Structure -> MD5
hashStructure :: Structure -> MD5
hashStructure = ByteString -> MD5
md5 (ByteString -> MD5)
-> (Structure -> ByteString) -> Structure -> MD5
forall b c a. (b -> c) -> (a -> b) -> a -> c
. LazyByteString -> ByteString
LBS.toStrict (LazyByteString -> ByteString)
-> (Structure -> LazyByteString) -> Structure -> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Builder -> LazyByteString
Builder.toLazyByteString (Builder -> LazyByteString)
-> (Structure -> Builder) -> Structure -> LazyByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Structure -> Builder
structureBuilder

-- | A van-Laarhoven lens into 'TypeVersion' of 'Structure'
--
-- @
-- 'typeVersion' :: Lens' 'Structure' 'TypeVersion'
-- @
typeVersion :: Functor f => (TypeVersion -> f TypeVersion) -> Structure -> f Structure
typeVersion :: forall (f :: * -> *).
Functor f =>
(TypeVersion -> f TypeVersion) -> Structure -> f Structure
typeVersion TypeVersion -> f TypeVersion
f (Nominal TypeRep
t TypeVersion
v String
n [Structure]
s) = (TypeVersion -> Structure) -> f TypeVersion -> f Structure
forall a b. (a -> b) -> f a -> f b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (\TypeVersion
v' -> TypeRep -> TypeVersion -> String -> [Structure] -> Structure
Nominal TypeRep
t TypeVersion
v' String
n [Structure]
s) (TypeVersion -> f TypeVersion
f TypeVersion
v)
typeVersion TypeVersion -> f TypeVersion
f (Newtype TypeRep
t TypeVersion
v String
n Structure
s) = (TypeVersion -> Structure) -> f TypeVersion -> f Structure
forall a b. (a -> b) -> f a -> f b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (\TypeVersion
v' -> TypeRep -> TypeVersion -> String -> Structure -> Structure
Newtype TypeRep
t TypeVersion
v' String
n Structure
s) (TypeVersion -> f TypeVersion
f TypeVersion
v)
typeVersion TypeVersion -> f TypeVersion
f (Structure TypeRep
t TypeVersion
v String
n SopStructure
s) = (TypeVersion -> Structure) -> f TypeVersion -> f Structure
forall a b. (a -> b) -> f a -> f b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (\TypeVersion
v' -> TypeRep -> TypeVersion -> String -> SopStructure -> Structure
Structure TypeRep
t TypeVersion
v' String
n SopStructure
s) (TypeVersion -> f TypeVersion
f TypeVersion
v)

-- | A van-Laarhoven lens into 'TypeName' of 'Structure'
--
-- @
-- 'typeName' :: Lens' 'Structure' 'TypeName'
-- @
typeName :: Functor f => (TypeName -> f TypeName) -> Structure -> f Structure
typeName :: forall (f :: * -> *).
Functor f =>
(String -> f String) -> Structure -> f Structure
typeName String -> f String
f (Nominal TypeRep
t TypeVersion
v String
n [Structure]
s) = (String -> Structure) -> f String -> f Structure
forall a b. (a -> b) -> f a -> f b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (\String
n' -> TypeRep -> TypeVersion -> String -> [Structure] -> Structure
Nominal TypeRep
t TypeVersion
v String
n' [Structure]
s) (String -> f String
f String
n)
typeName String -> f String
f (Newtype TypeRep
t TypeVersion
v String
n Structure
s) = (String -> Structure) -> f String -> f Structure
forall a b. (a -> b) -> f a -> f b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (\String
n' -> TypeRep -> TypeVersion -> String -> Structure -> Structure
Newtype TypeRep
t TypeVersion
v String
n' Structure
s) (String -> f String
f String
n)
typeName String -> f String
f (Structure TypeRep
t TypeVersion
v String
n SopStructure
s) = (String -> Structure) -> f String -> f Structure
forall a b. (a -> b) -> f a -> f b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (\String
n' -> TypeRep -> TypeVersion -> String -> SopStructure -> Structure
Structure TypeRep
t TypeVersion
v String
n' SopStructure
s) (String -> f String
f String
n)

-------------------------------------------------------------------------------
-- Builder
-------------------------------------------------------------------------------

-- | Flatten 'Structure' into something we can calculate hash of.
--
-- As 'Structure' can be potentially infinite. For mutually recursive types,
-- we keep track of 'TypeRep's, and put just 'TypeRep' name when it's occurred
-- another time.
structureBuilder :: Structure -> Builder.Builder
structureBuilder :: Structure -> Builder
structureBuilder Structure
s0 = State (Map String (NonEmpty TypeRep)) Builder
-> Map String (NonEmpty TypeRep) -> Builder
forall s a. State s a -> s -> a
State.evalState (Structure -> State (Map String (NonEmpty TypeRep)) Builder
go Structure
s0) Map String (NonEmpty TypeRep)
forall k a. Map k a
Map.empty
  where
    go :: Structure -> State.State (Map.Map String (NonEmpty TypeRep)) Builder.Builder
    go :: Structure -> State (Map String (NonEmpty TypeRep)) Builder
go (Nominal TypeRep
t TypeVersion
v String
n [Structure]
s) = TypeRep
-> State (Map String (NonEmpty TypeRep)) Builder
-> State (Map String (NonEmpty TypeRep)) Builder
forall {m :: * -> *}.
Monad m =>
TypeRep
-> StateT (Map String (NonEmpty TypeRep)) m Builder
-> StateT (Map String (NonEmpty TypeRep)) m Builder
withTypeRep TypeRep
t (State (Map String (NonEmpty TypeRep)) Builder
 -> State (Map String (NonEmpty TypeRep)) Builder)
-> State (Map String (NonEmpty TypeRep)) Builder
-> State (Map String (NonEmpty TypeRep)) Builder
forall a b. (a -> b) -> a -> b
$ do
      s' <- (Structure -> State (Map String (NonEmpty TypeRep)) Builder)
-> [Structure]
-> StateT (Map String (NonEmpty TypeRep)) Identity [Builder]
forall (t :: * -> *) (f :: * -> *) a b.
(Traversable t, Applicative f) =>
(a -> f b) -> t a -> f (t b)
forall (f :: * -> *) a b.
Applicative f =>
(a -> f b) -> [a] -> f [b]
traverse Structure -> State (Map String (NonEmpty TypeRep)) Builder
go [Structure]
s
      return $ mconcat $ Builder.word8 1 : Builder.word32LE v : Builder.stringUtf8 n : s'
    go (Newtype TypeRep
t TypeVersion
v String
n Structure
s) = TypeRep
-> State (Map String (NonEmpty TypeRep)) Builder
-> State (Map String (NonEmpty TypeRep)) Builder
forall {m :: * -> *}.
Monad m =>
TypeRep
-> StateT (Map String (NonEmpty TypeRep)) m Builder
-> StateT (Map String (NonEmpty TypeRep)) m Builder
withTypeRep TypeRep
t (State (Map String (NonEmpty TypeRep)) Builder
 -> State (Map String (NonEmpty TypeRep)) Builder)
-> State (Map String (NonEmpty TypeRep)) Builder
-> State (Map String (NonEmpty TypeRep)) Builder
forall a b. (a -> b) -> a -> b
$ do
      s' <- Structure -> State (Map String (NonEmpty TypeRep)) Builder
go Structure
s
      return $ mconcat [Builder.word8 2, Builder.word32LE v, Builder.stringUtf8 n, s']
    go (Structure TypeRep
t TypeVersion
v String
n SopStructure
s) = TypeRep
-> State (Map String (NonEmpty TypeRep)) Builder
-> State (Map String (NonEmpty TypeRep)) Builder
forall {m :: * -> *}.
Monad m =>
TypeRep
-> StateT (Map String (NonEmpty TypeRep)) m Builder
-> StateT (Map String (NonEmpty TypeRep)) m Builder
withTypeRep TypeRep
t (State (Map String (NonEmpty TypeRep)) Builder
 -> State (Map String (NonEmpty TypeRep)) Builder)
-> State (Map String (NonEmpty TypeRep)) Builder
-> State (Map String (NonEmpty TypeRep)) Builder
forall a b. (a -> b) -> a -> b
$ do
      s' <- SopStructure -> State (Map String (NonEmpty TypeRep)) Builder
goSop SopStructure
s
      return $ mconcat [Builder.word8 3, Builder.word32LE v, Builder.stringUtf8 n, s']

    withTypeRep :: TypeRep
-> StateT (Map String (NonEmpty TypeRep)) m Builder
-> StateT (Map String (NonEmpty TypeRep)) m Builder
withTypeRep TypeRep
t StateT (Map String (NonEmpty TypeRep)) m Builder
k = do
      acc <- StateT
  (Map String (NonEmpty TypeRep)) m (Map String (NonEmpty TypeRep))
forall (m :: * -> *) s. Monad m => StateT s m s
State.get
      case insert t acc of
        Maybe (Map String (NonEmpty TypeRep))
Nothing -> Builder -> StateT (Map String (NonEmpty TypeRep)) m Builder
forall a. a -> StateT (Map String (NonEmpty TypeRep)) m a
forall (m :: * -> *) a. Monad m => a -> m a
return (Builder -> StateT (Map String (NonEmpty TypeRep)) m Builder)
-> Builder -> StateT (Map String (NonEmpty TypeRep)) m Builder
forall a b. (a -> b) -> a -> b
$ [Builder] -> Builder
forall a. Monoid a => [a] -> a
mconcat [Word8 -> Builder
Builder.word8 Word8
0, String -> Builder
Builder.stringUtf8 (TypeRep -> String
forall a. Show a => a -> String
show TypeRep
t)]
        Just Map String (NonEmpty TypeRep)
acc' -> do
          Map String (NonEmpty TypeRep)
-> StateT (Map String (NonEmpty TypeRep)) m ()
forall (m :: * -> *) s. Monad m => s -> StateT s m ()
State.put Map String (NonEmpty TypeRep)
acc'
          StateT (Map String (NonEmpty TypeRep)) m Builder
k

    goSop :: SopStructure -> State.State (Map.Map String (NonEmpty TypeRep)) Builder.Builder
    goSop :: SopStructure -> State (Map String (NonEmpty TypeRep)) Builder
goSop SopStructure
sop = do
      parts <- ((String, [Structure])
 -> State (Map String (NonEmpty TypeRep)) Builder)
-> SopStructure
-> StateT (Map String (NonEmpty TypeRep)) Identity [Builder]
forall (t :: * -> *) (f :: * -> *) a b.
(Traversable t, Applicative f) =>
(a -> f b) -> t a -> f (t b)
forall (f :: * -> *) a b.
Applicative f =>
(a -> f b) -> [a] -> f [b]
traverse (String, [Structure])
-> State (Map String (NonEmpty TypeRep)) Builder
part SopStructure
sop
      return $ mconcat parts

    part :: (String, [Structure])
-> State (Map String (NonEmpty TypeRep)) Builder
part (String
cn, [Structure]
s) = do
      s' <- (Structure -> State (Map String (NonEmpty TypeRep)) Builder)
-> [Structure]
-> StateT (Map String (NonEmpty TypeRep)) Identity [Builder]
forall (t :: * -> *) (f :: * -> *) a b.
(Traversable t, Applicative f) =>
(a -> f b) -> t a -> f (t b)
forall (f :: * -> *) a b.
Applicative f =>
(a -> f b) -> [a] -> f [b]
traverse Structure -> State (Map String (NonEmpty TypeRep)) Builder
go [Structure]
s
      return $ Data.Monoid.mconcat [Builder.stringUtf8 cn, mconcat s']

    insert :: TypeRep -> Map.Map String (NonEmpty TypeRep) -> Maybe (Map.Map String (NonEmpty TypeRep))
    insert :: TypeRep
-> Map String (NonEmpty TypeRep)
-> Maybe (Map String (NonEmpty TypeRep))
insert TypeRep
tr Map String (NonEmpty TypeRep)
m = case String -> Map String (NonEmpty TypeRep) -> Maybe (NonEmpty TypeRep)
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup String
trShown Map String (NonEmpty TypeRep)
m of
      Maybe (NonEmpty TypeRep)
Nothing -> Maybe (Map String (NonEmpty TypeRep))
inserted
      Just NonEmpty TypeRep
ne
        | TypeRep
tr TypeRep -> NonEmpty TypeRep -> Bool
forall a. Eq a => a -> NonEmpty a -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`Data.Foldable.elem` NonEmpty TypeRep
ne -> Maybe (Map String (NonEmpty TypeRep))
forall a. Maybe a
Nothing
        | Bool
otherwise -> Maybe (Map String (NonEmpty TypeRep))
inserted
      where
        inserted :: Maybe (Map String (NonEmpty TypeRep))
inserted = Map String (NonEmpty TypeRep)
-> Maybe (Map String (NonEmpty TypeRep))
forall a. a -> Maybe a
Just ((NonEmpty TypeRep -> NonEmpty TypeRep -> NonEmpty TypeRep)
-> String
-> NonEmpty TypeRep
-> Map String (NonEmpty TypeRep)
-> Map String (NonEmpty TypeRep)
forall k a. Ord k => (a -> a -> a) -> k -> a -> Map k a -> Map k a
Map.insertWith NonEmpty TypeRep -> NonEmpty TypeRep -> NonEmpty TypeRep
forall a. Semigroup a => a -> a -> a
(Data.Semigroup.<>) String
trShown (TypeRep -> NonEmpty TypeRep
forall a. a -> NonEmpty a
forall (f :: * -> *) a. Applicative f => a -> f a
pure TypeRep
tr) Map String (NonEmpty TypeRep)
m)
        trShown :: String
trShown = TypeRep -> String
forall a. Show a => a -> String
show TypeRep
tr

-------------------------------------------------------------------------------
-- Classes
-------------------------------------------------------------------------------

-- | Class of types with a known 'Structure'.
--
-- For regular data types 'Structured' can be derived generically.
--
-- @
-- data Record = Record { a :: Int, b :: Bool, c :: [Char] } deriving ('Generic')
-- instance 'Structured' Record
-- @
--
-- @since 3.2.0.0
class Typeable a => Structured a where
  structure :: Proxy a -> Structure
  default structure :: (Generic a, GStructured (Rep a)) => Proxy a -> Structure
  structure = Proxy a -> Structure
forall a.
(Typeable a, Generic a, GStructured (Rep a)) =>
Proxy a -> Structure
genericStructure

  -- This member is hidden. It's there to precalc
  structureHash' :: Tagged a MD5
  structureHash' = MD5 -> Tagged a MD5
forall {k} (a :: k) b. b -> Tagged a b
Tagged (Structure -> MD5
hashStructure (Proxy a -> Structure
forall a. Structured a => Proxy a -> Structure
structure (Proxy a
forall {k} (t :: k). Proxy t
Proxy :: Proxy a)))

-- private Tagged
newtype Tagged a b = Tagged {forall {k} (a :: k) b. Tagged a b -> b
untag :: b}

-- | Semantically @'hashStructure' . 'structure'@.
structureHash :: forall a. Structured a => Proxy a -> MD5
structureHash :: forall a. Structured a => Proxy a -> MD5
structureHash Proxy a
_ = Tagged a MD5 -> MD5
forall {k} (a :: k) b. Tagged a b -> b
untag (Tagged a MD5
forall a. Structured a => Tagged a MD5
structureHash' :: Tagged a MD5)

-------------------------------------------------------------------------------
-- Functions
-------------------------------------------------------------------------------

-- | Structured 'Binary.encode'.
-- Encode a value to using binary serialisation to a lazy 'LBS.ByteString'.
-- Encoding starts with 16 byte large structure hash.
structuredEncode
  :: forall a
   . (Binary.Binary a, Structured a)
  => a
  -> LBS.ByteString
structuredEncode :: forall a. (Binary a, Structured a) => a -> LazyByteString
structuredEncode a
x = (Tag a, a) -> LazyByteString
forall a. Binary a => a -> LazyByteString
Binary.encode (Tag a
forall {k} (a :: k). Tag a
Tag :: Tag a, a
x)

-- | Lazily serialise a value to a file
structuredEncodeFile :: (Binary.Binary a, Structured a) => FilePath -> a -> IO ()
structuredEncodeFile :: forall a. (Binary a, Structured a) => String -> a -> IO ()
structuredEncodeFile String
f = String -> LazyByteString -> IO ()
LBS.writeFile String
f (LazyByteString -> IO ()) -> (a -> LazyByteString) -> a -> IO ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> LazyByteString
forall a. (Binary a, Structured a) => a -> LazyByteString
structuredEncode

-- | Structured 'Binary.decode'.
-- Decode a value from a lazy 'LBS.ByteString', reconstructing the original structure.
-- Throws pure exception on invalid inputs.
structuredDecode
  :: forall a
   . (Binary.Binary a, Structured a)
  => LBS.ByteString
  -> a
structuredDecode :: forall a. (Binary a, Structured a) => LazyByteString -> a
structuredDecode LazyByteString
lbs = (Tag a, a) -> a
forall a b. (a, b) -> b
snd (LazyByteString -> (Tag a, a)
forall a. Binary a => LazyByteString -> a
Binary.decode LazyByteString
lbs :: (Tag a, a))

structuredDecodeOrFailIO :: (Binary.Binary a, Structured a) => LBS.ByteString -> IO (Either String a)
structuredDecodeOrFailIO :: forall a.
(Binary a, Structured a) =>
LazyByteString -> IO (Either String a)
structuredDecodeOrFailIO LazyByteString
bs =
  IO (Either String a)
-> (ErrorCall -> IO (Either String a)) -> IO (Either String a)
forall e a. Exception e => IO a -> (e -> IO a) -> IO a
catch (a -> IO a
forall a. a -> IO a
evaluate (LazyByteString -> a
forall a. (Binary a, Structured a) => LazyByteString -> a
structuredDecode LazyByteString
bs) IO a -> (a -> IO (Either String a)) -> IO (Either String a)
forall a b. IO a -> (a -> IO b) -> IO b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= Either String a -> IO (Either String a)
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (Either String a -> IO (Either String a))
-> (a -> Either String a) -> a -> IO (Either String a)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> Either String a
forall a b. b -> Either a b
Right) ErrorCall -> IO (Either String a)
forall {m :: * -> *} {b}.
Monad m =>
ErrorCall -> m (Either String b)
handler
  where
    handler :: ErrorCall -> m (Either String b)
handler (ErrorCallWithLocation String
str String
_) = Either String b -> m (Either String b)
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return (Either String b -> m (Either String b))
-> Either String b -> m (Either String b)
forall a b. (a -> b) -> a -> b
$ String -> Either String b
forall a b. a -> Either a b
Left String
str

-- | Lazily reconstruct a value previously written to a file.
structuredDecodeFileOrFail :: (Binary.Binary a, Structured a) => FilePath -> IO (Either String a)
structuredDecodeFileOrFail :: forall a.
(Binary a, Structured a) =>
String -> IO (Either String a)
structuredDecodeFileOrFail String
f = LazyByteString -> IO (Either String a)
forall a.
(Binary a, Structured a) =>
LazyByteString -> IO (Either String a)
structuredDecodeOrFailIO (LazyByteString -> IO (Either String a))
-> IO LazyByteString -> IO (Either String a)
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< String -> IO LazyByteString
LBS.readFile String
f

-------------------------------------------------------------------------------
-- Helper data
-------------------------------------------------------------------------------

data Tag a = Tag

instance Structured a => Binary.Binary (Tag a) where
  get :: Get (Tag a)
get = do
    actual <- Get MD5
binaryGetMD5
    if actual == expected
      then return Tag
      else
        fail $
          concat
            [ "Non-matching structured hashes: "
            , showMD5 actual
            , "; expected: "
            , showMD5 expected
            ]
    where
      expected :: MD5
expected = Tagged a MD5 -> MD5
forall {k} (a :: k) b. Tagged a b -> b
untag (Tagged a MD5
forall a. Structured a => Tagged a MD5
structureHash' :: Tagged a MD5)

  put :: Tag a -> Put
put Tag a
_ = MD5 -> Put
binaryPutMD5 MD5
expected
    where
      expected :: MD5
expected = Tagged a MD5 -> MD5
forall {k} (a :: k) b. Tagged a b -> b
untag (Tagged a MD5
forall a. Structured a => Tagged a MD5
structureHash' :: Tagged a MD5)

-------------------------------------------------------------------------------
-- Smart constructors
-------------------------------------------------------------------------------

-- | Use 'Typeable' to infer name
nominalStructure :: Typeable a => Proxy a -> Structure
nominalStructure :: forall {k} (a :: k). Typeable a => Proxy a -> Structure
nominalStructure Proxy a
p = TypeRep -> TypeVersion -> String -> [Structure] -> Structure
Nominal TypeRep
tr TypeVersion
0 (TypeRep -> String
forall a. Show a => a -> String
show TypeRep
tr) []
  where
    tr :: TypeRep
tr = Proxy a -> TypeRep
forall {k} (proxy :: k -> *) (a :: k).
Typeable a =>
proxy a -> TypeRep
typeRep Proxy a
p

containerStructure :: forall f a. (Typeable f, Structured a) => Proxy (f a) -> Structure
containerStructure :: forall {k} (f :: * -> k) a.
(Typeable f, Structured a) =>
Proxy (f a) -> Structure
containerStructure Proxy (f a)
_ =
  TypeRep -> TypeVersion -> String -> [Structure] -> Structure
Nominal
    TypeRep
faTypeRep
    TypeVersion
0
    (TypeRep -> String
forall a. Show a => a -> String
show TypeRep
fTypeRep)
    [ Proxy a -> Structure
forall a. Structured a => Proxy a -> Structure
structure (Proxy a
forall {k} (t :: k). Proxy t
Proxy :: Proxy a)
    ]
  where
    fTypeRep :: TypeRep
fTypeRep = Proxy f -> TypeRep
forall {k} (proxy :: k -> *) (a :: k).
Typeable a =>
proxy a -> TypeRep
typeRep (Proxy f
forall {k} (t :: k). Proxy t
Proxy :: Proxy f)
    faTypeRep :: TypeRep
faTypeRep = Proxy (f a) -> TypeRep
forall {k} (proxy :: k -> *) (a :: k).
Typeable a =>
proxy a -> TypeRep
typeRep (Proxy (f a)
forall {k} (t :: k). Proxy t
Proxy :: Proxy (f a))

-------------------------------------------------------------------------------
-- Generic
-------------------------------------------------------------------------------

-- | Derive 'structure' generically.
genericStructure :: forall a. (Typeable a, Generic a, GStructured (Rep a)) => Proxy a -> Structure
genericStructure :: forall a.
(Typeable a, Generic a, GStructured (Rep a)) =>
Proxy a -> Structure
genericStructure Proxy a
_ = TypeRep -> Proxy (Rep a) -> TypeVersion -> Structure
forall (f :: * -> *).
GStructured f =>
TypeRep -> Proxy f -> TypeVersion -> Structure
gstructured (Proxy a -> TypeRep
forall {k} (proxy :: k -> *) (a :: k).
Typeable a =>
proxy a -> TypeRep
typeRep (Proxy a
forall {k} (t :: k). Proxy t
Proxy :: Proxy a)) (Proxy (Rep a)
forall {k} (t :: k). Proxy t
Proxy :: Proxy (Rep a)) TypeVersion
0

-- | Used to implement 'genericStructure'.
class GStructured (f :: Type -> Type) where
  gstructured :: TypeRep -> Proxy f -> TypeVersion -> Structure

instance (i ~ D, Datatype c, GStructuredSum f) => GStructured (M1 i c f) where
  gstructured :: TypeRep -> Proxy (M1 i c f) -> TypeVersion -> Structure
gstructured TypeRep
tr Proxy (M1 i c f)
_ TypeVersion
v = case SopStructure
sop of
    [(String
_, [Structure
s])] | M1 i c f () -> Bool
forall {k} (d :: k) k1 (t :: k -> (k1 -> *) -> k1 -> *)
       (f :: k1 -> *) (a :: k1).
Datatype d =>
t d f a -> Bool
forall k1 (t :: Meta -> (k1 -> *) -> k1 -> *) (f :: k1 -> *)
       (a :: k1).
t c f a -> Bool
isNewtype M1 i c f ()
p -> TypeRep -> TypeVersion -> String -> Structure -> Structure
Newtype TypeRep
tr TypeVersion
v String
name Structure
s
    SopStructure
_ -> TypeRep -> TypeVersion -> String -> SopStructure -> Structure
Structure TypeRep
tr TypeVersion
v String
name SopStructure
sop
    where
      p :: M1 i c f ()
p = M1 i c f ()
forall a. HasCallStack => a
undefined :: M1 i c f ()
      name :: String
name = M1 i c f () -> String
forall {k} (d :: k) k1 (t :: k -> (k1 -> *) -> k1 -> *)
       (f :: k1 -> *) (a :: k1).
Datatype d =>
t d f a -> String
forall k1 (t :: Meta -> (k1 -> *) -> k1 -> *) (f :: k1 -> *)
       (a :: k1).
t c f a -> String
datatypeName M1 i c f ()
p
      sop :: SopStructure
sop = Proxy f -> SopStructure -> SopStructure
forall (f :: * -> *).
GStructuredSum f =>
Proxy f -> SopStructure -> SopStructure
gstructuredSum (Proxy f
forall {k} (t :: k). Proxy t
Proxy :: Proxy f) []

class GStructuredSum (f :: Type -> Type) where
  gstructuredSum :: Proxy f -> SopStructure -> SopStructure

instance (i ~ C, Constructor c, GStructuredProd f) => GStructuredSum (M1 i c f) where
  gstructuredSum :: Proxy (M1 i c f) -> SopStructure -> SopStructure
gstructuredSum Proxy (M1 i c f)
_ SopStructure
xs = (String
name, [Structure]
prod) (String, [Structure]) -> SopStructure -> SopStructure
forall a. a -> [a] -> [a]
: SopStructure
xs
    where
      name :: String
name = M1 i c f () -> String
forall {k} (c :: k) k1 (t :: k -> (k1 -> *) -> k1 -> *)
       (f :: k1 -> *) (a :: k1).
Constructor c =>
t c f a -> String
forall k1 (t :: Meta -> (k1 -> *) -> k1 -> *) (f :: k1 -> *)
       (a :: k1).
t c f a -> String
conName (M1 i c f ()
forall a. HasCallStack => a
undefined :: M1 i c f ())
      prod :: [Structure]
prod = Proxy f -> [Structure] -> [Structure]
forall (f :: * -> *).
GStructuredProd f =>
Proxy f -> [Structure] -> [Structure]
gstructuredProd (Proxy f
forall {k} (t :: k). Proxy t
Proxy :: Proxy f) []

instance (GStructuredSum f, GStructuredSum g) => GStructuredSum (f :+: g) where
  gstructuredSum :: Proxy (f :+: g) -> SopStructure -> SopStructure
gstructuredSum Proxy (f :+: g)
_ SopStructure
xs =
    Proxy f -> SopStructure -> SopStructure
forall (f :: * -> *).
GStructuredSum f =>
Proxy f -> SopStructure -> SopStructure
gstructuredSum (Proxy f
forall {k} (t :: k). Proxy t
Proxy :: Proxy f) (SopStructure -> SopStructure) -> SopStructure -> SopStructure
forall a b. (a -> b) -> a -> b
$
      Proxy g -> SopStructure -> SopStructure
forall (f :: * -> *).
GStructuredSum f =>
Proxy f -> SopStructure -> SopStructure
gstructuredSum (Proxy g
forall {k} (t :: k). Proxy t
Proxy :: Proxy g) SopStructure
xs

instance GStructuredSum V1 where
  gstructuredSum :: Proxy V1 -> SopStructure -> SopStructure
gstructuredSum Proxy V1
_ = SopStructure -> SopStructure
forall a. a -> a
id

class GStructuredProd (f :: Type -> Type) where
  gstructuredProd :: Proxy f -> [Structure] -> [Structure]

instance (i ~ S, GStructuredProd f) => GStructuredProd (M1 i c f) where
  gstructuredProd :: Proxy (M1 i c f) -> [Structure] -> [Structure]
gstructuredProd Proxy (M1 i c f)
_ = Proxy f -> [Structure] -> [Structure]
forall (f :: * -> *).
GStructuredProd f =>
Proxy f -> [Structure] -> [Structure]
gstructuredProd (Proxy f
forall {k} (t :: k). Proxy t
Proxy :: Proxy f)

instance Structured c => GStructuredProd (K1 i c) where
  gstructuredProd :: Proxy (K1 i c) -> [Structure] -> [Structure]
gstructuredProd Proxy (K1 i c)
_ [Structure]
xs = Proxy c -> Structure
forall a. Structured a => Proxy a -> Structure
structure (Proxy c
forall {k} (t :: k). Proxy t
Proxy :: Proxy c) Structure -> [Structure] -> [Structure]
forall a. a -> [a] -> [a]
: [Structure]
xs

instance GStructuredProd U1 where
  gstructuredProd :: Proxy U1 -> [Structure] -> [Structure]
gstructuredProd Proxy U1
_ = [Structure] -> [Structure]
forall a. a -> a
id

instance (GStructuredProd f, GStructuredProd g) => GStructuredProd (f :*: g) where
  gstructuredProd :: Proxy (f :*: g) -> [Structure] -> [Structure]
gstructuredProd Proxy (f :*: g)
_ [Structure]
xs =
    Proxy f -> [Structure] -> [Structure]
forall (f :: * -> *).
GStructuredProd f =>
Proxy f -> [Structure] -> [Structure]
gstructuredProd (Proxy f
forall {k} (t :: k). Proxy t
Proxy :: Proxy f) ([Structure] -> [Structure]) -> [Structure] -> [Structure]
forall a b. (a -> b) -> a -> b
$
      Proxy g -> [Structure] -> [Structure]
forall (f :: * -> *).
GStructuredProd f =>
Proxy f -> [Structure] -> [Structure]
gstructuredProd (Proxy g
forall {k} (t :: k). Proxy t
Proxy :: Proxy g) [Structure]
xs

-------------------------------------------------------------------------------
-- instances
-------------------------------------------------------------------------------

instance Structured ()
instance Structured Bool
instance Structured Ordering

instance Structured Char where structure :: Proxy Char -> Structure
structure = Proxy Char -> Structure
forall {k} (a :: k). Typeable a => Proxy a -> Structure
nominalStructure
instance Structured Int where structure :: Proxy Int -> Structure
structure = Proxy Int -> Structure
forall {k} (a :: k). Typeable a => Proxy a -> Structure
nominalStructure
instance Structured Integer where structure :: Proxy Integer -> Structure
structure = Proxy Integer -> Structure
forall {k} (a :: k). Typeable a => Proxy a -> Structure
nominalStructure

instance Structured Data.Word.Word where structure :: Proxy Word -> Structure
structure = Proxy Word -> Structure
forall {k} (a :: k). Typeable a => Proxy a -> Structure
nominalStructure

instance Structured Int8 where structure :: Proxy Int8 -> Structure
structure = Proxy Int8 -> Structure
forall {k} (a :: k). Typeable a => Proxy a -> Structure
nominalStructure
instance Structured Int16 where structure :: Proxy Int16 -> Structure
structure = Proxy Int16 -> Structure
forall {k} (a :: k). Typeable a => Proxy a -> Structure
nominalStructure
instance Structured Int32 where structure :: Proxy Int32 -> Structure
structure = Proxy Int32 -> Structure
forall {k} (a :: k). Typeable a => Proxy a -> Structure
nominalStructure
instance Structured Int64 where structure :: Proxy Int64 -> Structure
structure = Proxy Int64 -> Structure
forall {k} (a :: k). Typeable a => Proxy a -> Structure
nominalStructure

instance Structured Word8 where structure :: Proxy Word8 -> Structure
structure = Proxy Word8 -> Structure
forall {k} (a :: k). Typeable a => Proxy a -> Structure
nominalStructure
instance Structured Word16 where structure :: Proxy Word16 -> Structure
structure = Proxy Word16 -> Structure
forall {k} (a :: k). Typeable a => Proxy a -> Structure
nominalStructure
instance Structured Word32 where structure :: Proxy TypeVersion -> Structure
structure = Proxy TypeVersion -> Structure
forall {k} (a :: k). Typeable a => Proxy a -> Structure
nominalStructure
instance Structured Word64 where structure :: Proxy Word64 -> Structure
structure = Proxy Word64 -> Structure
forall {k} (a :: k). Typeable a => Proxy a -> Structure
nominalStructure

instance Structured Float where structure :: Proxy Float -> Structure
structure = Proxy Float -> Structure
forall {k} (a :: k). Typeable a => Proxy a -> Structure
nominalStructure
instance Structured Double where structure :: Proxy Double -> Structure
structure = Proxy Double -> Structure
forall {k} (a :: k). Typeable a => Proxy a -> Structure
nominalStructure

instance Structured a => Structured (Maybe a)
instance (Structured a, Structured b) => Structured (Either a b)
instance Structured a => Structured (Ratio a) where structure :: Proxy (Ratio a) -> Structure
structure = Proxy (Ratio a) -> Structure
forall {k} (f :: * -> k) a.
(Typeable f, Structured a) =>
Proxy (f a) -> Structure
containerStructure
instance Structured a => Structured [a] where structure :: Proxy [a] -> Structure
structure = Proxy [a] -> Structure
forall {k} (f :: * -> k) a.
(Typeable f, Structured a) =>
Proxy (f a) -> Structure
containerStructure
instance Structured a => Structured (NonEmpty a) where structure :: Proxy (NonEmpty a) -> Structure
structure = Proxy (NonEmpty a) -> Structure
forall {k} (f :: * -> k) a.
(Typeable f, Structured a) =>
Proxy (f a) -> Structure
containerStructure

instance (Structured a1, Structured a2) => Structured (a1, a2)
instance (Structured a1, Structured a2, Structured a3) => Structured (a1, a2, a3)
instance (Structured a1, Structured a2, Structured a3, Structured a4) => Structured (a1, a2, a3, a4)
instance (Structured a1, Structured a2, Structured a3, Structured a4, Structured a5) => Structured (a1, a2, a3, a4, a5)
instance (Structured a1, Structured a2, Structured a3, Structured a4, Structured a5, Structured a6) => Structured (a1, a2, a3, a4, a5, a6)
instance (Structured a1, Structured a2, Structured a3, Structured a4, Structured a5, Structured a6, Structured a7) => Structured (a1, a2, a3, a4, a5, a6, a7)

instance Structured BS.ByteString where structure :: Proxy ByteString -> Structure
structure = Proxy ByteString -> Structure
forall {k} (a :: k). Typeable a => Proxy a -> Structure
nominalStructure
instance Structured LBS.ByteString where structure :: Proxy LazyByteString -> Structure
structure = Proxy LazyByteString -> Structure
forall {k} (a :: k). Typeable a => Proxy a -> Structure
nominalStructure

instance Structured T.Text where structure :: Proxy Text -> Structure
structure = Proxy Text -> Structure
forall {k} (a :: k). Typeable a => Proxy a -> Structure
nominalStructure
instance Structured LT.Text where structure :: Proxy Text -> Structure
structure = Proxy Text -> Structure
forall {k} (a :: k). Typeable a => Proxy a -> Structure
nominalStructure

instance (Structured k, Structured v) => Structured (Map.Map k v) where structure :: Proxy (Map k v) -> Structure
structure Proxy (Map k v)
_ = TypeRep -> TypeVersion -> String -> [Structure] -> Structure
Nominal (Proxy (Map k v) -> TypeRep
forall {k} (proxy :: k -> *) (a :: k).
Typeable a =>
proxy a -> TypeRep
typeRep (Proxy (Map k v)
forall {k} (t :: k). Proxy t
Proxy :: Proxy (Map.Map k v))) TypeVersion
0 String
"Map" [Proxy k -> Structure
forall a. Structured a => Proxy a -> Structure
structure (Proxy k
forall {k} (t :: k). Proxy t
Proxy :: Proxy k), Proxy v -> Structure
forall a. Structured a => Proxy a -> Structure
structure (Proxy v
forall {k} (t :: k). Proxy t
Proxy :: Proxy v)]
instance Structured k => Structured (Set.Set k) where structure :: Proxy (Set k) -> Structure
structure = Proxy (Set k) -> Structure
forall {k} (f :: * -> k) a.
(Typeable f, Structured a) =>
Proxy (f a) -> Structure
containerStructure
instance Structured v => Structured (IM.IntMap v) where structure :: Proxy (IntMap v) -> Structure
structure = Proxy (IntMap v) -> Structure
forall {k} (f :: * -> k) a.
(Typeable f, Structured a) =>
Proxy (f a) -> Structure
containerStructure
instance Structured IS.IntSet where structure :: Proxy IntSet -> Structure
structure = Proxy IntSet -> Structure
forall {k} (a :: k). Typeable a => Proxy a -> Structure
nominalStructure
instance Structured v => Structured (Seq.Seq v) where structure :: Proxy (Seq v) -> Structure
structure = Proxy (Seq v) -> Structure
forall {k} (f :: * -> k) a.
(Typeable f, Structured a) =>
Proxy (f a) -> Structure
containerStructure

instance Structured Time.UTCTime where structure :: Proxy UTCTime -> Structure
structure = Proxy UTCTime -> Structure
forall {k} (a :: k). Typeable a => Proxy a -> Structure
nominalStructure
instance Structured Time.DiffTime where structure :: Proxy DiffTime -> Structure
structure = Proxy DiffTime -> Structure
forall {k} (a :: k). Typeable a => Proxy a -> Structure
nominalStructure
instance Structured Time.UniversalTime where structure :: Proxy UniversalTime -> Structure
structure = Proxy UniversalTime -> Structure
forall {k} (a :: k). Typeable a => Proxy a -> Structure
nominalStructure
instance Structured Time.NominalDiffTime where structure :: Proxy NominalDiffTime -> Structure
structure = Proxy NominalDiffTime -> Structure
forall {k} (a :: k). Typeable a => Proxy a -> Structure
nominalStructure
instance Structured Time.Day where structure :: Proxy Day -> Structure
structure = Proxy Day -> Structure
forall {k} (a :: k). Typeable a => Proxy a -> Structure
nominalStructure
instance Structured Time.TimeZone where structure :: Proxy TimeZone -> Structure
structure = Proxy TimeZone -> Structure
forall {k} (a :: k). Typeable a => Proxy a -> Structure
nominalStructure
instance Structured Time.TimeOfDay where structure :: Proxy TimeOfDay -> Structure
structure = Proxy TimeOfDay -> Structure
forall {k} (a :: k). Typeable a => Proxy a -> Structure
nominalStructure
instance Structured Time.LocalTime where structure :: Proxy LocalTime -> Structure
structure = Proxy LocalTime -> Structure
forall {k} (a :: k). Typeable a => Proxy a -> Structure
nominalStructure