{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE NoImplicitPrelude #-}

module Data.Morpheus.Client
  ( gql,
    Fetch (..),
    FetchError(..),
    defineQuery,
    defineByDocument,
    defineByDocumentFile,
    defineByDocumentFile',
    defineByIntrospection,
    defineByIntrospectionFile,
    defineByIntrospectionFile',
    ScalarValue (..),
    DecodeScalar (..),
    EncodeScalar (..),
    ID (..),
  )
where

import Data.ByteString.Lazy (ByteString)
import qualified Data.ByteString.Lazy as L
  ( readFile,
  )
import Data.Morpheus.Client.Build
  ( defineQuery,
  )
import Data.Morpheus.Client.Fetch
  ( Fetch (..),
  )
import Data.Morpheus.Client.JSONSchema.Parse
  ( decodeIntrospection,
  )
import Data.Morpheus.Client.Internal.Types
  ( FetchError(..),
  )
import Data.Morpheus.Core
  ( parseFullSchema,
  )
import Data.Morpheus.Internal.Ext
  ( GQLResult,
  )
import Data.Morpheus.QuasiQuoter (gql)
import Data.Morpheus.Types.GQLScalar
  ( DecodeScalar (..),
    EncodeScalar (..),
  )
import Data.Morpheus.Types.ID (ID (..))
import Data.Morpheus.Types.Internal.AST
  ( ExecutableDocument,
    ScalarValue (..),
    Schema,
    VALID,
  )
import Language.Haskell.TH
import Language.Haskell.TH.Syntax
  ( qAddDependentFile,
  )
import Relude hiding (ByteString)

defineByDocumentFile :: FilePath -> (ExecutableDocument, String) -> Q [Dec]
defineByDocumentFile :: FilePath -> (ExecutableDocument, FilePath) -> Q [Dec]
defineByDocumentFile FilePath
filePath (ExecutableDocument, FilePath)
args = do
  FilePath -> Q ()
forall (m :: * -> *). Quasi m => FilePath -> m ()
qAddDependentFile FilePath
filePath
  IO ByteString -> (ExecutableDocument, FilePath) -> Q [Dec]
defineByDocument (FilePath -> IO ByteString
L.readFile FilePath
filePath) (ExecutableDocument, FilePath)
args

-- | This variant exposes 'Q FilePath' enabling the use of TH to generate the 'FilePath'. For example, https://hackage.haskell.org/package/file-embed-0.0.13.0/docs/Data-FileEmbed.html#v:makeRelativeToProject can be used to handle multi package projects more reliably.
defineByDocumentFile' :: Q FilePath -> (ExecutableDocument, String) -> Q [Dec]
defineByDocumentFile' :: Q FilePath -> (ExecutableDocument, FilePath) -> Q [Dec]
defineByDocumentFile' Q FilePath
qFilePath (ExecutableDocument, FilePath)
args = Q FilePath
qFilePath Q FilePath -> (FilePath -> Q [Dec]) -> Q [Dec]
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= (FilePath -> (ExecutableDocument, FilePath) -> Q [Dec])
-> (ExecutableDocument, FilePath) -> FilePath -> Q [Dec]
forall a b c. (a -> b -> c) -> b -> a -> c
flip FilePath -> (ExecutableDocument, FilePath) -> Q [Dec]
defineByDocumentFile (ExecutableDocument, FilePath)
args

defineByIntrospectionFile :: FilePath -> (ExecutableDocument, String) -> Q [Dec]
defineByIntrospectionFile :: FilePath -> (ExecutableDocument, FilePath) -> Q [Dec]
defineByIntrospectionFile FilePath
filePath (ExecutableDocument, FilePath)
args = do
  FilePath -> Q ()
forall (m :: * -> *). Quasi m => FilePath -> m ()
qAddDependentFile FilePath
filePath
  IO ByteString -> (ExecutableDocument, FilePath) -> Q [Dec]
defineByIntrospection (FilePath -> IO ByteString
L.readFile FilePath
filePath) (ExecutableDocument, FilePath)
args

-- | This variant exposes 'Q FilePath' enabling the use of TH to generate the 'FilePath'. For example, https://hackage.haskell.org/package/file-embed-0.0.13.0/docs/Data-FileEmbed.html#v:makeRelativeToProject can be used to handle multi package projects more reliably.
defineByIntrospectionFile' :: Q FilePath -> (ExecutableDocument, String) -> Q [Dec]
defineByIntrospectionFile' :: Q FilePath -> (ExecutableDocument, FilePath) -> Q [Dec]
defineByIntrospectionFile' Q FilePath
qFilePath (ExecutableDocument, FilePath)
args = Q FilePath
qFilePath Q FilePath -> (FilePath -> Q [Dec]) -> Q [Dec]
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= (FilePath -> (ExecutableDocument, FilePath) -> Q [Dec])
-> (ExecutableDocument, FilePath) -> FilePath -> Q [Dec]
forall a b c. (a -> b -> c) -> b -> a -> c
flip FilePath -> (ExecutableDocument, FilePath) -> Q [Dec]
defineByIntrospectionFile (ExecutableDocument, FilePath)
args

defineByDocument :: IO ByteString -> (ExecutableDocument, String) -> Q [Dec]
defineByDocument :: IO ByteString -> (ExecutableDocument, FilePath) -> Q [Dec]
defineByDocument IO ByteString
doc = IO (GQLResult (Schema VALID))
-> (ExecutableDocument, FilePath) -> Q [Dec]
defineQuery (IO ByteString -> IO (GQLResult (Schema VALID))
schemaByDocument IO ByteString
doc)

schemaByDocument :: IO ByteString -> IO (GQLResult (Schema VALID))
schemaByDocument :: IO ByteString -> IO (GQLResult (Schema VALID))
schemaByDocument = (ByteString -> GQLResult (Schema VALID))
-> IO ByteString -> IO (GQLResult (Schema VALID))
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ByteString -> GQLResult (Schema VALID)
parseFullSchema

defineByIntrospection :: IO ByteString -> (ExecutableDocument, String) -> Q [Dec]
defineByIntrospection :: IO ByteString -> (ExecutableDocument, FilePath) -> Q [Dec]
defineByIntrospection IO ByteString
json = IO (GQLResult (Schema VALID))
-> (ExecutableDocument, FilePath) -> Q [Dec]
defineQuery (ByteString -> GQLResult (Schema VALID)
decodeIntrospection (ByteString -> GQLResult (Schema VALID))
-> IO ByteString -> IO (GQLResult (Schema VALID))
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> IO ByteString
json)