module Data.MessagePack.Generic () where
import Control.Applicative (Applicative, (<$>), (<*>))
import Control.Monad ((>=>))
import Data.Bits (shiftR)
import Data.Word (Word64)
import GHC.Generics
import Data.MessagePack.Class
import Data.MessagePack.Object (Object (..))
instance GMessagePack U1 where
gToObject U1 = ObjectNil
gFromObject ObjectNil = return U1
gFromObject _ = fail "invalid encoding for custom unit type"
instance (GMessagePack a, GProdPack b) => GMessagePack (a :*: b) where
gToObject = toObject . prodToObject
gFromObject = fromObject >=> prodFromObject
instance (GSumPack a, GSumPack b, SumSize a, SumSize b) => GMessagePack (a :+: b) where
gToObject = sumToObject 0 size
where
size = unTagged (sumSize :: Tagged (a :+: b) Word64)
gFromObject = \case
ObjectInt code -> checkSumFromObject0 size (fromIntegral code)
o -> fromObject o >>= uncurry (checkSumFromObject size)
where
size = unTagged (sumSize :: Tagged (a :+: b) Word64)
instance GMessagePack a => GMessagePack (M1 t c a) where
gToObject (M1 x) = gToObject x
gFromObject x = M1 <$> gFromObject x
instance MessagePack a => GMessagePack (K1 i a) where
gToObject (K1 x) = toObject x
gFromObject o = K1 <$> fromObject o
class GProdPack f where
prodToObject :: f a -> [Object]
prodFromObject :: (Applicative m, Monad m) => [Object] -> m (f a)
instance (GMessagePack a, GProdPack b) => GProdPack (a :*: b) where
prodToObject (a :*: b) = gToObject a : prodToObject b
prodFromObject (a:b) = (:*:) <$> gFromObject a <*> prodFromObject b
prodFromObject _ = fail "invalid encoding for product type"
instance GMessagePack a => GProdPack (M1 t c a) where
prodToObject (M1 x) = [gToObject x]
prodFromObject [x] = M1 <$> gFromObject x
prodFromObject _ = fail "invalid encoding for product type"
checkSumFromObject0 :: (Applicative m, Monad m) => (GSumPack f) => Word64 -> Word64 -> m (f a)
checkSumFromObject0 size code
| code < size = sumFromObject code size ObjectNil
| otherwise = fail "invalid encoding for sum type"
checkSumFromObject :: (Applicative m, Monad m) => (GSumPack f) => Word64 -> Word64 -> Object -> m (f a)
checkSumFromObject size code x
| code < size = sumFromObject code size x
| otherwise = fail "invalid encoding for sum type"
class GSumPack f where
sumToObject :: Word64 -> Word64 -> f a -> Object
sumFromObject :: (Applicative m, Monad m) => Word64 -> Word64 -> Object -> m (f a)
instance (GSumPack a, GSumPack b) => GSumPack (a :+: b) where
sumToObject code size = \case
L1 x -> sumToObject code sizeL x
R1 x -> sumToObject (code + sizeL) sizeR x
where
sizeL = size `shiftR` 1
sizeR = size sizeL
sumFromObject code size x
| code < sizeL = L1 <$> sumFromObject code sizeL x
| otherwise = R1 <$> sumFromObject (code sizeL) sizeR x
where
sizeL = size `shiftR` 1
sizeR = size sizeL
instance GSumPack (C1 c U1) where
sumToObject code _ _ = toObject code
sumFromObject _ _ = gFromObject
instance GMessagePack a => GSumPack (C1 c a) where
sumToObject code _ x = toObject (code, gToObject x)
sumFromObject _ _ = gFromObject
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