{-# OPTIONS_GHC -Wno-orphans #-} {-# OPTIONS_GHC -Wno-simplifiable-class-constraints #-} {-# LANGUAGE CPP #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE IncoherentInstances #-} {-# LANGUAGE KindSignatures #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE StrictData #-} {-# LANGUAGE Trustworthy #-} {-# LANGUAGE TypeOperators #-} {-# LANGUAGE UndecidableInstances #-} module Data.MessagePack.Types.Generic () where import Control.Applicative (Applicative, (<$>), (<*>)) import Control.Monad ((>=>)) import Control.Monad.Trans.State.Strict (StateT, evalStateT, get, put) import Control.Monad.Validate (MonadValidate, refute) import Data.Bits (shiftR) import Data.Word (Word64) import GHC.Generics import Data.MessagePack.Types.Class import Data.MessagePack.Types.DecodeError (DecodeError) import Data.MessagePack.Types.Object (Object (..)) instance GMessagePack V1 where gToObject = undefined gFromObject _ _ = refute "can't instantiate void type" instance GMessagePack U1 where gToObject _ U1 = ObjectNil gFromObject _ ObjectNil = return U1 gFromObject _ _ = refute "invalid encoding for custom unit type" instance GProdPack a => GMessagePack a where gToObject cfg = toObject cfg . prodToObject cfg gFromObject cfg o = do list <- fromObjectWith cfg o evalStateT (prodFromObject cfg) list instance (GSumPack a, GSumPack b, SumSize a, SumSize b) => GMessagePack (a :+: b) where gToObject cfg = sumToObject cfg 0 size where size = unTagged (sumSize :: Tagged (a :+: b) Word64) gFromObject cfg = \case ObjectWord code -> checkSumFromObject0 cfg size (fromIntegral code) o -> fromObjectWith cfg o >>= uncurry (checkSumFromObject cfg size) where size = unTagged (sumSize :: Tagged (a :+: b) Word64) instance GMessagePack a => GMessagePack (M1 t c a) where gToObject cfg (M1 x) = gToObject cfg x gFromObject cfg x = M1 <$> gFromObject cfg x instance MessagePack a => GMessagePack (K1 i a) where gToObject cfg (K1 x) = toObject cfg x gFromObject cfg o = K1 <$> fromObjectWith cfg o -- Product type packing. class GProdPack f where prodToObject :: Config -> f a -> [Object] prodFromObject :: ( Applicative m , Monad m , MonadValidate DecodeError m ) => Config -> StateT [Object] m (f a) instance (GProdPack a, GProdPack b) => GProdPack (a :*: b) where prodToObject cfg (a :*: b) = prodToObject cfg a ++ prodToObject cfg b prodFromObject cfg = do f <- prodFromObject cfg g <- prodFromObject cfg pure $ f :*: g instance GMessagePack a => GProdPack (M1 t c a) where prodToObject cfg (M1 x) = [gToObject cfg x] prodFromObject cfg = do objs <- get case objs of (x:xs) -> do put xs M1 <$> gFromObject cfg x _ -> refute "invalid encoding for product type" -- Sum type packing. checkSumFromObject0 :: ( Applicative m , Monad m , MonadValidate DecodeError m ) => (GSumPack f) => Config -> Word64 -> Word64 -> m (f a) checkSumFromObject0 cfg size code | code < size = sumFromObject cfg code size ObjectNil | otherwise = refute "invalid encoding for sum type" checkSumFromObject :: ( Applicative m , Monad m , MonadValidate DecodeError m ) => (GSumPack f) => Config -> Word64 -> Word64 -> Object -> m (f a) checkSumFromObject cfg size code x | code < size = sumFromObject cfg code size x | otherwise = refute "invalid encoding for sum type" class GSumPack f where sumToObject :: Config -> Word64 -> Word64 -> f a -> Object sumFromObject :: ( Applicative m , Monad m , MonadValidate DecodeError m ) => Config -> Word64 -> Word64 -> Object -> m (f a) instance (GSumPack a, GSumPack b) => GSumPack (a :+: b) where sumToObject cfg code size = \case L1 x -> sumToObject cfg code sizeL x R1 x -> sumToObject cfg (code + sizeL) sizeR x where sizeL = size `shiftR` 1 sizeR = size - sizeL sumFromObject cfg code size x | code < sizeL = L1 <$> sumFromObject cfg code sizeL x | otherwise = R1 <$> sumFromObject cfg (code - sizeL) sizeR x where sizeL = size `shiftR` 1 sizeR = size - sizeL instance GSumPack (C1 c U1) where sumToObject cfg code _ _ = toObject cfg code sumFromObject cfg _ _ = gFromObject cfg instance GMessagePack a => GSumPack (C1 c a) where sumToObject cfg code _ x = toObject cfg (code, gToObject cfg x) sumFromObject cfg _ _ = gFromObject cfg -- Sum size. class SumSize f where sumSize :: Tagged f Word64 newtype Tagged (s :: * -> *) b = Tagged { unTagged :: b } instance (SumSize a, SumSize b) => SumSize (a :+: b) where sumSize = Tagged $ unTagged (sumSize :: Tagged a Word64) + unTagged (sumSize :: Tagged b Word64) instance SumSize (C1 c a) where sumSize = Tagged 1