morpheus-graphql-server-0.27.1: src/Data/Morpheus/Server/Deriving/Internal/Decode/Rep.hs
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE NoImplicitPrelude #-}
module Data.Morpheus.Server.Deriving.Internal.Decode.Rep
( DecodeRep (..),
)
where
import Control.Monad.Except (MonadError (throwError))
import Data.Morpheus.Server.Deriving.Internal.Decode.Utils
( Context (..),
CountFields (..),
DecoderT,
DescribeCons,
decodeFieldWith,
getFieldName,
getUnionInfos,
setVariantRef,
withInputObject,
withInputUnion,
)
import Data.Morpheus.Server.Deriving.Utils.Proxy
( selNameProxy,
)
import Data.Morpheus.Server.Deriving.Utils.Use
( UseDeriving (..),
UseValue (..),
)
import Data.Morpheus.Types.Internal.AST
( Object,
TypeName,
VALID,
ValidObject,
ValidValue,
Value (..),
internal,
msg,
)
import GHC.Generics
import Relude
decideEither ::
(DecodeRep gql args f, DecodeRep gql args g) =>
UseDeriving gql args ->
([TypeName], [TypeName]) ->
TypeName ->
ValidValue ->
DecoderT ((f :+: g) a)
decideEither dir (left, right) name value
| name `elem` left = L1 <$> decodeRep dir value
| name `elem` right = R1 <$> decodeRep dir value
| otherwise =
throwError $
internal $
"Constructor \""
<> msg name
<> "\" could not find in Union"
decodeInputUnionObject ::
(DecodeRep gql args f, DecodeRep gql args g) =>
UseDeriving gql args ->
([TypeName], [TypeName]) ->
TypeName ->
Object VALID ->
ValidObject ->
DecoderT ((f :+: g) a)
decodeInputUnionObject dir (l, r) name unions object
| [name] == l = L1 <$> decodeRep dir (Object object)
| [name] == r = R1 <$> decodeRep dir (Object object)
| otherwise = decideEither dir (l, r) name (Object unions)
class DecodeRep gql args (f :: Type -> Type) where
decodeRep :: UseDeriving gql args -> ValidValue -> DecoderT (f a)
instance (Datatype d, DecodeRep gql args f) => DecodeRep gql args (M1 D d f) where
decodeRep dir value = M1 <$> decodeRep dir value
instance (DescribeCons gql a, DescribeCons gql b, DecodeRep gql args a, DecodeRep gql args b) => DecodeRep gql args (a :+: b) where
decodeRep dir (Object obj) =
do
(kind, lr) <- getUnionInfos (dirGQL dir) (Proxy @(a :+: b))
setVariantRef kind $ withInputUnion (decodeInputUnionObject dir lr) obj
decodeRep dir (Enum name) = do
(_, (l, r)) <- getUnionInfos (dirGQL dir) (Proxy @(a :+: b))
visitor <- asks enumVisitor
decideEither dir (map visitor l, map visitor r) name (Enum name)
decodeRep _ _ = throwError (internal "lists and scalars are not allowed in Union")
instance (Constructor c, DecodeFields gql args a) => DecodeRep gql args (M1 C c a) where
decodeRep dir = fmap M1 . decodeFields dir 0
class DecodeFields gql args (f :: Type -> Type) where
decodeFields :: UseDeriving gql args -> Int -> ValidValue -> DecoderT (f a)
instance (DecodeFields gql args f, DecodeFields gql args g, CountFields g) => DecodeFields gql args (f :*: g) where
decodeFields dir index gql =
(:*:)
<$> decodeFields dir index gql
<*> decodeFields dir (index + countFields (Proxy @g)) gql
instance (Selector s, args a) => DecodeFields gql args (M1 S s (K1 i a)) where
decodeFields UseDeriving {dirArgs} index value =
M1 . K1 <$> do
Context {isVariantRef, fieldVisitor} <- ask
if isVariantRef
then lift (useDecodeValue dirArgs value)
else
let fieldName = fieldVisitor $ getFieldName (selNameProxy (Proxy @s)) index
fieldDecoder = decodeFieldWith (lift . useDecodeValue dirArgs) fieldName
in withInputObject fieldDecoder value
instance DecodeFields gql args U1 where
decodeFields _ _ _ = pure U1