{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE RecordWildCards #-}
module Data.Morpheus.Execution.Document.Introspect
( deriveObjectRep , instanceIntrospect
) where
import Data.Maybe(maybeToList)
import Data.Proxy (Proxy (..))
import Data.Text (unpack,Text)
import Data.Typeable (Typeable)
import Language.Haskell.TH
import Data.Morpheus.Execution.Internal.Declare (tyConArgs)
import Data.Morpheus.Execution.Server.Introspect (Introspect (..), introspectObjectFields, IntrospectRep (..),TypeScope(..))
import Data.Morpheus.Types.GQLType (GQLType (__typeName), TRUE)
import Data.Morpheus.Types.Internal.AST ( ConsD (..)
, TypeD (..)
, TypeDefinition(..)
, TypeContent(..)
, ArgumentsDefinition(..)
, FieldDefinition (..)
, insertType
, DataTypeKind(..)
, TypeRef (..)
, unsafeFromFields
, unsafeFromInputFields
)
import Data.Morpheus.Types.Internal.TH (instanceFunD, instanceProxyFunD,instanceHeadT, instanceHeadMultiT, typeT)
instanceIntrospect :: TypeDefinition -> Q [Dec]
instanceIntrospect TypeDefinition { typeName, typeContent = DataEnum enumType , .. }
| typeName `elem` ["__DirectiveLocation","__TypeKind"] = pure []
| otherwise = pure <$> instanceD (cxt []) iHead [defineIntrospect]
where
iHead = instanceHeadT ''Introspect typeName []
defineIntrospect = instanceProxyFunD ('introspect,body)
where
body =[| insertType TypeDefinition { typeContent = DataEnum enumType, .. } |]
instanceIntrospect _ = pure []
deriveObjectRep :: (TypeD, Maybe DataTypeKind) -> Q [Dec]
deriveObjectRep (TypeD {tName, tCons = [ConsD {cFields}]}, tKind) =
pure <$> instanceD (cxt constrains) iHead methods
where
mainTypeName = typeT (mkName $ unpack tName) typeArgs
typeArgs = concatMap tyConArgs (maybeToList tKind)
constrains = map conTypeable typeArgs
where
conTypeable name = typeT ''Typeable [name]
iHead = instanceHeadMultiT ''IntrospectRep (conT ''TRUE) [mainTypeName]
methods = [instanceFunD 'introspectRep ["_proxy1", "_proxy2"] body]
where
body
| tKind == Just KindInputObject || null tKind = [| (DataInputObject $ unsafeFromInputFields $(buildFields cFields), concat $(buildTypes cFields))|]
| otherwise = [| (DataObject [] $ unsafeFromFields $(buildFields cFields), concat $(buildTypes cFields))|]
deriveObjectRep _ = pure []
buildTypes :: [FieldDefinition] -> ExpQ
buildTypes = listE . concatMap introspectField
where
introspectField FieldDefinition {fieldType, fieldArgs } =
[|[introspect $(proxyT fieldType)]|] : inputTypes fieldArgs
where
inputTypes ArgumentsDefinition { argumentsTypename = Just argsTypeName }
| argsTypeName /= "()" = [[| snd $ introspectObjectFields (Proxy :: Proxy TRUE) (argsTypeName, InputType,$(proxyT tAlias))|]]
where
tAlias = TypeRef {typeConName = argsTypeName, typeWrappers = [], typeArgs = Nothing}
inputTypes _ = []
conTX :: Text -> Q Type
conTX = conT . mkName . unpack
varTX :: Text -> Q Type
varTX = varT . mkName . unpack
proxyT :: TypeRef -> Q Exp
proxyT TypeRef {typeConName, typeArgs} = [|(Proxy :: Proxy $(genSig typeArgs))|]
where
genSig (Just m) = appT (conTX typeConName) (varTX m)
genSig _ = conTX typeConName
buildFields :: [FieldDefinition] -> ExpQ
buildFields = listE . map buildField
where
buildField f@FieldDefinition {fieldType } = [| f { fieldType = fieldType {typeConName = __typeName $(proxyT fieldType) } } |]