morpheus-graphql-client-0.22.0: src/Data/Morpheus/Client/Internal/TH.hs
{-# LANGUAGE CPP #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TemplateHaskellQuotes #-}
{-# LANGUAGE NoImplicitPrelude #-}
module Data.Morpheus.Client.Internal.TH
( matchWith,
decodeObjectE,
mkFieldsE,
failExp,
deriveIfNotDefined,
declareIfNotDeclared,
)
where
import Data.Aeson ((.:))
import Data.Aeson.Types ((.:?))
import Data.Foldable (foldr1)
import Data.Morpheus.CodeGen.Internal.AST
( CodeGenConstructor (..),
CodeGenField (..),
CodeGenType (cgTypeName),
CodeGenTypeName (..),
getFullName,
)
import Data.Morpheus.CodeGen.TH
( toCon,
toName,
toString,
toVar,
v',
)
import Data.Morpheus.CodeGen.Utils
( camelCaseFieldName,
)
import Language.Haskell.TH
import Relude hiding (toString)
matchWith ::
Maybe (PatQ, ExpQ) ->
(t -> (PatQ, ExpQ)) ->
[t] ->
ExpQ
matchWith fbexp f xs = lamCaseE (map buildMatch xs <> fallback fbexp)
where
fallback (Just (pat, fb)) = [match pat (normalB fb) []]
fallback _ = []
buildMatch x = match pat (normalB body) []
where
(pat, body) = f x
failExp :: ExpQ
failExp =
appE
(toVar 'fail)
( uInfixE
(appE [|show|] v')
[|(<>)|]
(stringE " is Not Valid Union Constructor")
)
decodeObjectE :: CodeGenConstructor -> ExpQ
decodeObjectE CodeGenConstructor {..}
| null constructorFields = appE [|pure|] (toCon constructorName)
| otherwise =
uInfixE
(toCon constructorName)
[|(<$>)|]
(foldr1 withApplicative $ map defField constructorFields)
defField :: CodeGenField -> ExpQ
defField CodeGenField {..} = uInfixE v' (varE $ bindField fieldIsNullable) (toString fieldName)
bindField :: Bool -> Name
bindField nullable
| nullable = '(.:?)
| otherwise = '(.:)
withApplicative :: ExpQ -> ExpQ -> ExpQ
withApplicative x = uInfixE x [|(<*>)|]
-- | 'mkFieldsE'
--
-- input :
-- >>>
-- mkFieldsE 'mkValue [FieldDefinition { fieldName = \"field1" ,..} ,..]
-- >>>
--
-- expression :
-- >>>
-- [ mkValue \"field1\" field1,
-- ..
-- ]
-- >>>
mkFieldsE :: CodeGenTypeName -> Name -> [CodeGenField] -> Exp
mkFieldsE conName name = ListE . map (mkEntryWith conName name)
-- input : mkFieldWith 'mkValue (FieldDefinition { fieldName = "field1", ..})
-- expression: mkValue "field1" field1
mkEntryWith ::
CodeGenTypeName ->
Name ->
CodeGenField ->
Exp
mkEntryWith conName f CodeGenField {fieldName} =
AppE
(AppE (VarE f) (toString fieldName))
(toVar $ camelCaseFieldName (getFullName conName) fieldName)
isTypeDeclared :: CodeGenTypeName -> Q Bool
isTypeDeclared clientTypeName = do
let name = toName clientTypeName
m <- lookupTypeName (show name)
case m of
Nothing -> pure False
_ -> pure True
hasInstance :: Name -> CodeGenType -> Q Bool
hasInstance typeClass clientDef = isInstance typeClass [ConT (toName (cgTypeName clientDef))]
deriveIfNotDefined :: (CodeGenType -> Q Dec) -> Name -> CodeGenType -> Q [Dec]
deriveIfNotDefined derivation typeClass clientDef = do
exists <- isTypeDeclared (cgTypeName clientDef)
if exists
then do
has <- hasInstance typeClass clientDef
if has
then pure []
else mkDerivation
else mkDerivation
where
mkDerivation :: Q [Dec]
mkDerivation = pure <$> derivation clientDef
declareIfNotDeclared :: (CodeGenType -> a) -> CodeGenType -> Q [a]
declareIfNotDeclared f c = do
exists <- isTypeDeclared (cgTypeName c)
if exists
then pure []
else pure [f c]