morpheus-graphql-0.17.0: src/Data/Morpheus/Server/TH/Transform.hs
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE NoImplicitPrelude #-}
module Data.Morpheus.Server.TH.Transform
( toTHDefinitions,
TypeDec (..),
)
where
import Data.Morpheus.Internal.Utils
( capitalTypeName,
elems,
empty,
singleton,
)
import Data.Morpheus.Server.Internal.TH.Types
( ServerConsD,
ServerFieldDefinition (..),
ServerTypeDefinition (..),
toServerField,
)
import Data.Morpheus.Server.Internal.TH.Utils (isParametrizedResolverType)
import Data.Morpheus.Types.Internal.AST
( ANY,
ArgumentDefinition (..),
ConsD (..),
FieldContent (..),
FieldDefinition (..),
FieldName,
FieldsDefinition,
IN,
OUT,
TRUE,
TypeContent (..),
TypeDefinition (..),
TypeKind (..),
TypeName,
TypeRef (..),
UnionMember (..),
hsTypeName,
kindOf,
mkConsEnum,
toFieldName,
)
import Language.Haskell.TH
import Relude hiding (empty)
data TypeDec s = InputType (ServerTypeDefinition IN s) | OutputType (ServerTypeDefinition OUT s)
toTHDefinitions ::
forall s.
Bool ->
[TypeDefinition ANY s] ->
Q [TypeDec s]
toTHDefinitions namespace schema = traverse generateType schema
where
--------------------------------------------
generateType :: TypeDefinition ANY s -> Q (TypeDec s)
generateType
typeDef@TypeDefinition
{ typeName,
typeContent
} =
withType <$> genTypeContent schema toArgsTypeName typeName typeContent
where
toArgsTypeName :: FieldName -> TypeName
toArgsTypeName = mkArgsTypeName namespace typeName
tKind = kindOf typeDef
typeOriginal = Just typeDef
-------------------------
withType (ConsIN tCons) =
InputType
ServerTypeDefinition
{ tName = hsTypeName typeName,
tCons,
typeArgD = empty,
..
}
withType (ConsOUT typeArgD tCons) =
OutputType
ServerTypeDefinition
{ tName = hsTypeName typeName,
tCons,
..
}
toHSTypeRef :: TypeRef -> TypeRef
toHSTypeRef tyRef@TypeRef {typeConName} = tyRef {typeConName = hsTypeName typeConName}
toHSFieldDefinition :: FieldDefinition cat s -> FieldDefinition cat s
toHSFieldDefinition field = field {fieldType = toHSTypeRef (fieldType field)}
toHSServerFieldDefinition :: ServerFieldDefinition cat s -> ServerFieldDefinition cat s
toHSServerFieldDefinition field = field {originalField = toHSFieldDefinition (originalField field)}
mkCons :: TypeName -> [ServerFieldDefinition cat s] -> ServerConsD cat s
mkCons typename fields =
ConsD
{ cName = hsTypeName typename,
cFields = fmap toHSServerFieldDefinition fields
}
mkObjectCons :: TypeName -> [ServerFieldDefinition cat s] -> [ServerConsD cat s]
mkObjectCons typeName fields = [mkCons typeName fields]
mkArgsTypeName :: Bool -> TypeName -> FieldName -> TypeName
mkArgsTypeName namespace typeName fieldName
| namespace = hsTypeName typeName <> argTName
| otherwise = argTName
where
argTName = capitalTypeName (fieldName <> "Args")
mkObjectField ::
[TypeDefinition ANY s] ->
(FieldName -> TypeName) ->
FieldDefinition OUT s ->
Q (ServerFieldDefinition OUT s)
mkObjectField
schema
genArgsTypeName
originalField@FieldDefinition
{ fieldName,
fieldContent,
fieldType = TypeRef {typeConName}
} = do
isParametrized <- isParametrizedResolverType typeConName schema
pure
ServerFieldDefinition
{ argumentsTypeName = fieldContent >>= fieldCont,
..
}
where
fieldCont :: FieldContent TRUE OUT s -> Maybe TypeName
fieldCont (FieldArgs arguments)
| not (null arguments) = Just $ genArgsTypeName fieldName
fieldCont _ = Nothing
data BuildPlan s
= ConsIN [ServerConsD IN s]
| ConsOUT [ServerTypeDefinition IN s] [ServerConsD OUT s]
genTypeContent ::
[TypeDefinition ANY s] ->
(FieldName -> TypeName) ->
TypeName ->
TypeContent TRUE ANY s ->
Q (BuildPlan s)
genTypeContent _ _ _ DataScalar {} = pure (ConsIN [])
genTypeContent _ _ _ (DataEnum tags) = pure $ ConsIN (fmap mkConsEnum tags)
genTypeContent _ _ typeName (DataInputObject fields) =
pure $ ConsIN (mkObjectCons typeName $ map toServerField $ elems fields)
genTypeContent _ _ _ DataInputUnion {} = fail "Input Unions not Supported"
genTypeContent schema toArgsTyName typeName DataInterface {interfaceFields} = do
typeArgD <- genArgumentTypes toArgsTyName interfaceFields
objCons <- mkObjectCons typeName <$> traverse (mkObjectField schema toArgsTyName) (elems interfaceFields)
pure $ ConsOUT typeArgD objCons
genTypeContent schema toArgsTyName typeName DataObject {objectFields} = do
typeArgD <- genArgumentTypes toArgsTyName objectFields
objCons <-
mkObjectCons typeName
<$> traverse (mkObjectField schema toArgsTyName) (elems objectFields)
pure $ ConsOUT typeArgD objCons
genTypeContent _ _ typeName (DataUnion members) =
pure $ ConsOUT [] (fmap unionCon members)
where
unionCon UnionMember {memberName} =
mkCons
cName
$ singleton
ServerFieldDefinition
{ isParametrized = True,
argumentsTypeName = Nothing,
originalField =
FieldDefinition
{ fieldName = "un" <> toFieldName cName,
fieldType =
TypeRef
{ typeConName = utName,
typeWrappers = []
},
fieldDescription = Nothing,
fieldDirectives = empty,
fieldContent = Nothing
}
}
where
cName = hsTypeName typeName <> utName
utName = hsTypeName memberName
genArgumentTypes :: (FieldName -> TypeName) -> FieldsDefinition OUT s -> Q [ServerTypeDefinition IN s]
genArgumentTypes genArgsTypeName fields =
concat <$> traverse (genArgumentType genArgsTypeName) (elems fields)
genArgumentType :: (FieldName -> TypeName) -> FieldDefinition OUT s -> Q [ServerTypeDefinition IN s]
genArgumentType namespaceWith FieldDefinition {fieldName, fieldContent = Just (FieldArgs arguments)}
| not (null arguments) =
pure
[ ServerTypeDefinition
{ tName,
tCons = [mkCons tName $ map (toServerField . argument) $ elems arguments],
tKind = KindInputObject,
typeArgD = [],
typeOriginal = Nothing
}
]
where
tName = hsTypeName (namespaceWith fieldName)
genArgumentType _ _ = pure []