{-# 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 -- MORPHEUS 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 , .. } -- FIXME: dirty fix for introspection | 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 [] -- [(FieldDefinition, TypeUpdater)] 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) } } |]