{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE NoImplicitPrelude #-}

module Data.Morpheus.Client.CodeGen.Declare
  ( declareGlobalTypes,
    declareGlobalTypesByName,
    declareLocalTypes,
    declareLocalTypesInline,
    internalLegacyLocalDeclareTypes,
    clientTypeDeclarations,
    raw,
    parseClientTypeDeclarations,
  )
where

import Data.Morpheus.Client.CodeGen.AST
  ( ClientDeclaration (..),
  )
import Data.Morpheus.Client.CodeGen.Interpreting.Global
  ( toGlobalDefinitions,
  )
import Data.Morpheus.Client.CodeGen.Interpreting.Local
  ( toLocalDefinitions,
  )
import Data.Morpheus.Client.CodeGen.QuasiQuoter (raw)
import Data.Morpheus.Client.CodeGen.TH
  ( declareIfNotDeclared,
    deriveIfNotDefined,
  )
import Data.Morpheus.Client.CodeGen.Utils (getFile, getSource, handleResult)
import Data.Morpheus.Client.Fetch.Types
  ( ExecutableSource,
    SchemaSource,
  )
import Data.Morpheus.Client.Schema.Parse (parseSchema)
import Data.Morpheus.CodeGen.TH
  ( PrintDec (printDec),
  )
import Data.Morpheus.CodeGen.Utils
import Data.Morpheus.Core (parseRequest)
import Data.Morpheus.Internal.Ext (GQLResult)
import Data.Morpheus.Types.IO (GQLRequest (..))
import Data.Morpheus.Types.Internal.AST (TypeName)
import qualified Data.Set as S
import Language.Haskell.TH (Dec, Q, runIO)
import Relude

printDeclarations :: [ClientDeclaration] -> Q [Dec]
printDeclarations :: [ClientDeclaration] -> Q [Dec]
printDeclarations [ClientDeclaration]
clientType = forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall (t :: * -> *) (f :: * -> *) a b.
(Traversable t, Applicative f) =>
(a -> f b) -> t a -> f (t b)
traverse ClientDeclaration -> Q [Dec]
typeDeclarations [ClientDeclaration]
clientType

typeDeclarations :: ClientDeclaration -> Q [Dec]
typeDeclarations :: ClientDeclaration -> Q [Dec]
typeDeclarations (InstanceDeclaration DERIVING_MODE
_ TypeClassInstance ClientMethod
dec) = forall a.
(TypeClassInstance a -> Q Dec) -> TypeClassInstance a -> Q [Dec]
deriveIfNotDefined forall a. PrintDec a => a -> Q Dec
printDec TypeClassInstance ClientMethod
dec
typeDeclarations (ClientTypeDeclaration CodeGenType
c) = forall a. (CodeGenType -> Q a) -> CodeGenType -> Q [a]
declareIfNotDeclared forall a. PrintDec a => a -> Q Dec
printDec CodeGenType
c

internalLegacyLocalDeclareTypes :: IO SchemaSource -> ExecutableSource -> Q [Dec]
internalLegacyLocalDeclareTypes :: IO SchemaSource -> ExecutableSource -> Q [Dec]
internalLegacyLocalDeclareTypes IO SchemaSource
schemaSrc ExecutableSource
query = do
  SchemaSource
schemaText <- forall a. IO a -> Q a
runIO IO SchemaSource
schemaSrc
  SchemaSource -> Maybe ExecutableSource -> Q [Dec]
clientTypeDeclarations SchemaSource
schemaText (forall a. a -> Maybe a
Just ExecutableSource
query)

globalTypeDeclarations :: SchemaSource -> (TypeName -> Bool) -> Q [Dec]
globalTypeDeclarations :: SchemaSource -> (TypeName -> Bool) -> Q [Dec]
globalTypeDeclarations SchemaSource
src TypeName -> Bool
f = forall t a. GQLResult t -> (t -> Q a) -> Q a
handleResult (SchemaSource -> GQLResult (Schema VALID)
parseSchema SchemaSource
src forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap forall a b. (a, b) -> a
fst forall b c a. (b -> c) -> (a -> b) -> a -> c
. (TypeName -> Bool)
-> Schema VALID -> GQLResult ([ClientDeclaration], Flags)
toGlobalDefinitions TypeName -> Bool
f) [ClientDeclaration] -> Q [Dec]
printDeclarations

parseClientTypeDeclarations :: SchemaSource -> Maybe Text -> GQLResult ([ClientDeclaration], Flags)
parseClientTypeDeclarations :: SchemaSource
-> Maybe ExecutableSource -> GQLResult ([ClientDeclaration], Flags)
parseClientTypeDeclarations SchemaSource
schemaText (Just ExecutableSource
query) = do
  Schema VALID
schemaDoc <- SchemaSource -> GQLResult (Schema VALID)
parseSchema SchemaSource
schemaText
  ExecutableDocument
executableDoc <-
    GQLRequest -> GQLResult ExecutableDocument
parseRequest
      GQLRequest
        { ExecutableSource
query :: ExecutableSource
query :: ExecutableSource
query,
          operationName :: Maybe FieldName
operationName = forall a. Maybe a
Nothing,
          variables :: Maybe Value
variables = forall a. Maybe a
Nothing
        }
  (ExecutableSource, ExecutableDocument)
-> Schema VALID -> GQLResult ([ClientDeclaration], Flags)
toLocalDefinitions (ExecutableSource
query, ExecutableDocument
executableDoc) Schema VALID
schemaDoc
parseClientTypeDeclarations SchemaSource
src Maybe ExecutableSource
Nothing = SchemaSource -> GQLResult (Schema VALID)
parseSchema SchemaSource
src forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= (TypeName -> Bool)
-> Schema VALID -> GQLResult ([ClientDeclaration], Flags)
toGlobalDefinitions (forall a b. a -> b -> a
const Bool
True)

-- | declares global or local types, depending
-- on whether the second argument is specified or not
clientTypeDeclarations ::
  SchemaSource ->
  Maybe ExecutableSource ->
  Q [Dec]
clientTypeDeclarations :: SchemaSource -> Maybe ExecutableSource -> Q [Dec]
clientTypeDeclarations SchemaSource
src Maybe ExecutableSource
x = forall t a. GQLResult t -> (t -> Q a) -> Q a
handleResult (forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap forall a b. (a, b) -> a
fst (SchemaSource
-> Maybe ExecutableSource -> GQLResult ([ClientDeclaration], Flags)
parseClientTypeDeclarations SchemaSource
src Maybe ExecutableSource
x)) [ClientDeclaration] -> Q [Dec]
printDeclarations

{- ORMOLU_DISABLE -}
-- | declares input, enum and scalar types for specified schema
--
-- Example where the schema is defined in SDL format
--
-- @
-- 'declareGlobalTypes' "schema.gql"
-- @
--
-- Example with schema as introspection in JSON format.
--
-- @
-- 'declareGlobalTypes' "schema.json"
-- @
declareGlobalTypes ::
  FilePath  -- ^ the schema path relative to the  project location,
  -- both introspection (.json) and
  -- schema definition (.gql, .graphql) are accepted.
  -> Q [Dec]
declareGlobalTypes :: FilePath -> Q [Dec]
declareGlobalTypes = forall a b c. (a -> b -> c) -> b -> a -> c
flip FilePath -> Maybe FilePath -> Q [Dec]
declareClientTypes forall a. Maybe a
Nothing
{- ORMOLU_ENABLE -}

-- | declares global types like 'declareGlobalTypes',
-- while enabling to select only the types that are needed.
declareGlobalTypesByName :: FilePath -> [TypeName] -> Q [Dec]
declareGlobalTypesByName :: FilePath -> [TypeName] -> Q [Dec]
declareGlobalTypesByName FilePath
path [TypeName]
names = do
  SchemaSource
schema <- FilePath -> Q SchemaSource
getSource FilePath
path
  SchemaSource -> (TypeName -> Bool) -> Q [Dec]
globalTypeDeclarations SchemaSource
schema (forall a. Ord a => a -> Set a -> Bool
`S.member` forall a. Ord a => [a] -> Set a
S.fromList [TypeName]
names)

{- ORMOLU_DISABLE -}
-- | declares object, interface and union types for
-- specified schema and query.
--
-- Example where the schema is defined in SDL format
--
-- @
-- 'declareLocalTypes' "schema.gql" "query.gql"
-- @
--
-- Example with schema as introspection in JSON format.
--
-- @
-- 'declareLocalTypes' "schema.json" "query.gql"
-- @
declareLocalTypes ::
  FilePath -- ^  the schema path relative to the  project location.
  -- both introspection (`.json`) and
  -- schema definition (`.gql`, `.graphql`) are accepted.
  -> FilePath -- ^ query path relative to the  project location
  -> Q [Dec]
declareLocalTypes :: FilePath -> FilePath -> Q [Dec]
declareLocalTypes FilePath
schema FilePath
query = FilePath -> Maybe FilePath -> Q [Dec]
declareClientTypes FilePath
schema (forall a. a -> Maybe a
Just FilePath
query)
{- ORMOLU_ENABLE -}

{- ORMOLU_DISABLE -}
-- | inline version of `declareLocalTypes`, however
-- instead of specifying the file path, you can simply
-- pass the query as text using QuasiQuoter `raw`
--
-- @
-- `declareLocalTypesInline` "schema.gql"
--     [`raw`|
--        query GetUsers {
--           users {
--             name
--           }
--        }
--     ]
--  @
declareLocalTypesInline ::
  FilePath -- ^ the schema path relative to the  project location.
  -- both introspection (`.json`) and
  -- schema definition (`.gql`, `.graphql`) are accepted.
  -> ExecutableSource -- ^ inline graphql query in Text format
  -> Q [Dec]
declareLocalTypesInline :: FilePath -> ExecutableSource -> Q [Dec]
declareLocalTypesInline FilePath
schemaPath ExecutableSource
query = do
  SchemaSource
schema <- FilePath -> Q SchemaSource
getSource FilePath
schemaPath
  SchemaSource -> Maybe ExecutableSource -> Q [Dec]
clientTypeDeclarations SchemaSource
schema (forall a. a -> Maybe a
Just ExecutableSource
query)
{- ORMOLU_ENABLE -}

declareClientTypes ::
  FilePath ->
  Maybe FilePath ->
  Q [Dec]
declareClientTypes :: FilePath -> Maybe FilePath -> Q [Dec]
declareClientTypes FilePath
schemaPath Maybe FilePath
queryPath = do
  SchemaSource
schema <- FilePath -> Q SchemaSource
getSource FilePath
schemaPath
  Maybe ExecutableSource
query <- forall (t :: * -> *) (f :: * -> *) a b.
(Traversable t, Applicative f) =>
(a -> f b) -> t a -> f (t b)
traverse FilePath -> Q ExecutableSource
getFile Maybe FilePath
queryPath
  SchemaSource -> Maybe ExecutableSource -> Q [Dec]
clientTypeDeclarations SchemaSource
schema Maybe ExecutableSource
query