{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
module Data.Morpheus.Execution.Server.Decode
( decodeArguments
, Decode(..)
, DecodeObject(..)
)
where
import Data.Proxy ( Proxy(..) )
import Data.Semigroup ( (<>) )
import Data.Text ( pack )
import GHC.Generics
import Data.Morpheus.Error.Internal ( internalArgumentError
, internalTypeMismatch
)
import Data.Morpheus.Execution.Internal.Decode
( decodeFieldWith
, withEnum
, withList
, withMaybe
, withObject
, withUnion
)
import Data.Morpheus.Execution.Server.Generics.EnumRep
( EnumRep(..) )
import Data.Morpheus.Kind ( ENUM
, GQL_KIND
, INPUT_OBJECT
, INPUT_UNION
, SCALAR
)
import Data.Morpheus.Types.GQLScalar ( GQLScalar(..)
, toScalar
)
import Data.Morpheus.Types.GQLType ( GQLType(KIND, __typeName) )
import Data.Morpheus.Types.Internal.AST
( Key
, Argument(..)
, Arguments
, Object
, Value(..)
)
import Data.Morpheus.Types.Internal.Resolving
( Validation )
class Decode a where
decode :: Value -> Validation a
instance {-# OVERLAPPABLE #-} DecodeKind (KIND a) a => Decode a where
decode = decodeKind (Proxy @(KIND a))
instance Decode a => Decode (Maybe a) where
decode = withMaybe decode
instance Decode a => Decode [a] where
decode = withList decode
class DecodeKind (kind :: GQL_KIND) a where
decodeKind :: Proxy kind -> Value -> Validation a
instance (GQLScalar a) => DecodeKind SCALAR a where
decodeKind _ value = case toScalar value >>= parseValue of
Right scalar -> return scalar
Left errorMessage -> internalTypeMismatch errorMessage value
instance (Generic a, EnumRep (Rep a)) => DecodeKind ENUM a where
decodeKind _ = withEnum (fmap to . decodeEnum)
instance DecodeObject a => DecodeKind INPUT_OBJECT a where
decodeKind _ = withObject decodeObject
instance (Generic a, GDecode (Rep a)) => DecodeKind INPUT_UNION a where
decodeKind _ = withObject (fmap to . decodeUnion)
decodeArguments :: DecodeObject p => Arguments -> Validation p
decodeArguments = decodeObject . fmap toObject
where toObject (x, y) = (x, argumentValue y)
class DecodeObject a where
decodeObject :: Object -> Validation a
instance {-# OVERLAPPABLE #-} (Generic a, GDecode (Rep a)) => DecodeObject a where
decodeObject = fmap to . __decodeObject . Object
class GDecode f where
unionTags :: Proxy f -> [Key]
decodeUnion :: Object -> Validation (f a)
__decodeObject :: Value -> Validation (f a)
instance GDecode U1 where
unionTags _ = []
__decodeObject _ = pure U1
decodeUnion _ = pure U1
instance (Selector s, GQLType a, Decode a) => GDecode (M1 S s (K1 i a)) where
unionTags _ = [__typeName (Proxy @a)]
decodeUnion = fmap (M1 . K1) . decode . Object
__decodeObject = fmap (M1 . K1) . decodeRec
where
fieldName = pack $ selName (undefined :: M1 S s f a)
decodeRec = withObject (decodeFieldWith decode fieldName)
instance (Datatype c, GDecode f) => GDecode (M1 D c f) where
decodeUnion = fmap M1 . decodeUnion
unionTags _ = unionTags (Proxy @f)
__decodeObject = fmap M1 . __decodeObject
instance (Constructor c, GDecode f) => GDecode (M1 C c f) where
decodeUnion = fmap M1 . decodeUnion
unionTags _ = unionTags (Proxy @f)
__decodeObject = fmap M1 . __decodeObject
instance (GDecode f, GDecode g) => GDecode (f :*: g) where
__decodeObject gql = (:*:) <$> __decodeObject gql <*> __decodeObject gql
instance (GDecode a, GDecode b) => GDecode (a :+: b) where
decodeUnion = withUnion handleUnion
where
handleUnion name unions object
| [name] == l1Tags = L1 <$> decodeUnion object
| [name] == r1Tags = R1 <$> decodeUnion object
| name `elem` l1Tags = L1 <$> decodeUnion unions
| name `elem` r1Tags = R1 <$> decodeUnion unions
| otherwise = internalArgumentError
("type \"" <> name <> "\" could not find in union")
where
l1Tags = unionTags $ Proxy @a
r1Tags = unionTags $ Proxy @b
unionTags _ = unionTags (Proxy @a) ++ unionTags (Proxy @b)