{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeOperators #-} -- {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE RecordWildCards #-} module Data.Morpheus.Execution.Document.Convert ( toTHDefinitions ) where import Data.Semigroup ( (<>) ) import Data.Text (unpack) import Language.Haskell.TH -- MORPHEUS import Data.Morpheus.Types.Internal.TH (infoTyVars) import Data.Morpheus.Execution.Internal.Utils ( capital ) import Data.Morpheus.Types.Internal.AST ( FieldDefinition(..) , TypeContent(..) , TypeDefinition(..) , TypeRef(..) , DataEnumValue(..) , ConsD(..) , GQLTypeD(..) , TypeD(..) , Key , InputFieldsDefinition(..) , ArgumentsDefinition(..) , hasArguments , lookupWith , toHSFieldDefinition , hsTypeName , kindOf ) import Data.Morpheus.Types.Internal.Operation (Listable(..)) m_ :: Key m_ = "m" getTypeArgs :: Key -> [TypeDefinition] -> Q (Maybe Key) getTypeArgs "__TypeKind" _ = pure Nothing getTypeArgs "Boolean" _ = pure Nothing getTypeArgs "String" _ = pure Nothing getTypeArgs "Int" _ = pure Nothing getTypeArgs "Float" _ = pure Nothing getTypeArgs key lib = case typeContent <$> lookupWith typeName key lib of Just x -> pure (kindToTyArgs x) Nothing -> getTyArgs <$> reify (mkName $ unpack key) getTyArgs :: Info -> Maybe Key getTyArgs x | null (infoTyVars x) = Nothing | otherwise = Just m_ kindToTyArgs :: TypeContent -> Maybe Key kindToTyArgs DataObject{} = Just m_ kindToTyArgs DataUnion{} = Just m_ kindToTyArgs _ = Nothing toTHDefinitions :: Bool -> [TypeDefinition] -> Q [GQLTypeD] toTHDefinitions namespace lib = traverse renderTHType lib where renderTHType :: TypeDefinition -> Q GQLTypeD renderTHType x = generateType x where genArgsTypeName :: Key -> Key genArgsTypeName fieldName | namespace = hsTypeName (typeName x) <> argTName | otherwise = argTName where argTName = capital fieldName <> "Args" --------------------------------------------------------------------------------------------- genResField :: FieldDefinition -> Q FieldDefinition genResField field@FieldDefinition { fieldName, fieldArgs, fieldType = typeRef@TypeRef { typeConName } } = do typeArgs <- getTypeArgs typeConName lib pure $ field { fieldType = typeRef { typeConName = hsTypeName typeConName, typeArgs } , fieldArgs = fieldArguments } where fieldArguments | hasArguments fieldArgs = fieldArgs { argumentsTypename = Just $ genArgsTypeName fieldName } | otherwise = fieldArgs -------------------------------------------- generateType :: TypeDefinition -> Q GQLTypeD generateType typeOriginal@TypeDefinition { typeName, typeContent, typeMeta } = genType typeContent where buildType :: [ConsD] -> TypeD buildType tCons = TypeD { tName = hsTypeName typeName , tMeta = typeMeta , tNamespace = [] , tCons } buildObjectCons :: [FieldDefinition] -> [ConsD] buildObjectCons cFields = [ ConsD { cName = hsTypeName typeName , cFields } ] typeKindD = kindOf typeOriginal genType :: TypeContent -> Q GQLTypeD genType (DataEnum tags) = pure GQLTypeD { typeD = TypeD { tName = hsTypeName typeName , tNamespace = [] , tCons = map enumOption tags , tMeta = typeMeta } , typeArgD = [] , .. } where enumOption DataEnumValue { enumName } = ConsD { cName = hsTypeName enumName, cFields = [] } genType DataScalar {} = fail "Scalar Types should defined By Native Haskell Types" genType DataInputUnion {} = fail "Input Unions not Supported" genType DataInterface {} = fail "interfaces must be eliminated in Validation" genType (DataInputObject fields) = pure GQLTypeD { typeD = buildType $ buildObjectCons $ genInputFields fields , typeArgD = [] , .. } genType DataObject {objectFields} = do typeArgD <- concat <$> traverse (genArgumentType genArgsTypeName) (toList objectFields) objCons <- buildObjectCons <$> traverse genResField (toList objectFields) pure GQLTypeD { typeD = buildType objCons , typeArgD , .. } genType (DataUnion members) = pure GQLTypeD { typeD = buildType (map unionCon members) , typeArgD = [] , .. } where unionCon memberName = ConsD { cName , cFields = [ FieldDefinition { fieldName = "un" <> cName , fieldType = TypeRef { typeConName = utName , typeArgs = Just m_ , typeWrappers = [] } , fieldMeta = Nothing , fieldArgs = NoArguments } ] } where cName = hsTypeName typeName <> utName utName = hsTypeName memberName genArgumentType :: (Key -> Key) -> FieldDefinition -> Q [TypeD] genArgumentType _ FieldDefinition { fieldArgs = NoArguments } = pure [] genArgumentType namespaceWith FieldDefinition { fieldName, fieldArgs } = pure [ TypeD { tName , tNamespace = [] , tCons = [ ConsD { cName = hsTypeName tName , cFields = genArguments fieldArgs } ] , tMeta = Nothing } ] where tName = namespaceWith (hsTypeName fieldName) genArguments :: ArgumentsDefinition -> [FieldDefinition] genArguments = genInputFields . InputFieldsDefinition . arguments genInputFields :: InputFieldsDefinition -> [FieldDefinition] genInputFields = map toHSFieldDefinition . toList