packages feed

morpheus-graphql-0.17.0: src/Data/Morpheus/Server/Internal/TH/Utils.hs

{-# LANGUAGE GADTs #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE NoImplicitPrelude #-}

module Data.Morpheus.Server.Internal.TH.Utils
  ( kindName,
    constraintTypeable,
    typeNameStringE,
    withPure,
    mkTypeableConstraints,
    m',
    m_,
    tyConArgs,
    funDProxy,
    isParametrizedResolverType,
    isSubscription,
  )
where

import Data.Morpheus.Internal.TH
  ( _',
    apply,
    funDSimple,
    toName,
    vars,
  )
import Data.Morpheus.Kind
  ( INTERFACE,
    SCALAR,
    TYPE,
    WRAPPER,
  )
import Data.Morpheus.Types.Internal.AST
  ( ANY,
    OperationType (..),
    TypeDefinition (..),
    TypeKind (..),
    TypeName (..),
    isResolverType,
    lookupWith,
  )
import Data.Text (unpack)
import Language.Haskell.TH
  ( CxtQ,
    Dec (..),
    DecQ,
    Exp (..),
    ExpQ,
    Info (..),
    Lit (..),
    Name,
    Q,
    TyVarBndr,
    Type (..),
    cxt,
    mkName,
    reify,
  )
import Relude hiding (Type)

m_ :: String
m_ = "m"

m' :: Type
m' = VarT (mkName m_)

isParametrizedResolverType :: TypeName -> [TypeDefinition ANY s] -> Q Bool
isParametrizedResolverType "__TypeKind" _ = pure False
isParametrizedResolverType "Boolean" _ = pure False
isParametrizedResolverType "String" _ = pure False
isParametrizedResolverType "Int" _ = pure False
isParametrizedResolverType "Float" _ = pure False
isParametrizedResolverType key lib = case lookupWith typeName key lib of
  Just x -> pure (isResolverType x)
  Nothing -> isParametrizedType <$> reify (toName key)

isParametrizedType :: Info -> Bool
isParametrizedType (TyConI x) = not $ null $ getTypeVariables x
isParametrizedType _ = False

getTypeVariables :: Dec -> [TyVarBndr]
getTypeVariables (DataD _ _ args _ _ _) = args
getTypeVariables (NewtypeD _ _ args _ _ _) = args
getTypeVariables (TySynD _ args _) = args
getTypeVariables _ = []

funDProxy :: [(Name, ExpQ)] -> [DecQ]
funDProxy = map fun
  where
    fun (name, body) = funDSimple name [_'] body

tyConArgs :: TypeKind -> [String]
tyConArgs kind
  | isResolverType kind = [m_]
  | otherwise = []

withPure :: Exp -> Exp
withPure = AppE (VarE 'pure)

typeNameStringE :: TypeName -> Exp
typeNameStringE = LitE . StringL . (unpack . readTypeName)

constraintTypeable :: Type -> Type
constraintTypeable name = apply ''Typeable [name]

mkTypeableConstraints :: [String] -> CxtQ
mkTypeableConstraints args = cxt $ map (pure . constraintTypeable) (vars args)

kindName :: TypeKind -> Name
kindName KindScalar = ''SCALAR
kindName KindList = ''WRAPPER
kindName KindNonNull = ''WRAPPER
kindName KindInterface = ''INTERFACE
kindName _ = ''TYPE

isSubscription :: TypeKind -> Bool
isSubscription (KindObject (Just Subscription)) = True
isSubscription _ = False