ribosome-0.2.0.0: lib/Ribosome/Msgpack/Decode.hs
{-# LANGUAGE DefaultSignatures #-}
{-# LANGUAGE TypeOperators #-}
module Ribosome.Msgpack.Decode(
MsgpackDecode(..),
) where
import GHC.Generics (
Generic,
Rep,
(:*:)(..),
M1(..),
D1,
C1,
S1,
K1(..),
Selector,
Constructor,
selName,
to,
conIsRecord,
)
import Data.ByteString.Internal (unpackChars)
import Data.Map.Strict (Map, (!?))
import qualified Data.Map.Strict as Map (fromList, toList)
import Data.MessagePack (Object(..))
import Ribosome.Msgpack.Util (Err)
import qualified Ribosome.Msgpack.Util as Util (string, invalid, missingRecordKey, illegalType)
class MsgpackDecode a where
fromMsgpack :: Object -> Either Err a
default fromMsgpack :: (Generic a, GMsgpackDecode (Rep a)) => Object -> Either Err a
fromMsgpack = fmap to . gMsgpackDecode
class GMsgpackDecode f where
gMsgpackDecode :: Object -> Either Err (f a)
class MsgpackDecodeProd f where
msgpackDecodeRecord :: Map Object Object -> Either Err (f a)
msgpackDecodeProd :: [Object] -> Either Err ([Object], f a)
instance GMsgpackDecode f => GMsgpackDecode (D1 c f) where
gMsgpackDecode = fmap M1 . gMsgpackDecode @f
instance (Constructor c, MsgpackDecodeProd f) => GMsgpackDecode (C1 c f) where
gMsgpackDecode =
fmap M1 . decode
where
isRec = conIsRecord (undefined :: t c f p)
decode o@(ObjectMap om) =
if isRec then msgpackDecodeRecord om else Util.invalid "illegal ObjectMap for product" o
decode o@(ObjectArray oa) =
if isRec then Util.invalid "illegal ObjectArray for record" o else decode'
where
decode' = do
(rest, a) <- msgpackDecodeProd oa
case rest of
[] -> Right a
_ -> Util.invalid "too many values for product" o
decode o =
Util.invalid "illegal Object for constructor" o
instance (MsgpackDecodeProd f, MsgpackDecodeProd g) => MsgpackDecodeProd (f :*: g) where
msgpackDecodeRecord o = do
left <- msgpackDecodeRecord o
right <- msgpackDecodeRecord o
return $ left :*: right
msgpackDecodeProd o = do
(rest, left) <- msgpackDecodeProd o
(rest1, right) <- msgpackDecodeProd rest
return (rest1, left :*: right)
instance (Selector s, GMsgpackDecode f) => MsgpackDecodeProd (S1 s f) where
msgpackDecodeRecord o =
maybe (Util.missingRecordKey key (ObjectMap o)) (fmap M1 . gMsgpackDecode) (o !? Util.string key)
where
key = selName (undefined :: t s f p)
msgpackDecodeProd (cur:rest) = do
a <- gMsgpackDecode cur
return $ (rest, M1 a)
msgpackDecodeProd [] = Util.invalid "too few values for product" ObjectNil
instance MsgpackDecode a => GMsgpackDecode (K1 i a) where
gMsgpackDecode = fmap K1 . fromMsgpack
instance (Ord k, MsgpackDecode k, MsgpackDecode v) => MsgpackDecode (Map k v) where
fromMsgpack (ObjectMap om) = do
m <- traverse decodePair $ Map.toList om
Right $ Map.fromList m
where
decodePair (k, v) = do
k1 <- fromMsgpack k
v1 <- fromMsgpack v
return (k1, v1)
fromMsgpack o = Util.illegalType "Map" o
instance MsgpackDecode Int where
fromMsgpack (ObjectInt i) = Right $ fromIntegral i
fromMsgpack o = Util.illegalType "Int" o
instance {-# OVERLAPPING #-} MsgpackDecode String where
fromMsgpack (ObjectString os) = Right $ unpackChars os
fromMsgpack o = Util.illegalType "String" o
instance {-# OVERLAPPABLE #-} MsgpackDecode a => MsgpackDecode [a] where
fromMsgpack (ObjectArray oa) = traverse fromMsgpack oa
fromMsgpack o = Util.illegalType "List" o