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

module Data.Morpheus.Client.Internal.Utils
  ( removeDuplicates,
    isEnum,
    getSource,
    handleResult,
    getFile,
  )
where

import Control.Monad.Except (MonadError (catchError))
import qualified Data.ByteString.Lazy.Char8 as L
import Data.FileEmbed (makeRelativeToProject)
import Data.List (isSuffixOf)
import Data.Morpheus.Client.Internal.Types
  ( ClientConstructorDefinition (cFields),
    SchemaSource (..),
  )
import Data.Morpheus.Error (gqlWarnings, renderGQLErrors)
import Data.Morpheus.Internal.Ext (GQLResult, Result (..))
import qualified Data.Text.IO as TIO
import Language.Haskell.TH (Q, runIO)
import Language.Haskell.TH.Syntax (qAddDependentFile)
import Relude

removeDuplicates :: Eq a => [a] -> [a]
removeDuplicates :: [a] -> [a]
removeDuplicates = ([a], [a]) -> [a]
forall a b. (a, b) -> a
fst (([a], [a]) -> [a]) -> ([a] -> ([a], [a])) -> [a] -> [a]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [a] -> ([a], [a])
forall a. Eq a => [a] -> ([a], [a])
splitDuplicates

splitDuplicates :: Eq a => [a] -> ([a], [a])
splitDuplicates :: [a] -> ([a], [a])
splitDuplicates = ([a], [a]) -> [a] -> ([a], [a])
forall a. Eq a => ([a], [a]) -> [a] -> ([a], [a])
collectElems ([], [])
  where
    collectElems :: Eq a => ([a], [a]) -> [a] -> ([a], [a])
    collectElems :: ([a], [a]) -> [a] -> ([a], [a])
collectElems ([a], [a])
collected [] = ([a], [a])
collected
    collectElems ([a]
collected, [a]
errors) (a
x : [a]
xs)
      | a
x a -> [a] -> Bool
forall (f :: * -> *) a.
(Foldable f, DisallowElem f, Eq a) =>
a -> f a -> Bool
`elem` [a]
collected = ([a], [a]) -> [a] -> ([a], [a])
forall a. Eq a => ([a], [a]) -> [a] -> ([a], [a])
collectElems ([a]
collected, [a]
errors [a] -> [a] -> [a]
forall a. Semigroup a => a -> a -> a
<> [a
x]) [a]
xs
      | Bool
otherwise = ([a], [a]) -> [a] -> ([a], [a])
forall a. Eq a => ([a], [a]) -> [a] -> ([a], [a])
collectElems ([a]
collected [a] -> [a] -> [a]
forall a. Semigroup a => a -> a -> a
<> [a
x], [a]
errors) [a]
xs

isEnum :: [ClientConstructorDefinition] -> Bool
isEnum :: [ClientConstructorDefinition] -> Bool
isEnum = (ClientConstructorDefinition -> Bool)
-> [ClientConstructorDefinition] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all ([FieldDefinition ANY VALID] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null ([FieldDefinition ANY VALID] -> Bool)
-> (ClientConstructorDefinition -> [FieldDefinition ANY VALID])
-> ClientConstructorDefinition
-> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ClientConstructorDefinition -> [FieldDefinition ANY VALID]
cFields)

getSource :: FilePath -> Q SchemaSource
getSource :: FilePath -> Q SchemaSource
getSource FilePath
p
  | FilePath
".json" FilePath -> FilePath -> Bool
forall a. Eq a => [a] -> [a] -> Bool
`isSuffixOf` FilePath
p = ByteString -> SchemaSource
JSON (ByteString -> SchemaSource) -> Q ByteString -> Q SchemaSource
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> (FilePath -> IO ByteString) -> FilePath -> Q ByteString
forall a. (FilePath -> IO a) -> FilePath -> Q a
readWith FilePath -> IO ByteString
L.readFile FilePath
p
  | FilePath
