{- This Source Code Form is subject to the terms of the Mozilla Public License,
   v. 2.0. If a copy of the MPL was not distributed with this file, You can
   obtain one at https://mozilla.org/MPL/2.0/. -}

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE FlexibleInstances #-}

-- | ToGraphQL and FromGraphQL typeclasses used for user-defined type
-- conversion.
module Language.GraphQL.Class
    ( FromGraphQL(..)
    , ToGraphQL(..)
    , deriveFromGraphQL
    , deriveToGraphQL
    , gql
    ) where

import Data.Int (Int8, Int16, Int32, Int64)
import Data.Text (Text)
import Data.Word (Word8, Word16, Word32, Word64)
import qualified Data.Text.Read as Text.Read
import Data.Vector (Vector)
import qualified Data.Vector as Vector
import Data.Scientific (Scientific, toRealFloat)
import qualified Data.Text as Text
import Data.Time
    ( Day
    , DiffTime
    , LocalTime(..)
    , NominalDiffTime
    , TimeOfDay(..)
    , UTCTime(..)
    , showGregorian
    , secondsToNominalDiffTime
    , secondsToDiffTime
    )
import Data.Time.Format.ISO8601
    ( ISO8601(..)
    , formatParseM
    , iso8601Format
    , iso8601Show
    )
import Language.Haskell.TH
    ( Con(..)
    , Dec(..)
    , Exp(..)
    , Info(..)
    , Lit(..)
    , Quote(..)
    , Name
    , Q
    , VarBangType
    , appT
    , conP
    , conT
    , instanceD
    , recP
    , reify
    , nameBase
    , listE
    , stringL
    , tupE
    , litE
    , varE
    , varP
    , funD
    , clause
    , normalB
    , appE
    , mkName
    , conE
    , integerL
    , litP
    , wildP
    )
import Language.Haskell.TH.Quote (QuasiQuoter(..))
import Data.Foldable (Foldable(..))
import qualified Data.HashMap.Strict as HashMap
import qualified Language.GraphQL.Type as Type
import Prelude hiding (id)

fromGraphQLToIntegral :: Integral a => Type.Value -> Maybe a
fromGraphQLToIntegral :: forall a. Integral a => Value -> Maybe a
fromGraphQLToIntegral (Type.Int Int32
value) = a -> Maybe a
forall a. a -> Maybe a
Just (a -> Maybe a) -> a -> Maybe a
forall a b. (a -> b) -> a -> b
$ Int32 -> a
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int32
value
fromGraphQLToIntegral (Type.String Text
value) =
    case Reader a
forall a. Integral a => Reader a
Text.Read.decimal Text
value of
        Right (a
converted, Text
"") -> a -> Maybe a
forall a. a -> Maybe a
Just a
converted
        Either String (a, Text)
_conversionError -> Maybe a
forall a. Maybe a
Nothing
fromGraphQLToIntegral Value
_ = Maybe a
forall a. Maybe a
Nothing

iso8601ToGraphQL :: ISO8601 t => t -> Type.Value
iso8601ToGraphQL :: forall t. ISO8601 t => t -> Value
iso8601ToGraphQL = Text -> Value
Type.String (Text -> Value) -> (t -> Text) -> t -> Value
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Text
Text.pack (String -> Text) -> (t -> String) -> t -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. t -> String
forall t. ISO8601 t => t -> String
iso8601Show

fromGraphQLToISO8601 :: ISO8601 t => Type.Value -> Maybe t
fromGraphQLToISO8601 :: forall t. ISO8601 t => Value -> Maybe t
fromGraphQLToISO8601 (Type.String Text
value') = Format t -> String -> Maybe t
forall (m :: * -> *) t. MonadFail m => Format t -> String -> m t
formatParseM Format t
forall t. ISO8601 t => Format t
iso8601Format (String -> Maybe t) -> String -> Maybe t
forall a b. (a -> b) -> a -> b
$ Text -> String
Text.unpack Text
value'
fromGraphQLToISO8601 Value
_ = Maybe t
forall a. Maybe a
Nothing

-- | Instances of this typeclass can be converted to GraphQL internal
-- representation.
class ToGraphQL a
  where
    toGraphQL :: a -> Type.Value

instance ToGraphQL Type.Value
  where
    toGraphQL :: Value -> Value
toGraphQL Value
a = Value
a

instance ToGraphQL Text
  where
    toGraphQL :: Text -> Value
toGraphQL = Text -> Value
Type.String

instance ToGraphQL Int
  where
    toGraphQL :: Int -> Value
toGraphQL = Int32 -> Value
Type.Int (Int32 -> Value) -> (Int -> Int32) -> Int -> Value
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> Int32
forall a b. (Integral a, Num b) => a -> b
fromIntegral

instance ToGraphQL Int8
  where
    toGraphQL :: Int8 -> Value
toGraphQL = Int32 -> Value
Type.Int (Int32 -> Value) -> (Int8 -> Int32) -> Int8 -> Value
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int8 -> Int32
forall a b. (Integral a, Num b) => a -> b
fromIntegral

instance ToGraphQL Int16
  where
    toGraphQL :: Int16 -> Value
toGraphQL = Int32 -> Value
Type.Int (Int32 -> Value) -> (Int16 -> Int32) -> Int16 -> Value
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int16 -> Int32
forall a b. (Integral a, Num b) => a -> b
fromIntegral

instance ToGraphQL Int32
  where
    toGraphQL :: Int32 -> Value
toGraphQL = Int32 -> Value
Type.Int

instance ToGraphQL Int64
  where
    toGraphQL :: Int64 -> Value
toGraphQL = Int32 -> Value
Type.Int (Int32 -> Value) -> (Int64 -> Int32) -> Int64 -> Value
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int64 -> Int32
forall a b. (Integral a, Num b) => a -> b
fromIntegral

instance ToGraphQL Word
  where
    toGraphQL :: Word -> Value
toGraphQL = Int32 -> Value
Type.Int (Int32 -> Value) -> (Word -> Int32) -> Word -> Value
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Word -> Int32
forall a b. (Integral a, Num b) => a -> b
fromIntegral

instance ToGraphQL Word8
  where
    toGraphQL :: Word8 -> Value
toGraphQL = Int32 -> Value
Type.Int (Int32 -> Value) -> (Word8 -> Int32) -> Word8 -> Value
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Word8 -> Int32
forall a b. (Integral a, Num b) => a -> b
fromIntegral

instance ToGraphQL Word16
  where
    toGraphQL :: Word16 -> Value
toGraphQL = Int32 -> Value
Type.Int (Int32 -> Value) -> (Word16 -> Int32) -> Word16 -> Value
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Word16 -> Int32
forall a b. (Integral a, Num b) => a -> b
fromIntegral

instance ToGraphQL Word32
  where
    toGraphQL :: Word32 -> Value
toGraphQL = Int32 -> Value
Type.Int (Int32 -> Value) -> (Word32 -> Int32) -> Word32 -> Value
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Word32 -> Int32
forall a b. (Integral a, Num b) => a -> b
fromIntegral

instance ToGraphQL Word64
  where
    toGraphQL :: Word64 -> Value
toGraphQL = Int32 -> Value
Type.Int (Int32 -> Value) -> (Word64 -> Int32) -> Word64 -> Value
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Word64 -> Int32
forall a b. (Integral a, Num b) => a -> b
fromIntegral

instance ToGraphQL a => ToGraphQL [a]
  where
    toGraphQL :: [a] -> Value
toGraphQL = [Value] -> Value
Type.List ([Value] -> Value) -> ([a] -> [Value]) -> [a] -> Value
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (a -> Value) -> [a] -> [Value]
forall a b. (a -> b) -> [a] -> [b]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap a -> Value
forall a. ToGraphQL a => a -> Value
toGraphQL

instance ToGraphQL a => ToGraphQL (Vector a)
  where
    toGraphQL :: Vector a -> Value
toGraphQL = [Value] -> Value
Type.List ([Value] -> Value) -> (Vector a -> [Value]) -> Vector a -> Value
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Vector Value -> [Value]
forall a. Vector a -> [a]
forall (t :: * -> *) a. Foldable t => t a -> [a]
toList (Vector Value -> [Value])
-> (Vector a -> Vector Value) -> Vector a -> [Value]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (a -> Value) -> Vector a -> Vector Value
forall a b. (a -> b) -> Vector a -> Vector b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap a -> Value
forall a. ToGraphQL a => a -> Value
toGraphQL

instance ToGraphQL a => ToGraphQL (Maybe a)
  where
    toGraphQL :: Maybe a -> Value
toGraphQL (Just a
justValue) = a -> Value
forall a. ToGraphQL a => a -> Value
toGraphQL a
justValue
    toGraphQL Maybe a
Nothing = Value
Type.Null

instance ToGraphQL Bool
  where
    toGraphQL :: Bool -> Value
toGraphQL = Bool -> Value
Type.Boolean

instance ToGraphQL Float
  where
    toGraphQL :: Float -> Value
toGraphQL = Double -> Value
Type.Float (Double -> Value) -> (Float -> Double) -> Float -> Value
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Float -> Double
forall a b. (Real a, Fractional b) => a -> b
realToFrac

instance ToGraphQL Double
  where
    toGraphQL :: Double -> Value
toGraphQL = Double -> Value
Type.Float

instance ToGraphQL Scientific
  where
    toGraphQL :: Scientific -> Value
toGraphQL = Double -> Value
Type.Float (Double -> Value) -> (Scientific -> Double) -> Scientific -> Value
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Scientific -> Double
forall a. RealFloat a => Scientific -> a
toRealFloat

instance ToGraphQL Day
  where
    toGraphQL :: Day -> Value
toGraphQL = Text -> Value
Type.String (Text -> Value) -> (Day -> Text) -> Day -> Value
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Text
Text.pack (String -> Text) -> (Day -> String) -> Day -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Day -> String
showGregorian

instance ToGraphQL DiffTime
  where
    toGraphQL :: DiffTime -> Value
toGraphQL = Int32 -> Value
Type.Int (Int32 -> Value) -> (DiffTime -> Int32) -> DiffTime -> Value
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Double -> Int32
forall b. Integral b => Double -> b
forall a b. (RealFrac a, Integral b) => a -> b
truncate (Double -> Int32) -> (DiffTime -> Double) -> DiffTime -> Int32
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (DiffTime -> Double
forall a b. (Real a, Fractional b) => a -> b
realToFrac :: DiffTime -> Double)

instance ToGraphQL NominalDiffTime
  where
    toGraphQL :: NominalDiffTime -> Value
toGraphQL = Int32 -> Value
Type.Int (Int32 -> Value)
-> (NominalDiffTime -> Int32) -> NominalDiffTime -> Value
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Double -> Int32
forall b. Integral b => Double -> b
forall a b. (RealFrac a, Integral b) => a -> b
truncate (Double -> Int32)
-> (NominalDiffTime -> Double) -> NominalDiffTime -> Int32
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (NominalDiffTime -> Double
forall a b. (Real a, Fractional b) => a -> b
realToFrac :: NominalDiffTime -> Double)

instance ToGraphQL UTCTime
  where
    toGraphQL :: UTCTime -> Value
toGraphQL = UTCTime -> Value
forall t. ISO8601 t => t -> Value
iso8601ToGraphQL

instance ToGraphQL TimeOfDay
  where
    toGraphQL :: TimeOfDay -> Value
toGraphQL = TimeOfDay -> Value
forall t. ISO8601 t => t -> Value
iso8601ToGraphQL

instance ToGraphQL LocalTime
  where
    toGraphQL :: LocalTime -> Value
toGraphQL = LocalTime -> Value
forall t. ISO8601 t => t -> Value
iso8601ToGraphQL

instance ToGraphQL a => ToGraphQL (HashMap.HashMap Text a)
  where
    toGraphQL :: HashMap Text a -> Value
toGraphQL = HashMap Text Value -> Value
Type.Object (HashMap Text Value -> Value)
-> (HashMap Text a -> HashMap Text Value)
-> HashMap Text a
-> Value
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (a -> Value) -> HashMap Text a -> HashMap Text Value
forall a b. (a -> b) -> HashMap Text a -> HashMap Text b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap a -> Value
forall a. ToGraphQL a => a -> Value
toGraphQL

-- | Instances of this typeclass can be used to convert GraphQL internal
-- representation to user-defined type.
class FromGraphQL a
  where
    fromGraphQL :: Type.Value -> Maybe a

instance FromGraphQL Type.Value
  where
    fromGraphQL :: Value -> Maybe Value
fromGraphQL = Value -> Maybe Value
forall a. a -> Maybe a
Just

instance FromGraphQL Text
  where
    fromGraphQL :: Value -> Maybe Text
fromGraphQL (Type.String Text
value) = Text -> Maybe Text
forall a. a -> Maybe a
Just Text
value
    fromGraphQL Value
_ = Maybe Text
forall a. Maybe a
Nothing

instance FromGraphQL Int
  where
    fromGraphQL :: Value -> Maybe Int
fromGraphQL = Value -> Maybe Int
forall a. Integral a => Value -> Maybe a
fromGraphQLToIntegral

instance FromGraphQL Int8
  where
    fromGraphQL :: Value -> Maybe Int8
fromGraphQL = Value -> Maybe Int8
forall a. Integral a => Value -> Maybe a
fromGraphQLToIntegral

instance FromGraphQL Int16
  where
    fromGraphQL :: Value -> Maybe Int16
fromGraphQL = Value -> Maybe Int16
forall a. Integral a => Value -> Maybe a
fromGraphQLToIntegral

instance FromGraphQL Int32
  where
    fromGraphQL :: Value -> Maybe Int32
fromGraphQL = Value -> Maybe Int32
forall a. Integral a => Value -> Maybe a
fromGraphQLToIntegral

instance FromGraphQL Int64
  where
    fromGraphQL :: Value -> Maybe Int64
fromGraphQL = Value -> Maybe Int64
forall a. Integral a => Value -> Maybe a
fromGraphQLToIntegral

instance FromGraphQL Word
  where
    fromGraphQL :: Value -> Maybe Word
fromGraphQL = Value -> Maybe Word
forall a. Integral a => Value -> Maybe a
fromGraphQLToIntegral

instance FromGraphQL Word8
  where
    fromGraphQL :: Value -> Maybe Word8
fromGraphQL = Value -> Maybe Word8
forall a. Integral a => Value -> Maybe a
fromGraphQLToIntegral

instance FromGraphQL Word16
  where
    fromGraphQL :: Value -> Maybe Word16
fromGraphQL = Value -> Maybe Word16
forall a. Integral a => Value -> Maybe a
fromGraphQLToIntegral

instance FromGraphQL Word32
  where
    fromGraphQL :: Value -> Maybe Word32
fromGraphQL = Value -> Maybe Word32
forall a. Integral a => Value -> Maybe a
fromGraphQLToIntegral

instance FromGraphQL Word64
  where
    fromGraphQL :: Value -> Maybe Word64
fromGraphQL = Value -> Maybe Word64
forall a. Integral a => Value -> Maybe a
fromGraphQLToIntegral

instance FromGraphQL a => FromGraphQL [a]
  where
    fromGraphQL :: Value -> Maybe [a]
fromGraphQL (Type.List [Value]
value) = (Value -> Maybe a) -> [Value] -> Maybe [a]
forall (t :: * -> *) (f :: * -> *) a b.
(Traversable t, Applicative f) =>
(a -> f b) -> t a -> f (t b)
forall (f :: * -> *) a b.
Applicative f =>
(a -> f b) -> [a] -> f [b]
traverse Value -> Maybe a
forall a. FromGraphQL a => Value -> Maybe a
fromGraphQL [Value]
value
    fromGraphQL Value
_ = Maybe [a]
forall a. Maybe a
Nothing

instance FromGraphQL a => FromGraphQL (Vector a)
  where
    fromGraphQL :: Value -> Maybe (Vector a)
fromGraphQL (Type.List [Value]
value) = [a] -> Vector a
forall a. [a] -> Vector a
Vector.fromList
        ([a] -> Vector a) -> Maybe [a] -> Maybe (Vector a)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> (Value -> Maybe a) -> [Value] -> Maybe [a]
forall (t :: * -> *) (f :: * -> *) a b.
(Traversable t, Applicative f) =>
(a -> f b) -> t a -> f (t b)
forall (f :: * -> *) a b.
Applicative f =>
(a -> f b) -> [a] -> f [b]
traverse Value -> Maybe a
forall a. FromGraphQL a => Value -> Maybe a
fromGraphQL [Value]
value
    fromGraphQL Value
_ = Maybe (Vector a)
forall a. Maybe a
Nothing

instance FromGraphQL a => FromGraphQL (Maybe a)
  where
    fromGraphQL :: Value -> Maybe (Maybe a)
fromGraphQL Value
Type.Null = Maybe a -> Maybe (Maybe a)
forall a. a -> Maybe a
Just Maybe a
forall a. Maybe a
Nothing
    fromGraphQL Value
value = a -> Maybe a
forall a. a -> Maybe a
Just (a -> Maybe a) -> Maybe a -> Maybe (Maybe a)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Value -> Maybe a
forall a. FromGraphQL a => Value -> Maybe a
fromGraphQL Value
value

instance FromGraphQL Bool
  where
    fromGraphQL :: Value -> Maybe Bool
fromGraphQL (Type.Boolean Bool
value) = Bool -> Maybe Bool
forall a. a -> Maybe a
Just Bool
value
    fromGraphQL Value
_ = Maybe Bool
forall a. Maybe a
Nothing

instance FromGraphQL Float
  where
    fromGraphQL :: Value -> Maybe Float
fromGraphQL (Type.Float Double
value) = Float -> Maybe Float
forall a. a -> Maybe a
Just (Float -> Maybe Float) -> Float -> Maybe Float
forall a b. (a -> b) -> a -> b
$ Double -> Float
forall a b. (Real a, Fractional b) => a -> b
realToFrac Double
value
    fromGraphQL Value
_ = Maybe Float
forall a. Maybe a
Nothing

instance FromGraphQL Double
  where
    fromGraphQL :: Value -> Maybe Double
fromGraphQL (Type.Float Double
value) = Double -> Maybe Double
forall a. a -> Maybe a
Just Double
value
    fromGraphQL Value
_ = Maybe Double
forall a. Maybe a
Nothing

instance FromGraphQL Scientific
  where
    fromGraphQL :: Value -> Maybe Scientific
fromGraphQL (Type.Float Double
value) = Scientific -> Maybe Scientific
forall a. a -> Maybe a
Just (Scientific -> Maybe Scientific) -> Scientific -> Maybe Scientific
forall a b. (a -> b) -> a -> b
$ Double -> Scientific
forall a b. (Real a, Fractional b) => a -> b
realToFrac Double
value
    fromGraphQL Value
_ = Maybe Scientific
forall a. Maybe a
Nothing

instance FromGraphQL Day
  where
    fromGraphQL :: Value -> Maybe Day
fromGraphQL = Value -> Maybe Day
forall t. ISO8601 t => Value -> Maybe t
fromGraphQLToISO8601

instance FromGraphQL DiffTime
  where
    fromGraphQL :: Value -> Maybe DiffTime
fromGraphQL (Type.Int Int32
value') = DiffTime -> Maybe DiffTime
forall a. a -> Maybe a
Just (DiffTime -> Maybe DiffTime) -> DiffTime -> Maybe DiffTime
forall a b. (a -> b) -> a -> b
$ Integer -> DiffTime
secondsToDiffTime (Integer -> DiffTime) -> Integer -> DiffTime
forall a b. (a -> b) -> a -> b
$ Int32 -> Integer
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int32
value'
    fromGraphQL Value
_ = Maybe DiffTime
forall a. Maybe a
Nothing

instance FromGraphQL NominalDiffTime
  where
    fromGraphQL :: Value -> Maybe NominalDiffTime
fromGraphQL (Type.Int Int32
value') = NominalDiffTime -> Maybe NominalDiffTime
forall a. a -> Maybe a
Just (NominalDiffTime -> Maybe NominalDiffTime)
-> NominalDiffTime -> Maybe NominalDiffTime
forall a b. (a -> b) -> a -> b
$ Pico -> NominalDiffTime
secondsToNominalDiffTime (Pico -> NominalDiffTime) -> Pico -> NominalDiffTime
forall a b. (a -> b) -> a -> b
$ Int32 -> Pico
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int32
value'
    fromGraphQL Value
_ = Maybe NominalDiffTime
forall a. Maybe a
Nothing

instance FromGraphQL UTCTime
  where
    fromGraphQL :: Value -> Maybe UTCTime
fromGraphQL = Value -> Maybe UTCTime
forall t. ISO8601 t => Value -> Maybe t
fromGraphQLToISO8601

instance FromGraphQL TimeOfDay
  where
    fromGraphQL :: Value -> Maybe TimeOfDay
fromGraphQL = Value -> Maybe TimeOfDay
forall t. ISO8601 t => Value -> Maybe t
fromGraphQLToISO8601

instance FromGraphQL LocalTime
  where
    fromGraphQL :: Value -> Maybe LocalTime
fromGraphQL = Value -> Maybe LocalTime
forall t. ISO8601 t => Value -> Maybe t
fromGraphQLToISO8601

instance FromGraphQL a => FromGraphQL (HashMap.HashMap Text a)
  where
    fromGraphQL :: Value -> Maybe (HashMap Text a)
fromGraphQL (Type.Object HashMap Text Value
hm) = (Value -> Maybe a) -> HashMap Text Value -> Maybe (HashMap Text a)
forall (t :: * -> *) (f :: * -> *) a b.
(Traversable t, Applicative f) =>
(a -> f b) -> t a -> f (t b)
forall (f :: * -> *) a b.
Applicative f =>
(a -> f b) -> HashMap Text a -> f (HashMap Text b)
traverse Value -> Maybe a
forall a. FromGraphQL a => Value -> Maybe a
fromGraphQL HashMap Text Value
hm
    fromGraphQL Value
_ = Maybe (HashMap Text a)
forall a. Maybe a
Nothing

stringLE :: Name -> Q Exp
stringLE :: Name -> Q Exp
stringLE = Lit -> Q Exp
forall (m :: * -> *). Quote m => Lit -> m Exp
litE (Lit -> Q Exp) -> (Name -> Lit) -> Name -> Q Exp
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Lit
stringL (String -> Lit) -> (Name -> String) -> Name -> Lit
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Name -> String
nameBase

-- | Given a type derives a 'FromGraphQL' instance for it.
--
-- The derivation can only work when all nested types already have 'FromGraphQL'
-- instances.
--
-- The following cases are supported:
--
-- * Records encode input objects.
-- * Sum types with all data constructors without parameters encode Enums.
deriveFromGraphQL :: Name -> Q [Dec]
deriveFromGraphQL :: Name -> Q [Dec]
deriveFromGraphQL Name
typeName = do
    TyConI Dec
plainConstructor <- Name -> Q Info
reify Name
typeName
    case Dec
plainConstructor of
        DataD Cxt
_ Name
_ [TyVarBndr ()]
_ Maybe Kind
_ [Con
cons'] [DerivClause]
_
            | RecC Name
dataConName [VarBangType]
varBangTypes <- Con
cons' ->
                Name -> [VarBangType] -> Q [Dec]
forall {b} {c}. Name -> [(Name, b, c)] -> Q [Dec]
withRecordConstructor Name
dataConName [VarBangType]
varBangTypes
        DataD Cxt
_ Name
_ [TyVarBndr ()]
_ Maybe Kind
_ [Con]
cons' [DerivClause]
_ -> Dec -> [Dec]
forall a. a -> [a]
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Dec -> [Dec]) -> Q Dec -> Q [Dec]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [Con] -> Q Dec
generateEnumInstance [Con]
cons'
        NewtypeD Cxt
_ Name
_ [TyVarBndr ()]
_ Maybe Kind
_ Con
cons' [DerivClause]
_
            | RecC Name
dataConName [VarBangType]
varBangTypes <- Con
cons' ->
                Name -> [VarBangType] -> Q [Dec]
forall {b} {c}. Name -> [(Name, b, c)] -> Q [Dec]
withRecordConstructor Name
dataConName [VarBangType]
varBangTypes
        Dec
_ -> String -> Q [Dec]
forall a. HasCallStack => String -> a
error String
"Only input objects and enums are supported if all member types have a FromGraphQL instance"
  where
    enumMemberPattern :: Con -> m Clause
enumMemberPattern (NormalC Name
normalName []) =
        let fromGraphQLF :: m Pat
fromGraphQLF = Name -> [m Pat] -> m Pat
forall (m :: * -> *). Quote m => Name -> [m Pat] -> m Pat
conP (String -> Name
mkName String
"Type.Enum") [Lit -> m Pat
forall (m :: * -> *). Quote m => Lit -> m Pat
litP (Lit -> m Pat) -> Lit -> m Pat
forall a b. (a -> b) -> a -> b
$ String -> Lit
stringL (String -> Lit) -> String -> Lit
forall a b. (a -> b) -> a -> b
$ Name -> String
nameBase Name
normalName]
         in (m Body -> [m Dec] -> m Clause) -> [m Dec] -> m Body -> m Clause
forall a b c. (a -> b -> c) -> b -> a -> c
flip ([m Pat] -> m Body -> [m Dec] -> m Clause
forall (m :: * -> *).
Quote m =>
[m Pat] -> m Body -> [m Dec] -> m Clause
clause [m Pat
fromGraphQLF]) []
            (m Body -> m Clause) -> m Body -> m Clause
forall a b. (a -> b) -> a -> b
$ m Exp -> m Body
forall (m :: * -> *). Quote m => m Exp -> m Body
normalB [|Just $(Name -> m Exp
forall (m :: * -> *). Quote m => Name -> m Exp
conE Name
normalName)|]
    enumMemberPattern Con
_ =
        String -> m Clause
forall a. HasCallStack => String -> a
error String
"Enum member should be a normal constructor without parameters"
    generateEnumInstance :: [Con] -> Q Dec
    generateEnumInstance :: [Con] -> Q Dec
generateEnumInstance [Con]
cons'
        = Q Cxt -> Q Kind -> [Q Dec] -> Q Dec
forall (m :: * -> *).
Quote m =>
m Cxt -> m Kind -> [m Dec] -> m Dec
instanceD Q Cxt
forall a. Monoid a => a
mempty (Q Kind -> Q Kind -> Q Kind
forall (m :: * -> *). Quote m => m Kind -> m Kind -> m Kind
appT (Name -> Q Kind
forall (m :: * -> *). Quote m => Name -> m Kind
conT ''FromGraphQL) Q Kind
conTName)
        ([Q Dec] -> Q Dec) -> [Q Dec] -> Q Dec
forall a b. (a -> b) -> a -> b
$ Q Dec -> [Q Dec]
forall a. a -> [a]
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Q Dec -> [Q Dec]) -> Q Dec -> [Q Dec]
forall a b. (a -> b) -> a -> b
$ Name -> [Q Clause] -> Q Dec
forall (m :: * -> *). Quote m => Name -> [m Clause] -> m Dec
funD 'fromGraphQL
        ([Q Clause] -> Q Dec) -> [Q Clause] -> Q Dec
forall a b. (a -> b) -> a -> b
$ (Con -> Q Clause
forall {m :: * -> *}. Quote m => Con -> m Clause
enumMemberPattern (Con -> Q Clause) -> [Con] -> [Q Clause]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [Con]
cons')
        [Q Clause] -> [Q Clause] -> [Q Clause]
forall a. Semigroup a => a -> a -> a
<> [[Q Pat] -> Q Body -> [Q Dec] -> Q Clause
forall (m :: * -> *).
Quote m =>
[m Pat] -> m Body -> [m Dec] -> m Clause
clause [Q Pat
forall (m :: * -> *). Quote m => m Pat
wildP] (Q Exp -> Q Body
forall (m :: * -> *). Quote m => m Exp -> m Body
normalB [|Nothing|]) []]
    hashMapLookup :: Name -> Q Exp -> Q Exp
hashMapLookup Name
fieldName Q Exp
objectName =
        [|HashMap.lookup $(Name -> Q Exp
stringLE Name
fieldName) $Q Exp
objectName >>= fromGraphQL|]
    addRecordField :: Q Exp -> Q Exp -> (Name, b, c) -> Q Exp
addRecordField Q Exp
objectName Q Exp
accumulator (Name
name', b
_, c
_)
        = Q Exp -> Q Exp -> Q Exp
forall (m :: * -> *). Quote m => m Exp -> m Exp -> m Exp
appE (Q Exp -> Q Exp -> Q Exp
forall (m :: * -> *). Quote m => m Exp -> m Exp -> m Exp
appE (Name -> Q Exp
forall (m :: * -> *). Quote m => Name -> m Exp
varE (Name -> Q Exp) -> Name -> Q Exp
forall a b. (a -> b) -> a -> b
$ String -> Name
mkName String
"<*>") Q Exp
accumulator)
        (Q Exp -> Q Exp) -> Q Exp -> Q Exp
forall a b. (a -> b) -> a -> b
$ Name -> Q Exp -> Q Exp
hashMapLookup Name
name' Q Exp
objectName
    withRecordConstructor :: Name -> [(Name, b, c)] -> Q [Dec]
withRecordConstructor Name
dataConName [(Name, b, c)]
varBangTypes = do
        Name
valueName <- String -> Q Name
forall (m :: * -> *). Quote m => String -> m Name
newName String
"value"
        let objectName :: Q Exp
objectName = Name -> Q Exp
forall (m :: * -> *). Quote m => Name -> m Exp
varE Name
valueName
            toGraphQLF :: Q Pat
toGraphQLF = Name -> [Q Pat] -> Q Pat
forall (m :: * -> *). Quote m => Name -> [m Pat] -> m Pat
conP (String -> Name
mkName String
"Type.Object") [Name -> Q Pat
forall (m :: * -> *). Quote m => Name -> m Pat
varP Name
valueName]
            fBody :: Q Exp
fBody = Q Exp -> Q Exp -> [(Name, b, c)] -> Q Exp
forall {b} {c}. Q Exp -> Q Exp -> [(Name, b, c)] -> Q Exp
makeRecordBody (Name -> Q Exp
forall (m :: * -> *). Quote m => Name -> m Exp
conE Name
dataConName) Q Exp
objectName [(Name, b, c)]
varBangTypes
            recordSize :: Q Exp
recordSize = Lit -> Q Exp
forall (m :: * -> *). Quote m => Lit -> m Exp
litE (Lit -> Q Exp) -> Lit -> Q Exp
forall a b. (a -> b) -> a -> b
$ Integer -> Lit
integerL (Integer -> Lit) -> Integer -> Lit
forall a b. (a -> b) -> a -> b
$ Int -> Integer
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Int -> Integer) -> Int -> Integer
forall a b. (a -> b) -> a -> b
$ [(Name, b, c)] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [(Name, b, c)]
varBangTypes
        [d|
            instance FromGraphQL $Q Kind
conTName
              where
                fromGraphQL $Q Pat
toGraphQLF
                    | HashMap.size $Q Exp
objectName == $Q Exp
recordSize = $Q Exp
fBody
                    | otherwise = Nothing
                fromGraphQL _ = Nothing
         |]
    makeRecordBody :: Q Exp -> Q Exp -> [(Name, b, c)] -> Q Exp
makeRecordBody Q Exp
dataConE Q Exp
objectName ((Name
headName, b
_, c
_) : [(Name, b, c)]
varBangTypes') =
        let initialExpression :: Q Exp
initialExpression = Q Exp -> Q Exp -> Q Exp
forall (m :: * -> *). Quote m => m Exp -> m Exp -> m Exp
appE (Q Exp -> Q Exp -> Q Exp
forall (m :: * -> *). Quote m => m Exp -> m Exp -> m Exp
appE (Name -> Q Exp
forall (m :: * -> *). Quote m => Name -> m Exp
varE (Name -> Q Exp) -> Name -> Q Exp
forall a b. (a -> b) -> a -> b
$ String -> Name
mkName String
"<$>") Q Exp
dataConE)
                (Q Exp -> Q Exp) -> Q Exp -> Q Exp
forall a b. (a -> b) -> a -> b
$ Name -> Q Exp -> Q Exp
hashMapLookup Name
headName Q Exp
objectName
         in (Q Exp -> (Name, b, c) -> Q Exp)
-> Q Exp -> [(Name, b, c)] -> Q Exp
forall b a. (b -> a -> b) -> b -> [a] -> b
forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
foldl' (Q Exp -> Q Exp -> (Name, b, c) -> Q Exp
forall {b} {c}. Q Exp -> Q Exp -> (Name, b, c) -> Q Exp
addRecordField Q Exp
objectName) Q Exp
initialExpression [(Name, b, c)]
varBangTypes'
    makeRecordBody Q Exp
dataConE Q Exp
_ [] = Q Exp
dataConE
    conTName :: Q Kind
conTName = Name -> Q Kind
forall (m :: * -> *). Quote m => Name -> m Kind
conT Name
typeName

-- | Given a type derives a 'ToGraphQL' instance for it.
--
-- The derivation can only work when all nested types already have 'ToGraphQL'
-- instances.
--
-- The following cases are supported:
--
-- * Records are decoded as objects.
-- * Sum types with all data constructors without parameters are decoded as Enums.
-- * Sum types whose data constructors have exactly one parameter are decoded as Unions.
deriveToGraphQL :: Name -> Q [Dec]
deriveToGraphQL :: Name -> Q [Dec]
deriveToGraphQL Name
typeName = do
    TyConI Dec
plainConstructor <- Name -> Q Info
reify Name
typeName
    case Dec
plainConstructor of
        DataD Cxt
_ Name
_ [TyVarBndr ()]
_ Maybe Kind
_ [Con
cons'] [DerivClause]
_
            | RecC Name
dataConName [VarBangType]
varBangTypes <- Con
cons' ->
                Name -> [VarBangType] -> Q [Dec]
withRecordConstructor Name
dataConName [VarBangType]
varBangTypes
        DataD Cxt
_ Name
_ [TyVarBndr ()]
_ Maybe Kind
_ [Con]
cons' [DerivClause]
_ -> (Dec -> [Dec]) -> Q Dec -> Q [Dec]
forall a b. (a -> b) -> Q a -> Q b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap Dec -> [Dec]
forall a. a -> [a]
forall (f :: * -> *) a. Applicative f => a -> f a
pure
            (Q Dec -> Q [Dec]) -> Q Dec -> Q [Dec]
forall a b. (a -> b) -> a -> b
$ Q Cxt -> Q Kind -> [Q Dec] -> Q Dec
forall (m :: * -> *).
Quote m =>
m Cxt -> m Kind -> [m Dec] -> m Dec
instanceD Q Cxt
forall a. Monoid a => a
mempty (Q Kind -> Q Kind -> Q Kind
forall (m :: * -> *). Quote m => m Kind -> m Kind -> m Kind
appT (Name -> Q Kind
forall (m :: * -> *). Quote m => Name -> m Kind
conT ''ToGraphQL) Q Kind
conTName)
            ([Q Dec] -> Q Dec) -> [Q Dec] -> Q Dec
forall a b. (a -> b) -> a -> b
$ Q Dec -> [Q Dec]
forall a. a -> [a]
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Q Dec -> [Q Dec]) -> Q Dec -> [Q Dec]
forall a b. (a -> b) -> a -> b
$ Name -> [Q Clause] -> Q Dec
forall (m :: * -> *). Quote m => Name -> [m Clause] -> m Dec
funD 'toGraphQL
            ([Q Clause] -> Q Dec) -> [Q Clause] -> Q Dec
forall a b. (a -> b) -> a -> b
$ [Con] -> [Q Clause]
forall {t :: * -> *}. Traversable t => t Con -> t (Q Clause)
generateSumTypeInstance [Con]
cons'
        NewtypeD Cxt
_ Name
_ [TyVarBndr ()]
_ Maybe Kind
_ Con
cons' [DerivClause]
_
            | RecC Name
dataConName [VarBangType]
varBangTypes <- Con
cons' ->
                Name -> [VarBangType] -> Q [Dec]
withRecordConstructor Name
dataConName [VarBangType]
varBangTypes
        Dec
_ -> String -> Q [Dec]
forall a. HasCallStack => String -> a
error String
"Only objects, unions and enums are supported if all member types have a ToGraphQL instance"
  where
    conTName :: Q Kind
conTName = Name -> Q Kind
forall (m :: * -> *). Quote m => Name -> m Kind
conT Name
typeName
    collectEnumMemberNames :: Con -> Maybe Name
collectEnumMemberNames (NormalC Name
normalName []) = Name -> Maybe Name
forall a. a -> Maybe a
Just Name
normalName
    collectEnumMemberNames Con
_ = Maybe Name
forall a. Maybe a
Nothing
    collectUnionMembers :: Con -> Maybe Name
collectUnionMembers (NormalC Name
normalName [BangType
_]) = Name -> Maybe Name
forall a. a -> Maybe a
Just Name
normalName
    collectUnionMembers Con
_ = Maybe Name
forall a. Maybe a
Nothing
    enumMemberPattern :: Name -> Q Clause
enumMemberPattern Name
normalName
        = (Q Body -> [Q Dec] -> Q Clause) -> [Q Dec] -> Q Body -> Q Clause
forall a b c. (a -> b -> c) -> b -> a -> c
flip ([Q Pat] -> Q Body -> [Q Dec] -> Q Clause
forall (m :: * -> *).
Quote m =>
[m Pat] -> m Body -> [m Dec] -> m Clause
clause [Name -> [Q Pat] -> Q Pat
forall (m :: * -> *). Quote m => Name -> [m Pat] -> m Pat
conP Name
normalName [Q Pat]
forall a. Monoid a => a
mempty]) []
        (Q Body -> Q Clause) -> Q Body -> Q Clause
forall a b. (a -> b) -> a -> b
$ Q Exp -> Q Body
forall (m :: * -> *). Quote m => m Exp -> m Body
normalB [|Type.Enum $(Name -> Q Exp
stringLE Name
normalName)|]
    unionMemberPattern :: Name -> m Clause
unionMemberPattern Name
normalName = do
        Name
dataName <- String -> m Name
forall (m :: * -> *). Quote m => String -> m Name
newName String
"member"
        (m Body -> [m Dec] -> m Clause) -> [m Dec] -> m Body -> m Clause
forall a b c. (a -> b -> c) -> b -> a -> c
flip ([m Pat] -> m Body -> [m Dec] -> m Clause
forall (m :: * -> *).
Quote m =>
[m Pat] -> m Body -> [m Dec] -> m Clause
clause [Name -> [m Pat] -> m Pat
forall (m :: * -> *). Quote m => Name -> [m Pat] -> m Pat
conP Name
normalName [Name -> m Pat
forall (m :: * -> *). Quote m => Name -> m Pat
varP Name
dataName]]) []
            (m Body -> m Clause) -> m Body -> m Clause
forall a b. (a -> b) -> a -> b
$ m Exp -> m Body
forall (m :: * -> *). Quote m => m Exp -> m Body
normalB
            (m Exp -> m Body) -> m Exp -> m Body
forall a b. (a -> b) -> a -> b
$ m Exp -> m Exp -> m Exp
forall (m :: * -> *). Quote m => m Exp -> m Exp -> m Exp
appE (Name -> m Exp
forall (m :: * -> *). Quote m => Name -> m Exp
varE (Name -> m Exp) -> Name -> m Exp
forall a b. (a -> b) -> a -> b
$ String -> Name
mkName String
"toGraphQL")
            (m Exp -> m Exp) -> m Exp -> m Exp
forall a b. (a -> b) -> a -> b
$ Name -> m Exp
forall (m :: * -> *). Quote m => Name -> m Exp
varE Name
dataName
    generateSumTypeInstance :: t Con -> t (Q Clause)
generateSumTypeInstance t Con
cons'
        | Just t Name
enumMemberNames <- (Con -> Maybe Name) -> t Con -> Maybe (t Name)
forall (t :: * -> *) (f :: * -> *) a b.
(Traversable t, Applicative f) =>
(a -> f b) -> t a -> f (t b)
forall (f :: * -> *) a b.
Applicative f =>
(a -> f b) -> t a -> f (t b)
traverse Con -> Maybe Name
collectEnumMemberNames t Con
cons' =
            Name -> Q Clause
enumMemberPattern (Name -> Q Clause) -> t Name -> t (Q Clause)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> t Name
enumMemberNames
        | Just t Name
unionMembers <- (Con -> Maybe Name) -> t Con -> Maybe (t Name)
forall (t :: * -> *) (f :: * -> *) a b.
(Traversable t, Applicative f) =>
(a -> f b) -> t a -> f (t b)
forall (f :: * -> *) a b.
Applicative f =>
(a -> f b) -> t a -> f (t b)
traverse Con -> Maybe Name
collectUnionMembers t Con
cons' =
            Name -> Q Clause
forall {m :: * -> *}. Quote m => Name -> m Clause
unionMemberPattern (Name -> Q Clause) -> t Name -> t (Q Clause)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> t Name
unionMembers
        | Bool
otherwise =  String -> t (Q Clause)
forall a. HasCallStack => String -> a
error String
"All data constructors should have either no parameters (Enum) or one parameter (Union)"
    withRecordConstructor :: Name -> [VarBangType] -> Q [Dec]
withRecordConstructor Name
dataConName [VarBangType]
varBangTypes = do
        [(Name, Name)]
fieldAliases <- (VarBangType -> Q (Name, Name))
-> [VarBangType] -> Q [(Name, Name)]
forall (t :: * -> *) (f :: * -> *) a b.
(Traversable t, Applicative f) =>
(a -> f b) -> t a -> f (t b)
forall (f :: * -> *) a b.
Applicative f =>
(a -> f b) -> [a] -> f [b]
traverse VarBangType -> Q (Name, Name)
newFieldAliases [VarBangType]
varBangTypes
        let fBody :: Q Exp
fBody =
                [| Type.Object
                    $ HashMap.insert "__typename" $(Name -> Q Exp
stringLE Name
typeName)
                    $ HashMap.fromList $([Q Exp] -> Q Exp
forall (m :: * -> *). Quote m => [m Exp] -> m Exp
listE ([Q Exp] -> Q Exp) -> [Q Exp] -> Q Exp
forall a b. (a -> b) -> a -> b
$ (Name, Name) -> Q Exp
resultObjectPairs ((Name, Name) -> Q Exp) -> [(Name, Name)] -> [Q Exp]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [(Name, Name)]
fieldAliases)
                |]
            toGraphQLF :: Q Pat
toGraphQLF = Name -> [Q FieldPat] -> Q Pat
forall (m :: * -> *). Quote m => Name -> [m FieldPat] -> m Pat
recP Name
dataConName ((Name, Name) -> Q FieldPat
forall {f :: * -> *} {t}. Quote f => (t, Name) -> f (t, Pat)
newFieldPatterns ((Name, Name) -> Q FieldPat) -> [(Name, Name)] -> [Q FieldPat]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [(Name, Name)]
fieldAliases)
        [d|
            instance ToGraphQL $Q Kind
conTName
              where
                toGraphQL $Q Pat
toGraphQLF = $Q Exp
fBody
         |]
    newFieldAliases :: VarBangType -> Q (Name, Name)
    newFieldAliases :: VarBangType -> Q (Name, Name)
newFieldAliases (Name
name', Bang
_, Kind
_) = (Name
name',) (Name -> (Name, Name)) -> Q Name -> Q (Name, Name)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> String -> Q Name
forall (m :: * -> *). Quote m => String -> m Name
newName (Name -> String
nameBase Name
name')
    newFieldPatterns :: (t, Name) -> f (t, Pat)
newFieldPatterns (t
name', Name
alias) = (t
name',) (Pat -> (t, Pat)) -> f Pat -> f (t, Pat)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Name -> f Pat
forall (m :: * -> *). Quote m => Name -> m Pat
varP Name
alias
    resultObjectPairs :: (Name, Name) -> Q Exp
    resultObjectPairs :: (Name, Name) -> Q Exp
resultObjectPairs (Name
name', Name
alias) = [Q Exp] -> Q Exp
forall (m :: * -> *). Quote m => [m Exp] -> m Exp
tupE
        [ Lit -> Q Exp
forall (m :: * -> *). Quote m => Lit -> m Exp
litE (String -> Lit
stringL (String -> Lit) -> String -> Lit
forall a b. (a -> b) -> a -> b
$ Name -> String
nameBase Name
name')
        , [|toGraphQL $(Name -> Q Exp
forall (m :: * -> *). Quote m => Name -> m Exp
varE Name
alias)|]
        ]

stripIndentation :: String -> String
stripIndentation :: String -> String
stripIndentation String
code = String -> String
forall a. [a] -> [a]
reverse
    (String -> String) -> String -> String
forall a b. (a -> b) -> a -> b
$ (Char -> Bool) -> String -> String
forall a. (a -> Bool) -> [a] -> [a]
dropWhile Char -> Bool
isLineBreak
    (String -> String) -> String -> String
forall a b. (a -> b) -> a -> b
$ String -> String
forall a. [a] -> [a]
reverse
    (String -> String) -> String -> String
forall a b. (a -> b) -> a -> b
$ [String] -> String
unlines
    ([String] -> String) -> [String] -> String
forall a b. (a -> b) -> a -> b
$ Int -> String -> String
forall {t}. (Eq t, Num t) => t -> String -> String
indent Int
spaces (String -> String) -> [String] -> [String]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> String -> [String]
lines' String
withoutLeadingNewlines
  where
    indent :: t -> String -> String
indent t
0 String
xs = String
xs
    indent t
count (Char
' ' : String
xs) = t -> String -> String
indent (t
count t -> t -> t
forall a. Num a => a -> a -> a
- t
1) String
xs
    indent t
_ String
xs = String
xs
    withoutLeadingNewlines :: String
withoutLeadingNewlines = (Char -> Bool) -> String -> String
forall a. (a -> Bool) -> [a] -> [a]
dropWhile Char -> Bool
isLineBreak String
code
    spaces :: Int
spaces = String -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length (String -> Int) -> String -> Int
forall a b. (a -> b) -> a -> b
$ (Char -> Bool) -> String -> String
forall a. (a -> Bool) -> [a] -> [a]
takeWhile (Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
' ') String
withoutLeadingNewlines
    lines' :: String -> [String]
lines' String
"" = []
    lines' String
string =
        let (String
line, String
rest) = (Char -> Bool) -> String -> (String, String)
forall a. (a -> Bool) -> [a] -> ([a], [a])
break Char -> Bool
isLineBreak String
string
            reminder :: [String]
reminder =
                case String
rest of
                    [] -> []
                    Char
'\r' : Char
'\n' : String
strippedString -> String -> [String]
lines' String
strippedString
                    Char
_ : String
strippedString -> String -> [String]
lines' String
strippedString
         in String
line String -> [String] -> [String]
forall a. a -> [a] -> [a]
: [String]
reminder
    isLineBreak :: Char -> Bool
isLineBreak = ((Char -> Bool) -> String -> Bool)
-> String -> (Char -> Bool) -> Bool
forall a b c. (a -> b -> c) -> b -> a -> c
flip (Char -> Bool) -> String -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any [Char
'\n', Char
'\r'] ((Char -> Bool) -> Bool) -> (Char -> Char -> Bool) -> Char -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
(==)

-- | Removes leading and trailing newlines. Indentation of the first line is
-- removed from each line of the string.
{-# DEPRECATED gql "Use Language.GraphQL.TH.gql instead" #-}
gql :: QuasiQuoter
gql :: QuasiQuoter
gql = QuasiQuoter
    { quoteExp :: String -> Q Exp
quoteExp = Exp -> Q Exp
forall a. a -> Q a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Exp -> Q Exp) -> (String -> Exp) -> String -> Q Exp
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Lit -> Exp
LitE (Lit -> Exp) -> (String -> Lit) -> String -> Exp
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Lit
StringL (String -> Lit) -> (String -> String) -> String -> Lit
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> String
stripIndentation
    , quotePat :: String -> Q Pat
quotePat = Q Pat -> String -> Q Pat
forall a b. a -> b -> a
const
        (Q Pat -> String -> Q Pat) -> Q Pat -> String -> Q Pat
forall a b. (a -> b) -> a -> b
$ String -> Q Pat
forall a. String -> Q a
forall (m :: * -> *) a. MonadFail m => String -> m a
fail String
"Illegal gql QuasiQuote (allowed as expression only, used as a pattern)"
    , quoteType :: String -> Q Kind
quoteType = Q Kind -> String -> Q Kind
forall a b. a -> b -> a
const
        (Q Kind -> String -> Q Kind) -> Q Kind -> String -> Q Kind
forall a b. (a -> b) -> a -> b
$ String -> Q Kind
forall a. String -> Q a
forall (m :: * -> *) a. MonadFail m => String -> m a
fail String
"Illegal gql QuasiQuote (allowed as expression only, used as a type)"
    , quoteDec :: String -> Q [Dec]
quoteDec = Q [Dec] -> String -> Q [Dec]
forall a b. a -> b -> a
const
        (Q [Dec] -> String -> Q [Dec]) -> Q [Dec] -> String -> Q [Dec]
forall a b. (a -> b) -> a -> b
$ String -> Q [Dec]
forall a. String -> Q a
forall (m :: * -> *) a. MonadFail m => String -> m a
fail String
"Illegal gql QuasiQuote (allowed as expression only, used as a declaration)"
    }