{-# LANGUAGE CPP #-} {-# LANGUAGE OverloadedStrings #-} module Data.GraphQL.Encoder where #if !MIN_VERSION_base(4,8,0) import Control.Applicative ((<$>)) import Data.Monoid (Monoid, mconcat, mempty) #endif import Data.Monoid ((<>)) import Data.Text (Text, cons, intercalate, pack, snoc) import Data.GraphQL.AST -- * Document -- TODO: Use query shorthand document :: Document -> Text document (Document defs) = (`snoc` '\n') . mconcat $ definition <$> defs definition :: Definition -> Text definition (DefinitionOperation x) = operationDefinition x definition (DefinitionFragment x) = fragmentDefinition x definition (DefinitionType x) = typeDefinition x operationDefinition :: OperationDefinition -> Text operationDefinition (Query n) = "query " <> node n operationDefinition (Mutation n) = "mutation " <> node n node :: Node -> Text node (Node name vds ds ss) = name <> optempty variableDefinitions vds <> optempty directives ds <> selectionSet ss variableDefinitions :: [VariableDefinition] -> Text variableDefinitions = parensCommas variableDefinition variableDefinition :: VariableDefinition -> Text variableDefinition (VariableDefinition var ty dv) = variable var <> ":" <> type_ ty <> maybe mempty defaultValue dv defaultValue :: DefaultValue -> Text defaultValue val = "=" <> value val variable :: Variable -> Text variable (Variable name) = "$" <> name selectionSet :: SelectionSet -> Text selectionSet = bracesCommas selection selection :: Selection -> Text selection (SelectionField x) = field x selection (SelectionInlineFragment x) = inlineFragment x selection (SelectionFragmentSpread x) = fragmentSpread x field :: Field -> Text field (Field alias name args ds ss) = optempty (`snoc` ':') alias <> name <> optempty arguments args <> optempty directives ds <> optempty selectionSet ss arguments :: [Argument] -> Text arguments = parensCommas argument argument :: Argument -> Text argument (Argument name v) = name <> ":" <> value v -- * Fragments fragmentSpread :: FragmentSpread -> Text fragmentSpread (FragmentSpread name ds) = "..." <> name <> optempty directives ds inlineFragment :: InlineFragment -> Text inlineFragment (InlineFragment (NamedType tc) ds ss) = "... on " <> tc <> optempty directives ds <> optempty selectionSet ss fragmentDefinition :: FragmentDefinition -> Text fragmentDefinition (FragmentDefinition name (NamedType tc) ds ss) = "fragment " <> name <> " on " <> tc <> optempty directives ds <> selectionSet ss -- * Values value :: Value -> Text value (ValueVariable x) = variable x -- TODO: This will be replaced with `decimal` Buidler value (ValueInt x) = pack $ show x -- TODO: This will be replaced with `decimal` Buidler value (ValueFloat x) = pack $ show x value (ValueBoolean x) = booleanValue x 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 stringValue :: StringValue -> Text stringValue (StringValue v) = quotes v listValue :: ListValue -> Text listValue (ListValue vs) = bracketsCommas value vs objectValue :: ObjectValue -> Text objectValue (ObjectValue ofs) = bracesCommas objectField ofs objectField :: ObjectField -> Text objectField (ObjectField name v) = name <> ":" <> value v -- * Directives directives :: [Directive] -> Text directives = spaces directive directive :: Directive -> Text directive (Directive name args) = "@" <> name <> optempty arguments args -- * Type Reference type_ :: Type -> Text type_ (TypeNamed (NamedType x)) = x type_ (TypeList x) = listType x type_ (TypeNonNull x) = nonNullType x namedType :: NamedType -> Text namedType (NamedType name) = name listType :: ListType -> Text listType (ListType ty) = brackets (type_ ty) nonNullType :: NonNullType -> Text nonNullType (NonNullTypeNamed (NamedType x)) = x <> "!" nonNullType (NonNullTypeList x) = listType x <> "!" typeDefinition :: TypeDefinition -> Text typeDefinition (TypeDefinitionObject x) = objectTypeDefinition x typeDefinition (TypeDefinitionInterface x) = interfaceTypeDefinition x typeDefinition (TypeDefinitionUnion x) = unionTypeDefinition x typeDefinition (TypeDefinitionScalar x) = scalarTypeDefinition x typeDefinition (TypeDefinitionEnum x) = enumTypeDefinition x typeDefinition (TypeDefinitionInputObject x) = inputObjectTypeDefinition x typeDefinition (TypeDefinitionTypeExtension x) = typeExtensionDefinition x objectTypeDefinition :: ObjectTypeDefinition -> Text objectTypeDefinition (ObjectTypeDefinition name ifaces fds) = "type " <> name <> optempty (spaced . interfaces) ifaces <> optempty fieldDefinitions fds interfaces :: Interfaces -> Text interfaces = ("implements " <>) . spaces namedType fieldDefinitions :: [FieldDefinition] -> Text fieldDefinitions = bracesCommas fieldDefinition fieldDefinition :: FieldDefinition -> Text fieldDefinition (FieldDefinition name args ty) = name <> optempty argumentsDefinition args <> ":" <> type_ ty argumentsDefinition :: ArgumentsDefinition -> Text argumentsDefinition = parensCommas inputValueDefinition interfaceTypeDefinition :: InterfaceTypeDefinition -> Text interfaceTypeDefinition (InterfaceTypeDefinition name fds) = "interface " <> name <> fieldDefinitions fds unionTypeDefinition :: UnionTypeDefinition -> Text unionTypeDefinition (UnionTypeDefinition name ums) = "union " <> name <> "=" <> unionMembers ums unionMembers :: [NamedType] -> Text unionMembers = intercalate "|" . fmap namedType scalarTypeDefinition :: ScalarTypeDefinition -> Text scalarTypeDefinition (ScalarTypeDefinition name) = "scalar " <> name enumTypeDefinition :: EnumTypeDefinition -> Text enumTypeDefinition (EnumTypeDefinition name evds) = "enum " <> name <> bracesCommas enumValueDefinition evds enumValueDefinition :: EnumValueDefinition -> Text enumValueDefinition (EnumValueDefinition name) = name inputObjectTypeDefinition :: InputObjectTypeDefinition -> Text inputObjectTypeDefinition (InputObjectTypeDefinition name ivds) = "input " <> name <> inputValueDefinitions ivds inputValueDefinitions :: [InputValueDefinition] -> Text inputValueDefinitions = bracesCommas inputValueDefinition inputValueDefinition :: InputValueDefinition -> Text inputValueDefinition (InputValueDefinition name ty dv) = name <> ":" <> type_ ty <> maybe mempty defaultValue dv typeExtensionDefinition :: TypeExtensionDefinition -> Text typeExtensionDefinition (TypeExtensionDefinition otd) = "extend " <> objectTypeDefinition otd -- * Internal 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 '{' '}' quotes :: Text -> Text quotes = 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