".gql" FilePath -> FilePath -> Bool
forall a. Eq a => [a] -> [a] -> Bool
`isSuffixOf` FilePath
p Bool -> Bool -> Bool
|| FilePath
".graphql" FilePath -> FilePath -> Bool
forall a. Eq a => [a] -> [a] -> Bool
`isSuffixOf` FilePath
p = ByteString -> SchemaSource
GQL (ByteString -> SchemaSource) -> Q ByteString -> Q SchemaSource
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> (FilePath -> IO ByteString) -> FilePath -> Q ByteString
forall a. (FilePath -> IO a) -> FilePath -> Q a
readWith FilePath -> IO ByteString
L.readFile FilePath
p
  | Bool
otherwise = FilePath -> Q SchemaSource
forall (m :: * -> *) a. MonadFail m => FilePath -> m a
fail FilePath
"Unsupported file format! The input should have one of the following extensions: json, gql, graphql"

getFile :: FilePath -> Q Text
getFile :: FilePath -> Q Text
getFile = (FilePath -> IO Text) -> FilePath -> Q Text
forall a. (FilePath -> IO a) -> FilePath -> Q a
readWith FilePath -> IO Text
TIO.readFile

readWith :: (FilePath -> IO a) -> FilePath -> Q a
readWith :: (FilePath -> IO a) -> FilePath -> Q a
readWith FilePath -> IO a
f FilePath
path = do
  FilePath
p <- FilePath -> Q FilePath
makeRelativeToProject FilePath
path
  FilePath -> Q ()
forall (m :: * -> *). Quasi m => FilePath -> m ()
qAddDependentFile FilePath
p
  Either FilePath a
file <- IO (Either FilePath a) -> Q (Either FilePath a)
forall a. IO a -> Q a
runIO (IO (Either FilePath a)
-> (IOException -> IO (Either FilePath a))
-> IO (Either FilePath a)
forall e (m :: * -> *) a.
MonadError e m =>
m a -> (e -> m a) -> m a
catchError (((a -> Either FilePath a) -> IO a -> IO (Either FilePath a)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap a -> Either FilePath a
forall a b. b -> Either a b
Right (IO a -> IO (Either FilePath a))
-> (FilePath -> IO a) -> FilePath -> IO (Either FilePath a)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. FilePath -> IO a
f) FilePath
p) (Either FilePath a -> IO (Either FilePath a)
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Either FilePath a -> IO (Either FilePath a))
-> (IOException -> Either FilePath a)
-> IOException
-> IO (Either FilePath a)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. FilePath -> Either FilePath a
forall a b. a -> Either a b
Left (FilePath -> Either FilePath a)
-> (IOException -> FilePath) -> IOException -> Either FilePath a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. IOException -> FilePath
forall b a. (Show a, IsString b) => a -> b
show))
  case Either FilePath a
file of
    Left FilePath
x -> FilePath -> Q a
forall (m :: * -> *) a. MonadFail m => FilePath -> m a
fail FilePath
x
    Right a
x -> a -> Q a
forall (f :: * -> *) a. Applicative f => a -> f a
pure a
x

handleResult :: GQLResult t -> (t -> Q a) -> Q a
handleResult :: GQLResult t -> (t -> Q a) -> Q a
handleResult GQLResult t
x t -> Q a
f = case GQLResult t
x of
  Failure NonEmpty GQLError
errors -> FilePath -> Q a
forall (m :: * -> *) a. MonadFail m => FilePath -> m a
fail (NonEmpty GQLError -> FilePath
renderGQLErrors NonEmpty GQLError
errors)
  Success
    { t
result :: forall err a. Result err a -> a
result :: t
result,
      [GQLError]
warnings :: forall err a. Result err a -> [err]
warnings :: [GQLError]
warnings
    } -> [GQLError] -> Q ()
gqlWarnings [GQLError]
warnings Q () -> Q a -> Q a
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> t -> Q a
f t
result