module GraphQL.Internal.Syntax.Encoder
( queryDocument
, schemaDocument
, value
) where
import Protolude hiding (intercalate)
import qualified Data.Aeson as Aeson
import Data.Text (Text, cons, intercalate, pack, snoc)
import qualified GraphQL.Internal.Syntax.AST as AST
queryDocument :: AST.QueryDocument -> Text
queryDocument (AST.QueryDocument defs) = (`snoc` '\n') . mconcat $ definition <$> defs
definition :: AST.Definition -> Text
definition (AST.DefinitionOperation x) = operationDefinition x
definition (AST.DefinitionFragment x) = fragmentDefinition x
schemaDocument :: AST.SchemaDocument -> Text
schemaDocument (AST.SchemaDocument defs) = (`snoc` '\n') . mconcat $ typeDefinition <$> defs
operationDefinition :: AST.OperationDefinition -> Text
operationDefinition (AST.Query n) = "query " <> node n
operationDefinition (AST.Mutation n) = "mutation " <> node n
operationDefinition (AST.AnonymousQuery ss) = selectionSet ss
node :: AST.Node -> Text
node (AST.Node name vds ds ss) =
AST.unName name
<> optempty variableDefinitions vds
<> optempty directives ds
<> selectionSet ss
variableDefinitions :: [AST.VariableDefinition] -> Text
variableDefinitions = parensCommas variableDefinition
variableDefinition :: AST.VariableDefinition -> Text
variableDefinition (AST.VariableDefinition var ty dv) =
variable var <> ":" <> type_ ty <> maybe mempty defaultValue dv
defaultValue :: AST.DefaultValue -> Text
defaultValue val = "=" <> value val
variable :: AST.Variable -> Text
variable (AST.Variable name) = "$" <> AST.unName name
selectionSet :: AST.SelectionSet -> Text
selectionSet = bracesCommas selection
selection :: AST.Selection -> Text
selection (AST.SelectionField x) = field x
selection (AST.SelectionInlineFragment x) = inlineFragment x
selection (AST.SelectionFragmentSpread x) = fragmentSpread x
field :: AST.Field -> Text
field (AST.Field alias name args ds ss) =
optempty (`snoc` ':') (maybe mempty AST.unName alias)
<> AST.unName name
<> optempty arguments args
<> optempty directives ds
<> optempty selectionSet ss
arguments :: [AST.Argument] -> Text
arguments = parensCommas argument
argument :: AST.Argument -> Text
argument (AST.Argument name v) = AST.unName name <> ":" <> value v
fragmentSpread :: AST.FragmentSpread -> Text
fragmentSpread (AST.FragmentSpread name ds) =
"..." <> AST.unName name <> optempty directives ds
inlineFragment :: AST.InlineFragment -> Text
inlineFragment (AST.InlineFragment (Just (AST.NamedType tc)) ds ss) =
"... on " <> AST.unName tc
<> optempty directives ds
<> optempty selectionSet ss
inlineFragment (AST.InlineFragment Nothing ds ss) =
"... " <> optempty directives ds
<> optempty selectionSet ss
fragmentDefinition :: AST.FragmentDefinition -> Text
fragmentDefinition (AST.FragmentDefinition name (AST.NamedType tc) ds ss) =
"fragment " <> AST.unName name <> " on " <> AST.unName tc
<> optempty directives ds
<> selectionSet ss
value :: AST.Value -> Text
value (AST.ValueVariable x) = variable x
value (AST.ValueInt x) = pack $ show x
value (AST.ValueFloat x) = pack $ show x
value (AST.ValueBoolean x) = booleanValue x
value (AST.ValueString x) = stringValue x
value (AST.ValueEnum x) = AST.unName x
value (AST.ValueList x) = listValue x
value (AST.ValueObject x) = objectValue x
value AST.ValueNull = "null"
booleanValue :: Bool -> Text
booleanValue True = "true"
booleanValue False = "false"
stringValue :: AST.StringValue -> Text
stringValue (AST.StringValue v) = toS $ Aeson.encode v
listValue :: AST.ListValue -> Text
listValue (AST.ListValue vs) = bracketsCommas value vs
objectValue :: AST.ObjectValue -> Text
objectValue (AST.ObjectValue ofs) = bracesCommas objectField ofs
objectField :: AST.ObjectField -> Text
objectField (AST.ObjectField name v) = AST.unName name <> ":" <> value v
directives :: [AST.Directive] -> Text
directives = spaces directive
directive :: AST.Directive -> Text
directive (AST.Directive name args) = "@" <> AST.unName name <> optempty arguments args
type_ :: AST.Type -> Text
type_ (AST.TypeNamed (AST.NamedType x)) = AST.unName x
type_ (AST.TypeList x) = listType x
type_ (AST.TypeNonNull x) = nonNullType x
namedType :: AST.NamedType -> Text
namedType (AST.NamedType name) = AST.unName name
listType :: AST.ListType -> Text
listType (AST.ListType ty) = brackets (type_ ty)
nonNullType :: AST.NonNullType -> Text
nonNullType (AST.NonNullTypeNamed (AST.NamedType x)) = AST.unName x <> "!"
nonNullType (AST.NonNullTypeList x) = listType x <> "!"
typeDefinition :: AST.TypeDefinition -> Text
typeDefinition (AST.TypeDefinitionObject x) = objectTypeDefinition x
typeDefinition (AST.TypeDefinitionInterface x) = interfaceTypeDefinition x
typeDefinition (AST.TypeDefinitionUnion x) = unionTypeDefinition x
typeDefinition (AST.TypeDefinitionScalar x) = scalarTypeDefinition x
typeDefinition (AST.TypeDefinitionEnum x) = enumTypeDefinition x
typeDefinition (AST.TypeDefinitionInputObject x) = inputObjectTypeDefinition x
typeDefinition (AST.TypeDefinitionTypeExtension x) = typeExtensionDefinition x
objectTypeDefinition :: AST.ObjectTypeDefinition -> Text
objectTypeDefinition (AST.ObjectTypeDefinition name ifaces fds) =
"type " <> AST.unName name
<> optempty (spaced . interfaces) ifaces
<> optempty fieldDefinitions fds
interfaces :: AST.Interfaces -> Text
interfaces = ("implements " <>) . spaces namedType
fieldDefinitions :: [AST.FieldDefinition] -> Text
fieldDefinitions = bracesCommas fieldDefinition
fieldDefinition :: AST.FieldDefinition -> Text
fieldDefinition (AST.FieldDefinition name args ty) =
AST.unName name <> optempty argumentsDefinition args
<> ":"
<> type_ ty
argumentsDefinition :: AST.ArgumentsDefinition -> Text
argumentsDefinition = parensCommas inputValueDefinition
interfaceTypeDefinition :: AST.InterfaceTypeDefinition -> Text
interfaceTypeDefinition (AST.InterfaceTypeDefinition name fds) =
"interface " <> AST.unName name <> fieldDefinitions fds
unionTypeDefinition :: AST.UnionTypeDefinition -> Text
unionTypeDefinition (AST.UnionTypeDefinition name ums) =
"union " <> AST.unName name <> "=" <> unionMembers ums
unionMembers :: [AST.NamedType] -> Text
unionMembers = intercalate "|" . fmap namedType
scalarTypeDefinition :: AST.ScalarTypeDefinition -> Text
scalarTypeDefinition (AST.ScalarTypeDefinition name) = "scalar " <> AST.unName name
enumTypeDefinition :: AST.EnumTypeDefinition -> Text
enumTypeDefinition (AST.EnumTypeDefinition name evds) =
"enum " <> AST.unName name
<> bracesCommas enumValueDefinition evds
enumValueDefinition :: AST.EnumValueDefinition -> Text
enumValueDefinition (AST.EnumValueDefinition name) = AST.unName name
inputObjectTypeDefinition :: AST.InputObjectTypeDefinition -> Text
inputObjectTypeDefinition (AST.InputObjectTypeDefinition name ivds) =
"input " <> AST.unName name <> inputValueDefinitions ivds
inputValueDefinitions :: [AST.InputValueDefinition] -> Text
inputValueDefinitions = bracesCommas inputValueDefinition
inputValueDefinition :: AST.InputValueDefinition -> Text
inputValueDefinition (AST.InputValueDefinition name ty dv) =
AST.unName name <> ":" <> type_ ty <> maybe mempty defaultValue dv
typeExtensionDefinition :: AST.TypeExtensionDefinition -> Text
typeExtensionDefinition (AST.TypeExtensionDefinition otd) =
"extend " <> objectTypeDefinition otd
spaced :: Text -> Text
spaced = cons '\SP'
between :: Char -> Char -> Text -> Text
between open close = cons open . (`snoc` close)
parens :: Text -> Text
parens = between '(' ')'
brackets :: Text -> Text
brackets = between '[' ']'
braces :: Text -> Text
braces = between '{' '}'
spaces :: (a -> Text) -> [a] -> Text
spaces f = intercalate "\SP" . fmap f
parensCommas :: (a -> Text) -> [a] -> Text
parensCommas f = parens . intercalate "," . fmap f
bracketsCommas :: (a -> Text) -> [a] -> Text
bracketsCommas f = brackets . intercalate "," . fmap f
bracesCommas :: (a -> Text) -> [a] -> Text
bracesCommas f = braces . intercalate "," . fmap f
optempty :: (Eq a, Monoid a, Monoid b) => (a -> b) -> a -> b
optempty f xs = if xs == mempty then mempty else f xs