registry-messagepack-0.3.0.1: 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 as MP
import Data.Registry
import Data.Registry.Internal.Types
import Data.Registry.MessagePack.Options
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 -> MP.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
-- | Create a Decoder from a Read instance
readDecoder :: forall a. (Typeable a, Read a) => Typed (Decoder String -> Decoder a)
readDecoder = fun readDecoderOf
readDecoderOf :: forall a. (Read a) => Decoder String -> Decoder a
readDecoderOf ds = Decoder $ decode ds >=> (either Error Success . readEither)
-- * 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 = makeDecoderWith defaultOptions
-- | Make a Decoder for a given data type, where all constructors and decoded types are qualified
-- Usage: $(makeDecoderQualified ''MyDataType <: otherDecoders)
makeDecoderQualified :: Name -> ExpQ
makeDecoderQualified = makeDecoderWith (Options identity)
-- | Make a Decoder for a given data type, where all constructors and decoded types are qualified
-- Usage: $(makeDecoderQualifiedLast ''MyDataType <: otherDecoders)
makeDecoderQualifiedLast :: Name -> ExpQ
makeDecoderQualifiedLast = makeDecoderWith (Options qualifyWithLastName)
-- | Make a Decoder with a given set of options
-- Usage: $(makeDecoderWith (Options qualify) ''MyDataType <: otherDecoders)
makeDecoderWith :: Options -> Name -> ExpQ
makeDecoderWith options 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
let cName = mkName $ show $ makeName options constructor
lamE [sigP (varP $ mkName "d") (appT (conT $ mkName "Decoder") (pure other))] (appE (appE (varE $ mkName "fmap") (conE cName)) (varE $ mkName "d"))
TyConI (NewtypeD _context _name _typeVars _kind (NormalC constructor [(_, other)]) _deriving) -> do
-- \(a::Decoder OldType) -> fmap NewType d
let cName = mkName $ show $ makeName options constructor
lamE [sigP (varP $ mkName "d") (appT (conT $ mkName "Decoder") (pure other))] (appE (appE (varE $ mkName "fmap") (conE cName)) (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 options typeName c
_ -> makeConstructorsDecoder options 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 :: Options -> Name -> Con -> ExpQ
makeConstructorDecoder options typeName c = do
ts <- typesOf c
cName <- makeName options <$> 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 options 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 :: Options -> Name -> [Con] -> ExpQ
makeConstructorsDecoder options 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 options ts) <$> zip cs [0 ..]
let errorClause = makeErrorClause options 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 :: Options -> Name -> MatchQ
makeErrorClause options typeName = do
let errorMessage =
appE (varE $ mkName "mconcat") $
listE
[ litE (StringL "not a valid "),
litE (StringL . show $ makeName options 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 :: Options -> [Type] -> Con -> Integer -> MatchQ
makeMatchClause options allTypes c constructorIndex = do
ts <- typesOf c
constructorTypes <- fmap snd <$> indexConstructorTypes allTypes ts
cName <- makeName options <$> 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)