morpheus-graphql-server-0.27.1: src/Data/Morpheus/Server/Deriving/Utils/GRep.hs
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE NoImplicitPrelude #-}
module Data.Morpheus.Server.Deriving.Utils.GRep
( GRep (..),
RepContext (..),
ConsRep (..),
FieldRep (..),
TypeRep (..),
deriveValue,
deriveTypeWith,
isEmptyConstraint,
isUnionRef,
unpackMonad,
)
where
import Data.Morpheus.Server.Deriving.Utils.Proxy
( conNameProxy,
isRecordProxy,
selNameProxy,
)
import Data.Morpheus.Server.Types.Internal
( TypeData (..),
)
import Data.Morpheus.Types.Internal.AST
( FieldName,
TypeName,
TypeRef (..),
packName,
)
import qualified Data.Text as T
import GHC.Generics
( C,
Constructor,
D,
Datatype,
Generic (..),
K1 (..),
M1 (..),
Meta,
Rec0,
S,
Selector,
U1 (..),
(:*:) (..),
(:+:) (..),
)
import Relude hiding (undefined)
data RepContext gql fun f result = RepContext
{ optApply :: forall a. fun a => f a -> result,
optTypeData :: forall proxy a. gql a => proxy a -> TypeData
}
deriveValue ::
(Generic a, GRep gql constraint value (Rep a), gql a) =>
RepContext gql constraint Identity value ->
a ->
TypeRep value
deriveValue options value = (deriveTypeValue options (from value)) {dataTypeName}
where
dataTypeName = gqlTypeName (optTypeData options (Identity value))
deriveTypeWith ::
forall kind gql c v kinded a.
(GRep gql c v (Rep a)) =>
RepContext gql c Proxy v ->
kinded kind a ->
[ConsRep v]
deriveTypeWith options _ = deriveTypeDefinition options (Proxy @(Rep a))
-- GENERIC UNION
class GRep (gql :: Type -> Constraint) (c :: Type -> Constraint) (v :: Type) f where
deriveTypeValue :: RepContext gql c Identity v -> f a -> TypeRep v
deriveTypeDefinition :: RepContext gql c Proxy v -> proxy f -> [ConsRep v]
instance (Datatype d, GRep gql c v f) => GRep gql c v (M1 D d f) where
deriveTypeValue options (M1 src) = deriveTypeValue options src
deriveTypeDefinition options _ = deriveTypeDefinition options (Proxy @f)
-- | recursion for Object types, both of them : 'INPUT_OBJECT' and 'OBJECT'
instance (GRep gql c v a, GRep gql c v b) => GRep gql c v (a :+: b) where
deriveTypeValue f (L1 x) = (deriveTypeValue f x) {tyIsUnion = True}
deriveTypeValue f (R1 x) = (deriveTypeValue f x) {tyIsUnion = True}
deriveTypeDefinition options _ = deriveTypeDefinition options (Proxy @a) <> deriveTypeDefinition options (Proxy @b)
instance (DeriveFieldRep gql con v f, Constructor c) => GRep gql con v (M1 C c f) where
deriveTypeValue options (M1 src) =
TypeRep
{ dataTypeName = "",
tyIsUnion = False,
tyCons = deriveConsRep (Proxy @c) (toFieldRep options src)
}
deriveTypeDefinition options _ = [deriveConsRep (Proxy @c) (conRep options (Proxy @f))]
deriveConsRep ::
Constructor (c :: Meta) =>
f c ->
[FieldRep v] ->
ConsRep v
deriveConsRep proxy fields = ConsRep {..}
where
consName = conNameProxy proxy
consFields
| isRecordProxy proxy = fields
| otherwise = enumerate fields
class DeriveFieldRep (gql :: Type -> Constraint) (c :: Type -> Constraint) (v :: Type) f where
toFieldRep :: RepContext gql c Identity v -> f a -> [FieldRep v]
conRep :: RepContext gql c Proxy v -> proxy f -> [FieldRep v]
instance (DeriveFieldRep gql c v a, DeriveFieldRep gql c v b) => DeriveFieldRep gql c v (a :*: b) where
toFieldRep options (a :*: b) = toFieldRep options a <> toFieldRep options b
conRep options _ = conRep options (Proxy @a) <> conRep options (Proxy @b)
instance (Selector s, gql a, c a) => DeriveFieldRep gql c v (M1 S s (Rec0 a)) where
toFieldRep RepContext {..} (M1 (K1 src)) =
[ FieldRep
{ fieldSelector = selNameProxy (Proxy @s),
fieldTypeRef = TypeRef gqlTypeName gqlWrappers,
fieldValue = optApply (Identity src)
}
]
where
TypeData {gqlTypeName, gqlWrappers} = optTypeData (Proxy @a)
conRep RepContext {..} _ =
[ FieldRep
{ fieldSelector = selNameProxy (Proxy @s),
fieldTypeRef = TypeRef gqlTypeName gqlWrappers,
fieldValue = optApply (Proxy @a)
}
]
where
TypeData {gqlTypeName, gqlWrappers} = optTypeData (Proxy @a)
instance DeriveFieldRep gql c v U1 where
toFieldRep _ _ = []
conRep _ _ = []
data TypeRep (v :: Type) = TypeRep
{ dataTypeName :: TypeName,
tyIsUnion :: Bool,
tyCons :: ConsRep v
}
deriving (Functor)
data ConsRep (v :: Type) = ConsRep
{ consName :: TypeName,
consFields :: [FieldRep v]
}
deriving (Functor)
data FieldRep (a :: Type) = FieldRep
{ fieldSelector :: FieldName,
fieldTypeRef :: TypeRef,
fieldValue :: a
}
deriving (Functor)
-- setFieldNames :: Power Int Text -> Power { _1 :: Int, _2 :: Text }
enumerate :: [FieldRep a] -> [FieldRep a]
enumerate = zipWith setFieldName ([0 ..] :: [Int])
where
setFieldName i field = field {fieldSelector = packName $ "_" <> T.pack (show i)}
isEmptyConstraint :: ConsRep a -> Bool
isEmptyConstraint ConsRep {consFields = []} = True
isEmptyConstraint _ = False
isUnionRef :: TypeName -> ConsRep k -> Bool
isUnionRef baseName ConsRep {consName, consFields = [fieldRep]} =
consName == baseName <> typeConName (fieldTypeRef fieldRep)
isUnionRef _ _ = False
unpackMonad :: Monad m => [ConsRep (m a)] -> m [ConsRep a]
unpackMonad = traverse unpackMonadFromCons
unpackMonadFromField :: Monad m => FieldRep (m a) -> m (FieldRep a)
unpackMonadFromField FieldRep {..} = do
cont <- fieldValue
pure (FieldRep {fieldValue = cont, ..})
unpackMonadFromCons :: Monad m => ConsRep (m a) -> m (ConsRep a)
unpackMonadFromCons ConsRep {..} = ConsRep consName <$> traverse unpackMonadFromField consFields