2015-09-21 18:26:22 +02:00
|
|
|
{-# LANGUAGE OverloadedStrings #-}
|
2016-03-15 14:02:34 +01:00
|
|
|
-- | This module defines a printer for the @GraphQL@ language.
|
2019-07-14 05:58:05 +02:00
|
|
|
module Language.GraphQL.Encoder
|
2019-07-31 05:40:17 +02:00
|
|
|
( Formatter(..)
|
|
|
|
, definition
|
2019-07-27 07:19:21 +02:00
|
|
|
, document
|
2019-07-14 05:58:05 +02:00
|
|
|
) where
|
2015-09-21 18:26:22 +02:00
|
|
|
|
2017-01-28 18:15:14 +01:00
|
|
|
import Data.Foldable (fold)
|
2015-09-21 18:26:22 +02:00
|
|
|
import Data.Monoid ((<>))
|
2017-01-28 18:15:14 +01:00
|
|
|
import qualified Data.List.NonEmpty as NonEmpty (toList)
|
2019-07-27 07:19:21 +02:00
|
|
|
import Data.Text (Text, pack)
|
|
|
|
import qualified Data.Text as Text
|
2019-07-07 06:31:53 +02:00
|
|
|
import Language.GraphQL.AST
|
2015-09-21 18:26:22 +02:00
|
|
|
|
2019-07-31 05:40:17 +02:00
|
|
|
-- | Instructs the encoder whether a GraphQL should be minified or pretty
|
|
|
|
-- printed.
|
|
|
|
data Formatter
|
|
|
|
= Minified
|
|
|
|
| Pretty Int
|
|
|
|
|
2019-07-27 07:19:21 +02:00
|
|
|
-- | Converts a 'Document' into a string.
|
2019-07-31 05:40:17 +02:00
|
|
|
document :: Formatter -> Document -> Text
|
|
|
|
document formatter defs
|
|
|
|
| Pretty _ <- formatter = Text.intercalate "\n" encodeDocument
|
|
|
|
| Minified <-formatter = Text.snoc (mconcat encodeDocument) '\n'
|
|
|
|
where
|
|
|
|
encodeDocument = NonEmpty.toList $ definition formatter <$> defs
|
2015-09-21 18:26:22 +02:00
|
|
|
|
2019-07-27 07:19:21 +02:00
|
|
|
-- | Converts a 'Definition' into a string.
|
2019-07-31 05:40:17 +02:00
|
|
|
definition :: Formatter -> Definition -> Text
|
|
|
|
definition formatter x
|
|
|
|
| Pretty _ <- formatter = Text.snoc (encodeDefinition x) '\n'
|
|
|
|
| Minified <- formatter = encodeDefinition x
|
2019-07-27 07:19:21 +02:00
|
|
|
where
|
|
|
|
encodeDefinition (DefinitionOperation operation)
|
2019-07-31 05:40:17 +02:00
|
|
|
= operationDefinition formatter operation
|
2019-07-27 07:19:21 +02:00
|
|
|
encodeDefinition (DefinitionFragment fragment)
|
2019-07-31 05:40:17 +02:00
|
|
|
= fragmentDefinition formatter fragment
|
|
|
|
|
|
|
|
operationDefinition :: Formatter -> OperationDefinition -> Text
|
|
|
|
operationDefinition formatter (OperationSelectionSet sels)
|
|
|
|
= selectionSet formatter sels
|
|
|
|
operationDefinition formatter (OperationDefinition Query name vars dirs sels)
|
|
|
|
= "query " <> node formatter (fold name) vars dirs sels
|
|
|
|
operationDefinition formatter (OperationDefinition Mutation name vars dirs sels)
|
|
|
|
= "mutation " <> node formatter (fold name) vars dirs sels
|
|
|
|
|
|
|
|
node :: Formatter
|
|
|
|
-> Name
|
|
|
|
-> VariableDefinitions
|
|
|
|
-> Directives
|
|
|
|
-> SelectionSet
|
|
|
|
-> Text
|
|
|
|
node formatter name vars dirs sels
|
|
|
|
= name
|
2017-01-28 18:15:14 +01:00
|
|
|
<> optempty variableDefinitions vars
|
|
|
|
<> optempty directives dirs
|
2019-07-31 05:40:17 +02:00
|
|
|
<> selectionSet formatter sels
|
2015-09-21 18:26:22 +02:00
|
|
|
|
|
|
|
variableDefinitions :: [VariableDefinition] -> Text
|
2015-09-22 10:45:14 +02:00
|
|
|
variableDefinitions = parensCommas variableDefinition
|
2015-09-21 18:26:22 +02:00
|
|
|
|
|
|
|
variableDefinition :: VariableDefinition -> Text
|
|
|
|
variableDefinition (VariableDefinition var ty dv) =
|
2015-09-22 10:45:14 +02:00
|
|
|
variable var <> ":" <> type_ ty <> maybe mempty defaultValue dv
|
2015-09-21 18:26:22 +02:00
|
|
|
|
2019-07-18 05:10:02 +02:00
|
|
|
defaultValue :: Value -> Text
|
2015-09-22 10:45:14 +02:00
|
|
|
defaultValue val = "=" <> value val
|
2015-09-21 18:26:22 +02:00
|
|
|
|
2019-07-18 05:10:02 +02:00
|
|
|
variable :: Name -> Text
|
2017-01-28 18:15:14 +01:00
|
|
|
variable var = "$" <> var
|
2015-09-21 18:26:22 +02:00
|
|
|
|
2019-07-31 05:40:17 +02:00
|
|
|
selectionSet :: Formatter -> SelectionSet -> Text
|
|
|
|
selectionSet formatter@(Pretty _) = bracesNewLines (selection formatter) . NonEmpty.toList
|
|
|
|
selectionSet Minified = bracesCommas (selection Minified) . NonEmpty.toList
|
2017-01-28 18:15:14 +01:00
|
|
|
|
2019-07-31 05:40:17 +02:00
|
|
|
selectionSetOpt :: Formatter -> SelectionSetOpt -> Text
|
|
|
|
selectionSetOpt formatter@(Pretty _) = bracesNewLines $ selection formatter
|
|
|
|
selectionSetOpt Minified = bracesCommas $ selection Minified
|
2015-09-21 18:26:22 +02:00
|
|
|
|
2019-07-31 05:40:17 +02:00
|
|
|
selection :: Formatter -> Selection -> Text
|
|
|
|
selection formatter (SelectionField x) = field formatter x
|
|
|
|
selection formatter (SelectionInlineFragment x) = inlineFragment formatter x
|
|
|
|
selection _ (SelectionFragmentSpread x) = fragmentSpread x
|
2015-09-21 18:26:22 +02:00
|
|
|
|
2019-07-31 05:40:17 +02:00
|
|
|
field :: Formatter -> Field -> Text
|
|
|
|
field formatter (Field alias name args dirs selso) =
|
|
|
|
optempty (`Text.append` ":") (fold alias)
|
2015-09-21 18:26:22 +02:00
|
|
|
<> name
|
|
|
|
<> optempty arguments args
|
2017-01-28 18:15:14 +01:00
|
|
|
<> optempty directives dirs
|
2019-07-31 05:40:17 +02:00
|
|
|
<> optempty (selectionSetOpt formatter) selso
|
2015-09-21 18:26:22 +02:00
|
|
|
|
|
|
|
arguments :: [Argument] -> Text
|
2015-09-22 10:45:14 +02:00
|
|
|
arguments = parensCommas argument
|
2015-09-21 18:26:22 +02:00
|
|
|
|
|
|
|
argument :: Argument -> Text
|
2015-09-22 10:45:14 +02:00
|
|
|
argument (Argument name v) = name <> ":" <> value v
|
2015-09-21 18:26:22 +02:00
|
|
|
|
|
|
|
-- * Fragments
|
|
|
|
|
|
|
|
fragmentSpread :: FragmentSpread -> Text
|
|
|
|
fragmentSpread (FragmentSpread name ds) =
|
2015-09-22 10:45:14 +02:00
|
|
|
"..." <> name <> optempty directives ds
|
2015-09-21 18:26:22 +02:00
|
|
|
|
2019-07-31 05:40:17 +02:00
|
|
|
inlineFragment :: Formatter -> InlineFragment -> Text
|
|
|
|
inlineFragment formatter (InlineFragment tc dirs sels) =
|
2017-01-28 18:15:14 +01:00
|
|
|
"... on " <> fold tc
|
|
|
|
<> directives dirs
|
2019-07-31 05:40:17 +02:00
|
|
|
<> selectionSet formatter sels
|
2015-09-21 18:26:22 +02:00
|
|
|
|
2019-07-31 05:40:17 +02:00
|
|
|
fragmentDefinition :: Formatter -> FragmentDefinition -> Text
|
|
|
|
fragmentDefinition formatter (FragmentDefinition name tc dirs sels) =
|
2015-09-21 18:26:22 +02:00
|
|
|
"fragment " <> name <> " on " <> tc
|
2017-01-28 18:15:14 +01:00
|
|
|
<> optempty directives dirs
|
2019-07-31 05:40:17 +02:00
|
|
|
<> selectionSet formatter sels
|
2015-09-21 18:26:22 +02:00
|
|
|
|
|
|
|
-- * Values
|
|
|
|
|
|
|
|
value :: Value -> Text
|
|
|
|
value (ValueVariable x) = variable x
|
2017-01-28 18:15:14 +01:00
|
|
|
-- TODO: This will be replaced with `decimal` Builder
|
2015-09-21 18:26:22 +02:00
|
|
|
value (ValueInt x) = pack $ show x
|
2017-01-28 18:15:14 +01:00
|
|
|
-- TODO: This will be replaced with `decimal` Builder
|
2015-09-21 18:26:22 +02:00
|
|
|
value (ValueFloat x) = pack $ show x
|
|
|
|
value (ValueBoolean x) = booleanValue x
|
2017-01-28 18:15:14 +01:00
|
|
|
value ValueNull = mempty
|
2015-09-21 18:26:22 +02:00
|
|
|
value (ValueString x) = stringValue x
|
|
|
|
value (ValueEnum x) = x
|
|
|
|
value (ValueList x) = listValue x
|
|
|
|
value (ValueObject x) = objectValue x
|
|
|
|
|
|
|
|
booleanValue :: Bool -> Text
|
|
|
|
booleanValue True = "true"
|
|
|
|
booleanValue False = "false"
|
|
|
|
|
|
|
|
-- TODO: Escape characters
|
2016-02-22 13:59:38 +01:00
|
|
|
stringValue :: Text -> Text
|
|
|
|
stringValue = quotes
|
2015-09-21 18:26:22 +02:00
|
|
|
|
2019-07-18 05:10:02 +02:00
|
|
|
listValue :: [Value] -> Text
|
2017-01-28 18:15:14 +01:00
|
|
|
listValue = bracketsCommas value
|
2015-09-21 18:26:22 +02:00
|
|
|
|
2019-07-18 05:10:02 +02:00
|
|
|
objectValue :: [ObjectField] -> Text
|
2017-01-28 18:15:14 +01:00
|
|
|
objectValue = bracesCommas objectField
|
2015-09-21 18:26:22 +02:00
|
|
|
|
|
|
|
objectField :: ObjectField -> Text
|
2015-09-22 10:45:14 +02:00
|
|
|
objectField (ObjectField name v) = name <> ":" <> value v
|
2015-09-21 18:26:22 +02:00
|
|
|
|
2015-09-22 10:45:14 +02:00
|
|
|
-- * Directives
|
2015-09-21 18:26:22 +02:00
|
|
|
|
|
|
|
directives :: [Directive] -> Text
|
2015-09-22 10:45:14 +02:00
|
|
|
directives = spaces directive
|
2015-09-21 18:26:22 +02:00
|
|
|
|
|
|
|
directive :: Directive -> Text
|
|
|
|
directive (Directive name args) = "@" <> name <> optempty arguments args
|
|
|
|
|
|
|
|
-- * Type Reference
|
|
|
|
|
|
|
|
type_ :: Type -> Text
|
2017-01-28 18:15:14 +01:00
|
|
|
type_ (TypeNamed x) = x
|
|
|
|
type_ (TypeList x) = listType x
|
2015-09-21 18:26:22 +02:00
|
|
|
type_ (TypeNonNull x) = nonNullType x
|
|
|
|
|
2017-01-28 18:15:14 +01:00
|
|
|
listType :: Type -> Text
|
|
|
|
listType x = brackets (type_ x)
|
2015-09-21 18:26:22 +02:00
|
|
|
|
|
|
|
nonNullType :: NonNullType -> Text
|
2017-01-28 18:15:14 +01:00
|
|
|
nonNullType (NonNullTypeNamed x) = x <> "!"
|
2015-09-21 18:26:22 +02:00
|
|
|
nonNullType (NonNullTypeList x) = listType x <> "!"
|
|
|
|
|
|
|
|
-- * Internal
|
|
|
|
|
2015-09-22 10:45:14 +02:00
|
|
|
between :: Char -> Char -> Text -> Text
|
2019-07-27 07:19:21 +02:00
|
|
|
between open close = Text.cons open . (`Text.snoc` close)
|
2015-09-21 18:26:22 +02:00
|
|
|
|
|
|
|
parens :: Text -> Text
|
2015-09-22 10:45:14 +02:00
|
|
|
parens = between '(' ')'
|
2015-09-21 18:26:22 +02:00
|
|
|
|
|
|
|
brackets :: Text -> Text
|
2015-09-22 10:45:14 +02:00
|
|
|
brackets = between '[' ']'
|
2015-09-21 18:26:22 +02:00
|
|
|
|
2015-09-22 10:45:14 +02:00
|
|
|
braces :: Text -> Text
|
|
|
|
braces = between '{' '}'
|
2015-09-21 18:26:22 +02:00
|
|
|
|
2015-09-22 13:53:37 +02:00
|
|
|
quotes :: Text -> Text
|
|
|
|
quotes = between '"' '"'
|
|
|
|
|
2015-09-22 10:45:14 +02:00
|
|
|
spaces :: (a -> Text) -> [a] -> Text
|
2019-07-27 07:19:21 +02:00
|
|
|
spaces f = Text.intercalate "\SP" . fmap f
|
2015-09-22 10:45:14 +02:00
|
|
|
|
|
|
|
parensCommas :: (a -> Text) -> [a] -> Text
|
2019-07-27 07:19:21 +02:00
|
|
|
parensCommas f = parens . Text.intercalate "," . fmap f
|
2015-09-22 10:45:14 +02:00
|
|
|
|
|
|
|
bracketsCommas :: (a -> Text) -> [a] -> Text
|
2019-07-27 07:19:21 +02:00
|
|
|
bracketsCommas f = brackets . Text.intercalate "," . fmap f
|
2015-09-22 10:45:14 +02:00
|
|
|
|
|
|
|
bracesCommas :: (a -> Text) -> [a] -> Text
|
2019-07-27 07:19:21 +02:00
|
|
|
bracesCommas f = braces . Text.intercalate "," . fmap f
|
2015-09-21 18:26:22 +02:00
|
|
|
|
2019-07-31 05:40:17 +02:00
|
|
|
bracesNewLines :: (a -> Text) -> [a] -> Text
|
|
|
|
bracesNewLines f xs = Text.append (Text.intercalate "\n" $ "{" : fmap f xs) "\n}"
|
|
|
|
|
2015-09-21 18:26:22 +02:00
|
|
|
optempty :: (Eq a, Monoid a, Monoid b) => (a -> b) -> a -> b
|
|
|
|
optempty f xs = if xs == mempty then mempty else f xs
|