morpheus-graphql-code-gen-0.27.0: src/Data/Morpheus/CodeGen/Server/Interpreting/Utils.hs
{-# LANGUAGE CPP #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE NoImplicitPrelude #-}
module Data.Morpheus.CodeGen.Server.Interpreting.Utils
( CodeGenMonad (..),
CodeGenM,
ServerCodeGenContext (..),
getFieldName,
getEnumName,
isParamResolverType,
lookupFieldType,
isSubscription,
inType,
getFieldTypeName,
checkTypeExistence,
)
where
import Data.Morpheus.CodeGen.Internal.AST
( CodeGenTypeName (CodeGenTypeName),
fromTypeName,
)
import Data.Morpheus.CodeGen.TH
( ToName (toName),
)
import Data.Morpheus.CodeGen.Utils
( CodeGenT,
Flags,
camelCaseFieldName,
requireExternal,
toHaskellTypeName,
)
import Data.Morpheus.Error (gqlWarnings)
import Data.Morpheus.Internal.Ext (GQLResult)
import Data.Morpheus.Internal.Utils (selectOr)
import Data.Morpheus.Types.Internal.AST
( ANY,
CONST,
DirectiveDefinition (..),
FieldDefinition (..),
FieldName,
GQLError,
OperationType (..),
TypeContent (..),
TypeDefinition (..),
TypeKind (..),
TypeName,
TypeRef (..),
isResolverType,
lookupWith,
packName,
unpackName,
)
import Language.Haskell.TH
( Dec (..),
Info (..),
Q,
TyVarBndr,
reify,
)
import Relude hiding (ByteString, get)
class (MonadReader ServerCodeGenContext m, Monad m, MonadFail m, CodeGenMonad m, MonadState Flags m) => CodeGenM m
instance CodeGenMonad m => CodeGenM (CodeGenT ServerCodeGenContext m)
data ServerCodeGenContext = ServerCodeGenContext
{ toArgsTypeName :: FieldName -> TypeName,
typeDefinitions :: [TypeDefinition ANY CONST],
directiveDefinitions :: [DirectiveDefinition CONST],
currentTypeName :: Maybe TypeName,
currentKind :: Maybe TypeKind,
hasNamespace :: Bool
}
checkTypeExistence :: CodeGenM m => TypeName -> m ()
checkTypeExistence name = do
exists <- isJust <$> lookupType name
if exists
then pure ()
else requireExternal (unpackName name)
getFieldTypeName :: CodeGenM m => TypeName -> m TypeName
getFieldTypeName name = checkTypeExistence name $> packName (toHaskellTypeName name)
getFieldName :: CodeGenM m => FieldName -> m FieldName
getFieldName fieldName = do
ServerCodeGenContext {hasNamespace, currentTypeName} <- ask
pure $
if hasNamespace
then maybe fieldName (`camelCaseFieldName` fieldName) currentTypeName
else fieldName
getEnumName :: MonadReader ServerCodeGenContext m => TypeName -> m CodeGenTypeName
getEnumName enumName = do
ServerCodeGenContext {hasNamespace, currentTypeName} <- ask
pure $
if hasNamespace
then CodeGenTypeName (map coerce $ maybeToList currentTypeName) [] enumName
else fromTypeName enumName
class (Monad m, MonadFail m) => CodeGenMonad m where
isParametrizedType :: TypeName -> m Bool
printWarnings :: [GQLError] -> m ()
instance CodeGenMonad m => CodeGenMonad (CodeGenT ctx m) where
isParametrizedType = lift . isParametrizedType
printWarnings = lift . printWarnings
instance CodeGenMonad Q where
isParametrizedType name = isParametrizedHaskellType <$> reify (toName name)
printWarnings = gqlWarnings
instance CodeGenMonad GQLResult where
isParametrizedType _ = pure False
printWarnings _ = pure ()
-- Utils: is Parametrized type
#if MIN_VERSION_template_haskell(2,17,0)
getTypeVariables :: Dec -> [TyVarBndr ()]
#else
getTypeVariables :: Dec -> [TyVarBndr]
#endif
getTypeVariables (DataD _ _ args _ _ _) = args
getTypeVariables (NewtypeD _ _ args _ _ _) = args
getTypeVariables (TySynD _ args _) = args
getTypeVariables _ = []
isParametrizedHaskellType :: Info -> Bool
isParametrizedHaskellType (TyConI x) = not $ null $ getTypeVariables x
isParametrizedHaskellType _ = False
isParametrizedResolverType :: CodeGenM m => TypeName -> [TypeDefinition ANY s] -> m Bool
isParametrizedResolverType "__TypeKind" _ = pure False
isParametrizedResolverType "Boolean" _ = pure False
isParametrizedResolverType "String" _ = pure False
isParametrizedResolverType "Int" _ = pure False
isParametrizedResolverType "Float" _ = pure False
isParametrizedResolverType name lib = case lookupWith typeName name lib of
Just x -> pure (isResolverType x)
Nothing -> isParametrizedType name
isParamResolverType :: CodeGenM m => TypeName -> m Bool
isParamResolverType typeConName =
isParametrizedResolverType typeConName =<< asks typeDefinitions
notFoundError :: MonadFail m => String -> String -> m a
notFoundError name at = fail $ "can't found " <> name <> "at " <> at <> "!"
lookupType :: CodeGenM m => TypeName -> m (Maybe (TypeDefinition ANY CONST))
lookupType name = do
types <- asks typeDefinitions
pure $ find (\t -> typeName t == name) types
lookupFieldType :: CodeGenM m => TypeName -> FieldName -> m TypeRef
lookupFieldType name fieldName = do
TypeDefinition {typeContent} <- lookupType name >>= maybe (notFoundError (show name) "type definitions") pure
case typeContent of
DataInputObject fields -> do
FieldDefinition {fieldType} <- selectOr (notFoundError (show fieldName) (show name)) pure fieldName fields
pure fieldType
_ -> notFoundError "input object" (show name)
isSubscription :: TypeKind -> Bool
isSubscription (KIND_OBJECT (Just OPERATION_SUBSCRIPTION)) = True
isSubscription _ = False
inType :: MonadReader ServerCodeGenContext m => Maybe TypeName -> m a -> m a
inType name = local (\x -> x {currentTypeName = name, currentKind = Nothing})