{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DefaultSignatures #-}
{-# LANGUAGE DeriveLift #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeSynonymInstances #-}
module Data.Morpheus.Types.Internal.Data
( Key
, DataScalar
, DataEnum
, DataObject
, DataArgument
, DataUnion
, DataArguments
, DataField(..)
, DataTyCon(..)
, DataType(..)
, DataTypeLib(..)
, DataTypeWrapper(..)
, DataValidator(..)
, DataTypeKind(..)
, DataFingerprint(..)
, RawDataType(..)
, ResolverKind(..)
, WrapperD(..)
, TypeAlias(..)
, ArgsType(..)
, isTypeDefined
, initTypeLib
, defineType
, isFieldNullable
, allDataTypes
, lookupDataType
, kindOf
, toNullableField
, toListField
, isObject
, isInput
, toHSWrappers
, isNullable
, toGQLWrapper
, isWeaker
, isSubscription
, isOutputObject
, sysTypes
, isDefaultTypeName
, isSchemaTypeName
, isPrimitiveTypeName
, OperationType(..)
, QUERY
, MUTATION
, SUBSCRIPTION
, Name
, Description
, isEntNode
) where
import Data.Semigroup ((<>))
import qualified Data.Text as T (pack, unpack)
import GHC.Fingerprint.Type (Fingerprint)
import Language.Haskell.TH.Syntax (Lift (..))
import Data.Morpheus.Types.Internal.Base (Key)
import Data.Morpheus.Types.Internal.TH (apply, liftText, liftTextMap)
import Data.Morpheus.Types.Internal.Value (Value (..))
type Name = Key
type Description = Key
type QUERY = 'Query
type MUTATION = 'Mutation
type SUBSCRIPTION = 'Subscription
isDefaultTypeName :: Key -> Bool
isDefaultTypeName x = isSchemaTypeName x || isPrimitiveTypeName x
isSchemaTypeName :: Key -> Bool
isSchemaTypeName = (`elem` sysTypes)
isPrimitiveTypeName :: Key -> Bool
isPrimitiveTypeName = (`elem` ["String", "Float", "Int", "Boolean", "ID"])
sysTypes :: [Key]
sysTypes =
["__Schema", "__Type", "__Directive", "__TypeKind", "__Field", "__DirectiveLocation", "__InputValue", "__EnumValue"]
data OperationType
= Query
| Subscription
| Mutation
deriving (Show, Eq, Lift)
isSubscription :: DataTypeKind -> Bool
isSubscription (KindObject (Just Subscription)) = True
isSubscription _ = False
isOutputObject :: DataTypeKind -> Bool
isOutputObject (KindObject _) = True
isOutputObject _ = False
isObject :: DataTypeKind -> Bool
isObject (KindObject _) = True
isObject KindInputObject = True
isObject _ = False
isInput :: DataTypeKind -> Bool
isInput KindInputObject = True
isInput _ = False
data DataTypeKind
= KindScalar
| KindObject (Maybe OperationType)
| KindUnion
| KindEnum
| KindInputObject
| KindList
| KindNonNull
| KindInputUnion
deriving (Eq, Show, Lift)
data ResolverKind
= PlainResolver
| TypeVarResolver
| ExternalResolver
deriving (Show, Eq, Lift)
data WrapperD
= ListD
| MaybeD
deriving (Show, Lift)
isFieldNullable :: DataField -> Bool
isFieldNullable = isNullable . aliasWrappers . fieldType
isNullable :: [WrapperD] -> Bool
isNullable (MaybeD:_) = True
isNullable _ = False
isWeaker :: [WrapperD] -> [WrapperD] -> Bool
isWeaker (MaybeD:xs1) (MaybeD:xs2) = isWeaker xs1 xs2
isWeaker (MaybeD:_) _ = True
isWeaker (_:xs1) (_:xs2) = isWeaker xs1 xs2
isWeaker _ _ = False
toGQLWrapper :: [WrapperD] -> [DataTypeWrapper]
toGQLWrapper (MaybeD:(MaybeD:tw)) = toGQLWrapper (MaybeD : tw)
toGQLWrapper (MaybeD:(ListD:tw)) = ListType : toGQLWrapper tw
toGQLWrapper (ListD:tw) = [NonNullType, ListType] <> toGQLWrapper tw
toGQLWrapper [MaybeD] = []
toGQLWrapper [] = [NonNullType]
toHSWrappers :: [DataTypeWrapper] -> [WrapperD]
toHSWrappers (NonNullType:(NonNullType:xs)) = toHSWrappers (NonNullType : xs)
toHSWrappers (NonNullType:(ListType:xs)) = ListD : toHSWrappers xs
toHSWrappers (ListType:xs) = [MaybeD, ListD] <> toHSWrappers xs
toHSWrappers [] = [MaybeD]
toHSWrappers [NonNullType] = []
data DataFingerprint
= SystemFingerprint Key
| TypeableFingerprint [Fingerprint]
deriving (Show, Eq, Ord)
newtype DataValidator = DataValidator
{ validateValue :: Value -> Either Key Value
}
instance Show DataValidator where
show _ = "DataValidator"
type DataScalar = DataTyCon DataValidator
type DataEnum = DataTyCon [Key]
type DataObject = DataTyCon [(Key, DataField)]
type DataArgument = DataField
type DataUnion = DataTyCon [DataField]
type DataArguments = [(Key, DataArgument)]
data DataTypeWrapper
= ListType
| NonNullType
deriving (Show, Lift)
data TypeAlias = TypeAlias
{ aliasTyCon :: Key
, aliasArgs :: Maybe Key
, aliasWrappers :: [WrapperD]
} deriving (Show)
instance Lift TypeAlias where
lift TypeAlias {aliasTyCon = x, aliasArgs, aliasWrappers} =
[|TypeAlias {aliasTyCon = name, aliasArgs = T.pack <$> args, aliasWrappers}|]
where
name = T.unpack x
args = T.unpack <$> aliasArgs
data ArgsType = ArgsType
{ argsTypeName :: Key
, resKind :: ResolverKind
} deriving (Show)
instance Lift ArgsType where
lift (ArgsType argT kind) = apply 'ArgsType [liftText argT, lift kind]
data DataField = DataField
{ fieldName :: Key
, fieldArgs :: [(Key, DataArgument)]
, fieldArgsType :: Maybe ArgsType
, fieldType :: TypeAlias
, fieldHidden :: Bool
} deriving (Show)
instance Lift DataField where
lift (DataField name args argsT ft hid) =
apply 'DataField [liftText name, liftTextMap args, lift argsT, lift ft, lift hid]
data DataTyCon a = DataTyCon
{ typeName :: Key
, typeFingerprint :: DataFingerprint
, typeDescription :: Maybe Key
, typeData :: a
} deriving (Show)
data RawDataType
= FinalDataType DataType
| Interface DataObject
| Implements { implementsInterfaces :: [Key]
, unImplements :: DataObject }
deriving (Show)
isEntNode :: DataType -> Bool
isEntNode DataScalar {} = True
isEntNode DataEnum {} = True
isEntNode _ = False
data DataType
= DataScalar DataScalar
| DataEnum DataEnum
| DataInputObject DataObject
| DataObject DataObject
| DataUnion DataUnion
| DataInputUnion DataUnion
deriving (Show)
data DataTypeLib = DataTypeLib
{ scalar :: [(Key, DataScalar)]
, enum :: [(Key, DataEnum)]
, inputObject :: [(Key, DataObject)]
, object :: [(Key, DataObject)]
, union :: [(Key, DataUnion)]
, inputUnion :: [(Key, DataUnion)]
, query :: (Key, DataObject)
, mutation :: Maybe (Key, DataObject)
, subscription :: Maybe (Key, DataObject)
} deriving (Show)
initTypeLib :: (Key, DataObject) -> DataTypeLib
initTypeLib query =
DataTypeLib
{ scalar = []
, enum = []
, inputObject = []
, query = query
, object = []
, union = []
, inputUnion = []
, mutation = Nothing
, subscription = Nothing
}
allDataTypes :: DataTypeLib -> [(Key, DataType)]
allDataTypes DataTypeLib { scalar, enum , inputObject, object, union, inputUnion, query, mutation, subscription } =
packType DataObject query :
fromMaybeType mutation ++
fromMaybeType subscription ++
map (packType DataScalar) scalar ++
map (packType DataEnum) enum ++
map (packType DataInputObject) inputObject ++
map (packType DataInputUnion) inputUnion ++ map (packType DataObject) object ++ map (packType DataUnion) union
where
packType f (x, y) = (x, f y)
fromMaybeType :: Maybe (Key, DataObject) -> [(Key, DataType)]
fromMaybeType (Just (key', dataType')) = [(key', DataObject dataType')]
fromMaybeType Nothing = []
lookupDataType :: Key -> DataTypeLib -> Maybe DataType
lookupDataType name lib = name `lookup` allDataTypes lib
kindOf :: DataType -> DataTypeKind
kindOf (DataScalar _) = KindScalar
kindOf (DataEnum _) = KindEnum
kindOf (DataInputObject _) = KindInputObject
kindOf (DataObject _) = KindObject Nothing
kindOf (DataUnion _) = KindUnion
kindOf (DataInputUnion _) = KindInputUnion
fromDataType :: (DataTyCon () -> v) -> DataType -> v
fromDataType f (DataScalar dt) = f dt {typeData = ()}
fromDataType f (DataEnum dt) = f dt {typeData = ()}
fromDataType f (DataUnion dt) = f dt {typeData = ()}
fromDataType f (DataInputObject dt) = f dt {typeData = ()}
fromDataType f (DataInputUnion dt) = f dt {typeData = ()}
fromDataType f (DataObject dt) = f dt {typeData = ()}
isTypeDefined :: Key -> DataTypeLib -> Maybe DataFingerprint
isTypeDefined name lib = fromDataType typeFingerprint <$> lookupDataType name lib
defineType :: (Key, DataType) -> DataTypeLib -> DataTypeLib
defineType (key', DataScalar type') lib = lib {scalar = (key', type') : scalar lib}
defineType (key', DataEnum type') lib = lib {enum = (key', type') : enum lib}
defineType (key', DataInputObject type') lib = lib {inputObject = (key', type') : inputObject lib}
defineType (key', DataObject type') lib = lib {object = (key', type') : object lib}
defineType (key', DataUnion type') lib = lib {union = (key', type') : union lib}
defineType (key', DataInputUnion type') lib = lib {inputUnion = (key', type') : inputUnion lib}
toNullableField :: DataField -> DataField
toNullableField dataField
| isNullable (aliasWrappers $ fieldType dataField) = dataField
| otherwise = dataField {fieldType = nullable (fieldType dataField)}
where
nullable alias@TypeAlias {aliasWrappers} = alias {aliasWrappers = MaybeD : aliasWrappers}
toListField :: DataField -> DataField
toListField dataField = dataField {fieldType = listW (fieldType dataField)}
where
listW alias@TypeAlias {aliasWrappers} = alias {aliasWrappers = ListD : aliasWrappers}