registry-messagepack-0.1.0.0: src/Data/Registry/MessagePack/Decoder.hs
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE OverloadedLists #-}
{-# LANGUAGE PartialTypeSignatures #-}
{-# OPTIONS_GHC -Wno-type-defaults #-}
{-# OPTIONS_GHC -fno-warn-partial-type-signatures #-}
{-
A Decoder is used to decode a MessagePack Object into a specific data type
This module provides several functions to create decoders and assemble them into a registry of encoders.
-}
module Data.Registry.MessagePack.Decoder where
import Control.Monad.Fail
import Data.List (nub)
import Data.MessagePack
import Data.Registry hiding (Result)
import Data.Registry.Internal.Types
import Data.Registry.MessagePack.TH
import qualified Data.Vector as Vector
import Language.Haskell.TH
import Language.Haskell.TH.Syntax
import Protolude hiding (Type)
import Prelude (String)
-- * DECODER DATA TYPE
newtype Decoder a = Decoder {decode :: Object -> Result a}
instance Functor Decoder where
fmap f (Decoder d) = Decoder (fmap f . d)
instance Applicative Decoder where
pure a = Decoder (const (pure a))
f <*> a = uncurry ($) <$> decoderAp f a
decoderAp :: Decoder a -> Decoder b -> Decoder (a, b)
decoderAp (Decoder da) (Decoder db) = Decoder $ \case
o@(ObjectArray ls) ->
case reverse (toList ls) of
b : as -> (,) <$> da (ObjectArray $ Vector.fromList $ reverse as) <*> db b
[] -> (,) <$> da o <*> db o
o -> (,) <$> da o <*> db o
-- * DECODING
-- | Use a Decoder to decode a ByteString into the desired type
decodeByteString :: forall a. (Typeable a) => Decoder a -> ByteString -> Either Text a
decodeByteString d bs =
case unpack' bs of
Left e -> Left $ "cannot unpack the bytestring as an Object: " <> show e <> ". The bytestring is: " <> show bs
Right o ->
case decodeObject d o of
Right a -> pure a
Left e -> Left $ "Error: " <> toS e <> ". Cannot decode " <> toS (showType @a) <> " from the Object: " <> show o
-- | Use a Decoder to decode an Object into the desired type
decodeObject :: Decoder a -> Object -> Either Text a
decodeObject (Decoder d) object =
case d object of
Success a -> Right a
Error e -> Left (toS e)
-- * CREATING DECODERS
-- | Add a Decoder a to a registry of decoders when a MessagePack a instance exists
-- usage: decoders = messagePackDecoder @a <: otherDecoders
messagePackDecoder :: forall a. (MessagePack a, Typeable a) => Typed (Decoder a)
messagePackDecoder = fun (messagePackDecoderOf @a)
messagePackDecoderOf :: MessagePack a => Decoder a
messagePackDecoderOf = Decoder fromObject
-- * COMBINATORS
-- | Add a Maybe (Decoder a) to a registry of decoders
-- usage: decoders = decodeMaybeOf @a <: otherDecoders
-- the list of otherDecoders must contain a Decoder a
-- otherwise there will be a compilation error
decodeMaybeOf :: forall a. (Typeable a) => Typed (Decoder a -> Decoder (Maybe a))
decodeMaybeOf = fun (maybeOfDecoder @a)
maybeOfDecoder :: forall a. Decoder a -> Decoder (Maybe a)
maybeOfDecoder (Decoder d) = Decoder $ \case
ObjectNil -> pure Nothing
just -> Just <$> d just
-- | Add a Maybe (a, b) to a registry of decoders
-- usage: decoders = decodePairOf @a @b <: otherDecoders
-- the list of otherDecoders must contain a Decoder a and a Decoder b
-- otherwise there will be a compilation error
decodePairOf :: forall a b. (Typeable a, Typeable b) => Typed (Decoder a -> Decoder b -> Decoder (a, b))
decodePairOf = fun (pairOfDecoder @a @b)
pairOfDecoder :: forall a b. (Typeable a, Typeable b) => Decoder a -> Decoder b -> Decoder (a, b)
pairOfDecoder (Decoder a) (Decoder b) = Decoder $ \case
ObjectArray [oa, ob] -> (,) <$> a oa <*> b ob
other -> Error $ "not a pair of " <> showType @a <> "," <> showType @b <> ": " <> show other
-- | Add a Maybe (a, b, c) to a registry of decoders
-- usage: decoders = decodeTripleOf @a @b @c <: otherDecoders
-- the list of otherDecoders must contain a Decoder a, a Decoder b and a Decoder c
-- otherwise there will be a compilation error
decodeTripleOf :: forall a b c. (Typeable a, Typeable b, Typeable c) => Typed (Decoder a -> Decoder b -> Decoder c -> Decoder (a, b, c))
decodeTripleOf = fun (tripleOfDecoder @a @b @c)
tripleOfDecoder :: forall a b c. (Typeable a, Typeable b, Typeable c) => Decoder a -> Decoder b -> Decoder c -> Decoder (a, b, c)
tripleOfDecoder (Decoder a) (Decoder b) (Decoder c) = Decoder $ \case
ObjectArray [oa, ob, oc] -> (,,) <$> a oa <*> b ob <*> c oc
other -> Error $ "not a triple of " <> showType @a <> "," <> showType @b <> "," <> showType @c <> ": " <> show other
-- | Add a Decoder [a] to a registry of decoders
-- usage: decoders = decodeListOf @a <: otherDecoders
-- the list of otherDecoders must contain a Decoder a
-- otherwise there will be a compilation error
decodeListOf :: forall a. (Typeable a) => Typed (Decoder a -> Decoder [a])
decodeListOf = fun (listOfDecoder @a)
listOfDecoder :: forall a. (Typeable a) => Decoder a -> Decoder [a]
listOfDecoder (Decoder a) = Decoder $ \case
ObjectArray os -> for (toList os) a
other -> Error $ "not a list of " <> showType @a <> ": " <> show other
-- | Add a Decoder (NonEmpty a) to a registry of decoders
-- usage: decoders = decodeNonEmptyOf @a <: otherDecoders
-- the list of otherDecoders must contain a Decoder a
-- otherwise there will be a compilation error
decodeNonEmptyOf :: forall a. (Typeable a) => Typed (Decoder a -> Decoder (NonEmpty a))
decodeNonEmptyOf = fun (nonEmptyOfDecoder @a)
nonEmptyOfDecoder :: forall a. (Typeable a) => Decoder a -> Decoder (NonEmpty a)
nonEmptyOfDecoder (Decoder a) = Decoder $ \case
ObjectArray values ->
case toList values of
[] -> Error $ "expected a NonEmpty of " <> showType @a
o : os -> (:|) <$> a o <*> for os a
other -> Error $ "not a list of " <> showType @a <> ": " <> show other
showType :: forall a. (Typeable a) => String
showType = show (typeRep (Proxy :: Proxy a))
-- * TEMPLATE HASKELL
-- | Make a Decoder for a given data type
-- Usage: $(makeDecoder ''MyDataType <: otherDecoders)
makeDecoder :: Name -> ExpQ
makeDecoder typeName = appE (varE $ mkName "fun") $ do
info <- reify typeName
case info of
TyConI (NewtypeD _context _name _typeVars _kind (RecC constructor [(_, _, other)]) _deriving) -> do
-- \(a::Decoder OldType) -> fmap NewType d
lamE [sigP (varP $ mkName "d") (appT (conT $ mkName "Decoder") (pure other))] (appE (appE (varE $ mkName "fmap") (conE . mkName $ show constructor)) (varE $ mkName "d"))
TyConI (NewtypeD _context _name _typeVars _kind (NormalC constructor [(_, other)]) _deriving) -> do
-- \(a::Decoder OldType) -> fmap NewType d
lamE [sigP (varP $ mkName "d") (appT (conT $ mkName "Decoder") (pure other))] (appE (appE (varE $ mkName "fmap") (conE . mkName $ show constructor)) (varE $ mkName "d"))
TyConI (DataD _context _name _typeVars _kind constructors _deriving) -> do
case constructors of
[] -> do
qReport True "can not make an Decoder for an empty data type"
fail "decoders creation failed"
[c] -> makeConstructorDecoder typeName c
_ -> makeConstructorsDecoder typeName constructors
other -> do
qReport True ("can only create decoders for an ADT, got: " <> show other)
fail "decoders creation failed"
-- | Make a Decoder for a single Constructor, where each field of the constructor is encoded as an element of an ObjectArray
makeConstructorDecoder :: Name -> Con -> ExpQ
makeConstructorDecoder typeName c = do
ts <- typesOf c
cName <- nameOf c
let decoderParameters = (\(t, n) -> sigP (varP (mkName $ "d" <> show n)) (appT (conT $ mkName "Decoder") (pure t))) <$> zip ts [0 ..]
let paramP = varP (mkName "o")
let paramE = varE (mkName "o")
let paramsP = (\n -> varP $ mkName $ "o" <> show n) <$> [0 .. length ts -1]
let matchClause =
match
(conP (mkName "ObjectArray") [viewP (varE (mkName "toList")) (listP paramsP)])
(normalB (applyDecoder cName [0 .. length ts - 1]))
[]
let decoded = caseE paramE [matchClause, makeErrorClause typeName]
-- (\(d1::Decoder Type1) (d2::Decoder Type2) ... -> Decoder (\case
-- ObjectArray (toList -> [o1, o2, ...]) -> Constructor <$> decode d1 o1 <*> decode d2 o2 ...))
-- other -> Error ("not a valid " <> constructorType <> ": " <> show other)
lamE decoderParameters (appE (conE (mkName "Decoder")) (lamE [paramP] decoded))
-- | Make a Decoder for a each Constructor of a data type:
-- - each constructor is specified by an ObjectArray [ObjectInt n, o1, o2, ...]
-- - n specifies the number of the constructor
-- - each object in the array represents a constructor field
makeConstructorsDecoder :: Name -> [Con] -> ExpQ
makeConstructorsDecoder typeName cs = do
ts <- nub . join <$> for cs typesOf
let decoderParameters = (\(t, n) -> sigP (varP (mkName $ "d" <> show n)) (appT (conT $ mkName "Decoder") (pure t))) <$> zip ts [0 ..]
let paramP = varP (mkName "o")
let paramE = varE (mkName "o")
let matchClauses = uncurry (makeMatchClause ts) <$> zip cs [0 ..]
let errorClause = makeErrorClause typeName
let decoded = caseE paramE (matchClauses <> [errorClause])
-- (\(d1::Decoder Type1) (d2::Decoder Type2) ... -> Decoder (\case
-- ObjectArray (toList -> [ObjectInt n, o1, o2, ...]) -> Constructor <$> decode d1 o1 <*> decode d2 o2 ...))
-- other -> Error ("not a valid " <> constructorType <> ": " <> show other)
lamE decoderParameters (appE (conE (mkName "Decoder")) (lamE [paramP] decoded))
-- | Return an error if an object is not an ObjectArray as expected
-- other -> Error (mconcat ["not a valid ", show typeName, ": ", show other])
makeErrorClause :: Name -> MatchQ
makeErrorClause typeName = do
let errorMessage =
appE (varE $ mkName "mconcat") $
listE
[ litE (StringL "not a valid "),
litE (StringL $ show typeName),
litE (StringL ": "),
appE (varE $ mkName "show") (varE $ mkName "_1")
]
match (varP $ mkName "_1") (normalB (appE (conE $ mkName "Error") errorMessage)) []
-- | Decode the nth constructor of a data type
makeMatchClause :: [Type] -> Con -> Integer -> MatchQ
makeMatchClause allTypes c constructorIndex = do
ts <- typesOf c
constructorTypes <- fmap snd <$> indexConstructorTypes allTypes ts
cName <- nameOf c
let paramsP = conP (mkName "ObjectInt") [litP (IntegerL constructorIndex)] : ((\n -> varP $ mkName $ "o" <> show n) <$> constructorTypes)
match
(conP (mkName "ObjectArray") [viewP (varE (mkName "toList")) (listP paramsP)])
(normalB (applyDecoder cName constructorTypes))
[]
-- ConstructorName <$> decode d1 o1 <*> decode d2 o2 ...
applyDecoder :: Name -> [Int] -> ExpQ
applyDecoder cName [] = appE (varE $ mkName "pure") (conE cName)
applyDecoder cName (n : ns) = do
let cons = appE (varE $ mkName "pure") (conE cName)
foldr (\i r -> appE (appE (varE (mkName "ap")) r) $ decodeAt i) (appE (appE (varE (mkName "ap")) cons) $ decodeAt n) (reverse ns)
where
decodeAt i = appE (appE (varE $ mkName "decode") (varE $ mkName ("d" <> show i))) (varE $ mkName $ "o" <> show i)