aboutsummaryrefslogtreecommitdiff
path: root/src/Language/GraphQL/AST/Encoder.hs
diff options
context:
space:
mode:
Diffstat (limited to 'src/Language/GraphQL/AST/Encoder.hs')
-rw-r--r--src/Language/GraphQL/AST/Encoder.hs116
1 files changed, 76 insertions, 40 deletions
diff --git a/src/Language/GraphQL/AST/Encoder.hs b/src/Language/GraphQL/AST/Encoder.hs
index 508212a..69f5599 100644
--- a/src/Language/GraphQL/AST/Encoder.hs
+++ b/src/Language/GraphQL/AST/Encoder.hs
@@ -15,7 +15,6 @@ module Language.GraphQL.AST.Encoder
import Data.Char (ord)
import Data.Foldable (fold)
-import Data.Monoid ((<>))
import qualified Data.List.NonEmpty as NonEmpty
import Data.Text (Text)
import qualified Data.Text as Text
@@ -26,6 +25,7 @@ import qualified Data.Text.Lazy.Builder as Builder
import Data.Text.Lazy.Builder.Int (decimal, hexadecimal)
import Data.Text.Lazy.Builder.RealFloat (realFloat)
import qualified Language.GraphQL.AST as Full
+import Language.GraphQL.AST.Document
-- | Instructs the encoder whether the GraphQL document should be minified or
-- pretty printed.
@@ -43,16 +43,18 @@ pretty = Pretty 0
minified :: Formatter
minified = Minified
--- | Converts a 'Full.Document' into a string.
-document :: Formatter -> Full.Document -> Lazy.Text
+-- | Converts a Document' into a string.
+document :: Formatter -> Document -> Lazy.Text
document formatter defs
| Pretty _ <- formatter = Lazy.Text.intercalate "\n" encodeDocument
| Minified <-formatter = Lazy.Text.snoc (mconcat encodeDocument) '\n'
where
- encodeDocument = NonEmpty.toList $ definition formatter <$> defs
+ encodeDocument = foldr executableDefinition [] defs
+ executableDefinition (ExecutableDefinition x) acc = definition formatter x : acc
+ executableDefinition _ acc = acc
--- | Converts a 'Full.Definition' into a string.
-definition :: Formatter -> Full.Definition -> Lazy.Text
+-- | Converts a t'Full.ExecutableDefinition' into a string.
+definition :: Formatter -> ExecutableDefinition -> Lazy.Text
definition formatter x
| Pretty _ <- formatter = Lazy.Text.snoc (encodeDefinition x) '\n'
| Minified <- formatter = encodeDefinition x
@@ -62,14 +64,16 @@ definition formatter x
encodeDefinition (Full.DefinitionFragment fragment)
= fragmentDefinition formatter fragment
+-- | Converts a 'Full.OperationDefinition into a string.
operationDefinition :: Formatter -> Full.OperationDefinition -> Lazy.Text
-operationDefinition formatter (Full.OperationSelectionSet sels)
+operationDefinition formatter (Full.SelectionSet sels)
= selectionSet formatter sels
operationDefinition formatter (Full.OperationDefinition Full.Query name vars dirs sels)
= "query " <> node formatter name vars dirs sels
operationDefinition formatter (Full.OperationDefinition Full.Mutation name vars dirs sels)
= "mutation " <> node formatter name vars dirs sels
+-- | Converts a Full.Query or Full.Mutation into a string.
node :: Formatter ->
Maybe Full.Name ->
[Full.VariableDefinition] ->
@@ -110,17 +114,21 @@ selectionSet formatter
selectionSetOpt :: Formatter -> Full.SelectionSetOpt -> Lazy.Text
selectionSetOpt formatter = bracesList formatter $ selection formatter
+indentSymbol :: Lazy.Text
+indentSymbol = " "
+
indent :: (Integral a) => a -> Lazy.Text
-indent indentation = Lazy.Text.replicate (fromIntegral indentation) " "
+indent indentation = Lazy.Text.replicate (fromIntegral indentation) indentSymbol
selection :: Formatter -> Full.Selection -> Lazy.Text
selection formatter = Lazy.Text.append indent' . encodeSelection
where
- encodeSelection (Full.SelectionField field') = field incrementIndent field'
- encodeSelection (Full.SelectionInlineFragment fragment) =
- inlineFragment incrementIndent fragment
- encodeSelection (Full.SelectionFragmentSpread spread) =
- fragmentSpread incrementIndent spread
+ encodeSelection (Full.Field alias name args directives' selections) =
+ field incrementIndent alias name args directives' selections
+ encodeSelection (Full.InlineFragment typeCondition directives' selections) =
+ inlineFragment incrementIndent typeCondition directives' selections
+ encodeSelection (Full.FragmentSpread name directives') =
+ fragmentSpread incrementIndent name directives'
incrementIndent
| Pretty indentation <- formatter = Pretty $ indentation + 1
| otherwise = Minified
@@ -131,8 +139,15 @@ selection formatter = Lazy.Text.append indent' . encodeSelection
colon :: Formatter -> Lazy.Text
colon formatter = eitherFormat formatter ": " ":"
-field :: Formatter -> Full.Field -> Lazy.Text
-field formatter (Full.Field alias name args dirs set)
+-- | Converts Full.Field into a string
+field :: Formatter ->
+ Maybe Full.Name ->
+ Full.Name ->
+ [Full.Argument] ->
+ [Full.Directive] ->
+ [Full.Selection] ->
+ Lazy.Text
+field formatter alias name args dirs set
= optempty prependAlias (fold alias)
<> Lazy.Text.fromStrict name
<> optempty (arguments formatter) args
@@ -154,13 +169,18 @@ argument formatter (Full.Argument name value')
-- * Fragments
-fragmentSpread :: Formatter -> Full.FragmentSpread -> Lazy.Text
-fragmentSpread formatter (Full.FragmentSpread name ds)
- = "..." <> Lazy.Text.fromStrict name <> optempty (directives formatter) ds
+fragmentSpread :: Formatter -> Full.Name -> [Full.Directive] -> Lazy.Text
+fragmentSpread formatter name directives'
+ = "..." <> Lazy.Text.fromStrict name
+ <> optempty (directives formatter) directives'
-inlineFragment :: Formatter -> Full.InlineFragment -> Lazy.Text
-inlineFragment formatter (Full.InlineFragment tc dirs sels)
- = "... on "
+inlineFragment ::
+ Formatter ->
+ Maybe Full.TypeCondition ->
+ [Full.Directive] ->
+ Full.SelectionSet ->
+ Lazy.Text
+inlineFragment formatter tc dirs sels = "... on "
<> Lazy.Text.fromStrict (fold tc)
<> directives formatter dirs
<> eitherFormat formatter " " mempty
@@ -191,7 +211,7 @@ value _ (Full.Variable x) = variable x
value _ (Full.Int x) = Builder.toLazyText $ decimal x
value _ (Full.Float x) = Builder.toLazyText $ realFloat x
value _ (Full.Boolean x) = booleanValue x
-value _ Full.Null = mempty
+value _ Full.Null = "null"
value formatter (Full.String string) = stringValue formatter string
value _ (Full.Enum x) = Lazy.Text.fromStrict x
value formatter (Full.List x) = listValue formatter x
@@ -201,26 +221,40 @@ booleanValue :: Bool -> Lazy.Text
booleanValue True = "true"
booleanValue False = "false"
+quote :: Builder.Builder
+quote = Builder.singleton '\"'
+
+oneLine :: Text -> Builder
+oneLine string = quote <> Text.foldr (mappend . escape) quote string
+
stringValue :: Formatter -> Text -> Lazy.Text
stringValue Minified string = Builder.toLazyText
- $ quote <> Text.foldr (mappend . escape') quote string
- where
- quote = Builder.singleton '\"'
- escape' '\n' = Builder.fromString "\\n"
- escape' char = escape char
-stringValue (Pretty indentation) string = byStringType $ Text.lines string
- where
- byStringType [] = "\"\""
- byStringType [line] = Builder.toLazyText
- $ quote <> Text.foldr (mappend . escape) quote line
- byStringType lines' = "\"\"\"\n"
- <> Lazy.Text.unlines (transformLine <$> lines')
- <> indent indentation
- <> "\"\"\""
- transformLine = (indent (indentation + 1) <>)
- . Lazy.Text.fromStrict
- . Text.replace "\"\"\"" "\\\"\"\""
- quote = Builder.singleton '\"'
+ $ quote <> Text.foldr (mappend . escape) quote string
+stringValue (Pretty indentation) string =
+ if hasEscaped string
+ then stringValue Minified string
+ else Builder.toLazyText $ encoded lines'
+ where
+ isWhiteSpace char = char == ' ' || char == '\t'
+ isNewline char = char == '\n' || char == '\r'
+ hasEscaped = Text.any (not . isAllowed)
+ isAllowed char =
+ char == '\t' || isNewline char || (char >= '\x0020' && char /= '\x007F')
+
+ tripleQuote = Builder.fromText "\"\"\""
+ start = tripleQuote <> Builder.singleton '\n'
+ end = Builder.fromLazyText (indent indentation) <> tripleQuote
+
+ strip = Text.dropWhile isWhiteSpace . Text.dropWhileEnd isWhiteSpace
+ lines' = map Builder.fromText $ Text.split isNewline (Text.replace "\r\n" "\n" $ strip string)
+ encoded [] = oneLine string
+ encoded [_] = oneLine string
+ encoded lines'' = start <> transformLines lines'' <> end
+ transformLines = foldr ((\line acc -> line <> Builder.singleton '\n' <> acc) . transformLine) mempty
+ transformLine line =
+ if Lazy.Text.null (Builder.toLazyText line)
+ then line
+ else Builder.fromLazyText (indent (indentation + 1)) <> line
escape :: Char -> Builder
escape char'
@@ -228,7 +262,9 @@ escape char'
| char' == '\"' = Builder.fromString "\\\""
| char' == '\b' = Builder.fromString "\\b"
| char' == '\f' = Builder.fromString "\\f"
+ | char' == '\n' = Builder.fromString "\\n"
| char' == '\r' = Builder.fromString "\\r"
+ | char' == '\t' = Builder.fromString "\\t"
| char' < '\x0010' = unicode "\\u000" char'
| char' < '\x0020' = unicode "\\u00" char'
| otherwise = Builder.singleton char'