Provide custom Show instances for AST values
This commit is contained in:
parent
a034f2ce4d
commit
b27da54bf4
@ -16,7 +16,7 @@ and this project adheres to
|
|||||||
- `Type.Schema.implementations` contains a map from interfaces and objects to
|
- `Type.Schema.implementations` contains a map from interfaces and objects to
|
||||||
interfaces they implement.
|
interfaces they implement.
|
||||||
- Show instances for GraphQL type definitions in the `Type` modules.
|
- Show instances for GraphQL type definitions in the `Type` modules.
|
||||||
- Custom Show instances for the type representation in the AST.
|
- Custom Show instances for type and value representations in the AST.
|
||||||
|
|
||||||
## [0.11.0.0] - 2020-11-07
|
## [0.11.0.0] - 2020-11-07
|
||||||
### Changed
|
### Changed
|
||||||
|
@ -4,7 +4,7 @@ cabal-version: 2.2
|
|||||||
--
|
--
|
||||||
-- see: https://github.com/sol/hpack
|
-- see: https://github.com/sol/hpack
|
||||||
--
|
--
|
||||||
-- hash: c89b0164372b6e02e4f338d3865dd6bb9dfd1a4475f25d808450480d73f94f91
|
-- hash: 55ede035c74b423b607abac158fb26436e4ea06a43fecdcc2ef42423e63597fc
|
||||||
|
|
||||||
name: graphql
|
name: graphql
|
||||||
version: 0.11.0.0
|
version: 0.11.0.0
|
||||||
@ -78,6 +78,7 @@ test-suite graphql-test
|
|||||||
type: exitcode-stdio-1.0
|
type: exitcode-stdio-1.0
|
||||||
main-is: Spec.hs
|
main-is: Spec.hs
|
||||||
other-modules:
|
other-modules:
|
||||||
|
Language.GraphQL.AST.DocumentSpec
|
||||||
Language.GraphQL.AST.EncoderSpec
|
Language.GraphQL.AST.EncoderSpec
|
||||||
Language.GraphQL.AST.LexerSpec
|
Language.GraphQL.AST.LexerSpec
|
||||||
Language.GraphQL.AST.ParserSpec
|
Language.GraphQL.AST.ParserSpec
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
{-# LANGUAGE DuplicateRecordFields #-}
|
{-# LANGUAGE DuplicateRecordFields #-}
|
||||||
{-# LANGUAGE ExplicitForAll #-}
|
{-# LANGUAGE ExplicitForAll #-}
|
||||||
|
{-# LANGUAGE NamedFieldPuns #-}
|
||||||
{-# LANGUAGE OverloadedStrings #-}
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
{-# LANGUAGE RecordWildCards #-}
|
{-# LANGUAGE RecordWildCards #-}
|
||||||
{-# LANGUAGE Safe #-}
|
{-# LANGUAGE Safe #-}
|
||||||
@ -47,11 +48,15 @@ module Language.GraphQL.AST.Document
|
|||||||
, UnionMemberTypes(..)
|
, UnionMemberTypes(..)
|
||||||
, Value(..)
|
, Value(..)
|
||||||
, VariableDefinition(..)
|
, VariableDefinition(..)
|
||||||
|
, escape
|
||||||
) where
|
) where
|
||||||
|
|
||||||
|
import Data.Char (ord)
|
||||||
import Data.Foldable (toList)
|
import Data.Foldable (toList)
|
||||||
import Data.Int (Int32)
|
import Data.Int (Int32)
|
||||||
|
import Data.List (intercalate)
|
||||||
import Data.List.NonEmpty (NonEmpty)
|
import Data.List.NonEmpty (NonEmpty)
|
||||||
|
import Numeric (showFloat, showHex)
|
||||||
import Data.Text (Text)
|
import Data.Text (Text)
|
||||||
import qualified Data.Text as Text
|
import qualified Data.Text as Text
|
||||||
import Language.GraphQL.AST.DirectiveLocation (DirectiveLocation)
|
import Language.GraphQL.AST.DirectiveLocation (DirectiveLocation)
|
||||||
@ -79,7 +84,10 @@ instance Ord Location where
|
|||||||
data Node a = Node
|
data Node a = Node
|
||||||
{ node :: a
|
{ node :: a
|
||||||
, location :: Location
|
, location :: Location
|
||||||
} deriving (Eq, Show)
|
} deriving Eq
|
||||||
|
|
||||||
|
instance Show a => Show (Node a) where
|
||||||
|
show Node{ node } = show node
|
||||||
|
|
||||||
instance Functor Node where
|
instance Functor Node where
|
||||||
fmap f Node{..} = Node (f node) location
|
fmap f Node{..} = Node (f node) location
|
||||||
@ -218,6 +226,28 @@ type TypeCondition = Name
|
|||||||
|
|
||||||
-- ** Input Values
|
-- ** Input Values
|
||||||
|
|
||||||
|
escape :: Char -> String
|
||||||
|
escape char'
|
||||||
|
| char' == '\\' = "\\\\"
|
||||||
|
| char' == '\"' = "\\\""
|
||||||
|
| char' == '\b' = "\\b"
|
||||||
|
| char' == '\f' = "\\f"
|
||||||
|
| char' == '\n' = "\\n"
|
||||||
|
| char' == '\r' = "\\r"
|
||||||
|
| char' == '\t' = "\\t"
|
||||||
|
| char' < '\x0010' = unicode "\\u000" char'
|
||||||
|
| char' < '\x0020' = unicode "\\u00" char'
|
||||||
|
| otherwise = [char']
|
||||||
|
where
|
||||||
|
unicode prefix uchar = prefix <> (showHex $ ord uchar) ""
|
||||||
|
|
||||||
|
showList' :: Show a => [a] -> String
|
||||||
|
showList' list = "[" ++ intercalate ", " (show <$> list) ++ "]"
|
||||||
|
|
||||||
|
showObject :: Show a => [ObjectField a] -> String
|
||||||
|
showObject fields =
|
||||||
|
"{ " ++ intercalate ", " (show <$> fields) ++ " }"
|
||||||
|
|
||||||
-- | Input value (literal or variable).
|
-- | Input value (literal or variable).
|
||||||
data Value
|
data Value
|
||||||
= Variable Name
|
= Variable Name
|
||||||
@ -229,7 +259,19 @@ data Value
|
|||||||
| Enum Name
|
| Enum Name
|
||||||
| List [Value]
|
| List [Value]
|
||||||
| Object [ObjectField Value]
|
| Object [ObjectField Value]
|
||||||
deriving (Eq, Show)
|
deriving Eq
|
||||||
|
|
||||||
|
instance Show Value where
|
||||||
|
showList = mappend . showList'
|
||||||
|
show (Variable variableName) = '$' : Text.unpack variableName
|
||||||
|
show (Int integer) = show integer
|
||||||
|
show (Float float) = show $ ConstFloat float
|
||||||
|
show (String text) = show $ ConstString text
|
||||||
|
show (Boolean boolean) = show boolean
|
||||||
|
show Null = "null"
|
||||||
|
show (Enum name) = Text.unpack name
|
||||||
|
show (List list) = show list
|
||||||
|
show (Object fields) = showObject fields
|
||||||
|
|
||||||
-- | Constant input value.
|
-- | Constant input value.
|
||||||
data ConstValue
|
data ConstValue
|
||||||
@ -241,7 +283,18 @@ data ConstValue
|
|||||||
| ConstEnum Name
|
| ConstEnum Name
|
||||||
| ConstList [ConstValue]
|
| ConstList [ConstValue]
|
||||||
| ConstObject [ObjectField ConstValue]
|
| ConstObject [ObjectField ConstValue]
|
||||||
deriving (Eq, Show)
|
deriving Eq
|
||||||
|
|
||||||
|
instance Show ConstValue where
|
||||||
|
showList = mappend . showList'
|
||||||
|
show (ConstInt integer) = show integer
|
||||||
|
show (ConstFloat float) = showFloat float mempty
|
||||||
|
show (ConstString text) = "\"" <> Text.foldr (mappend . escape) "\"" text
|
||||||
|
show (ConstBoolean boolean) = show boolean
|
||||||
|
show ConstNull = "null"
|
||||||
|
show (ConstEnum name) = Text.unpack name
|
||||||
|
show (ConstList list) = show list
|
||||||
|
show (ConstObject fields) = showObject fields
|
||||||
|
|
||||||
-- | Key-value pair.
|
-- | Key-value pair.
|
||||||
--
|
--
|
||||||
@ -250,7 +303,10 @@ data ObjectField a = ObjectField
|
|||||||
{ name :: Name
|
{ name :: Name
|
||||||
, value :: Node a
|
, value :: Node a
|
||||||
, location :: Location
|
, location :: Location
|
||||||
} deriving (Eq, Show)
|
} deriving Eq
|
||||||
|
|
||||||
|
instance Show a => Show (ObjectField a) where
|
||||||
|
show ObjectField{..} = Text.unpack name ++ ": " ++ show value
|
||||||
|
|
||||||
instance Functor ObjectField where
|
instance Functor ObjectField where
|
||||||
fmap f ObjectField{..} = ObjectField name (f <$> value) location
|
fmap f ObjectField{..} = ObjectField name (f <$> value) location
|
||||||
|
@ -16,7 +16,6 @@ module Language.GraphQL.AST.Encoder
|
|||||||
, value
|
, value
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import Data.Char (ord)
|
|
||||||
import Data.Foldable (fold)
|
import Data.Foldable (fold)
|
||||||
import qualified Data.List.NonEmpty as NonEmpty
|
import qualified Data.List.NonEmpty as NonEmpty
|
||||||
import Data.Text (Text)
|
import Data.Text (Text)
|
||||||
@ -25,7 +24,7 @@ import qualified Data.Text.Lazy as Lazy (Text)
|
|||||||
import qualified Data.Text.Lazy as Lazy.Text
|
import qualified Data.Text.Lazy as Lazy.Text
|
||||||
import Data.Text.Lazy.Builder (Builder)
|
import Data.Text.Lazy.Builder (Builder)
|
||||||
import qualified Data.Text.Lazy.Builder as Builder
|
import qualified Data.Text.Lazy.Builder as Builder
|
||||||
import Data.Text.Lazy.Builder.Int (decimal, hexadecimal)
|
import Data.Text.Lazy.Builder.Int (decimal)
|
||||||
import Data.Text.Lazy.Builder.RealFloat (realFloat)
|
import Data.Text.Lazy.Builder.RealFloat (realFloat)
|
||||||
import qualified Language.GraphQL.AST.Document as Full
|
import qualified Language.GraphQL.AST.Document as Full
|
||||||
|
|
||||||
@ -234,11 +233,12 @@ quote :: Builder.Builder
|
|||||||
quote = Builder.singleton '\"'
|
quote = Builder.singleton '\"'
|
||||||
|
|
||||||
oneLine :: Text -> Builder
|
oneLine :: Text -> Builder
|
||||||
oneLine string = quote <> Text.foldr (mappend . escape) quote string
|
oneLine string = quote <> Text.foldr merge quote string
|
||||||
|
where
|
||||||
|
merge = mappend . Builder.fromString . Full.escape
|
||||||
|
|
||||||
stringValue :: Formatter -> Text -> Lazy.Text
|
stringValue :: Formatter -> Text -> Lazy.Text
|
||||||
stringValue Minified string = Builder.toLazyText
|
stringValue Minified string = Builder.toLazyText $ oneLine string
|
||||||
$ quote <> Text.foldr (mappend . escape) quote string
|
|
||||||
stringValue (Pretty indentation) string =
|
stringValue (Pretty indentation) string =
|
||||||
if hasEscaped string
|
if hasEscaped string
|
||||||
then stringValue Minified string
|
then stringValue Minified string
|
||||||
@ -266,21 +266,6 @@ stringValue (Pretty indentation) string =
|
|||||||
= Builder.fromLazyText (indent (indentation + 1))
|
= Builder.fromLazyText (indent (indentation + 1))
|
||||||
<> line' <> newline <> acc
|
<> line' <> newline <> acc
|
||||||
|
|
||||||
escape :: Char -> Builder
|
|
||||||
escape char'
|
|
||||||
| char' == '\\' = Builder.fromString "\\\\"
|
|
||||||
| 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'
|
|
||||||
where
|
|
||||||
unicode prefix = mappend (Builder.fromString prefix) . (hexadecimal . ord)
|
|
||||||
|
|
||||||
listValue :: Formatter -> [Full.Value] -> Lazy.Text
|
listValue :: Formatter -> [Full.Value] -> Lazy.Text
|
||||||
listValue formatter = bracketsCommas formatter $ value formatter
|
listValue formatter = bracketsCommas formatter $ value formatter
|
||||||
|
|
||||||
|
20
tests/Language/GraphQL/AST/DocumentSpec.hs
Normal file
20
tests/Language/GraphQL/AST/DocumentSpec.hs
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
{-# LANGUAGE DuplicateRecordFields #-}
|
||||||
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
|
module Language.GraphQL.AST.DocumentSpec
|
||||||
|
( spec
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Language.GraphQL.AST.Document
|
||||||
|
import Test.Hspec (Spec, describe, it, shouldBe)
|
||||||
|
|
||||||
|
spec :: Spec
|
||||||
|
spec = do
|
||||||
|
describe "Document" $ do
|
||||||
|
it "shows objects" $
|
||||||
|
let zero = Location 0 0
|
||||||
|
object = ConstObject
|
||||||
|
[ ObjectField "field1" (Node (ConstFloat 1.2) zero) zero
|
||||||
|
, ObjectField "field2" (Node ConstNull zero) zero
|
||||||
|
]
|
||||||
|
expected = "{ field1: 1.2, field2: null }"
|
||||||
|
in show object `shouldBe` expected
|
@ -851,7 +851,7 @@ spec =
|
|||||||
|]
|
|]
|
||||||
expected = Error
|
expected = Error
|
||||||
{ message =
|
{ message =
|
||||||
"Value ConstInt 3 cannot be coerced to type \"Boolean\"."
|
"Value 3 cannot be coerced to type \"Boolean\"."
|
||||||
, locations = [AST.Location 4 48]
|
, locations = [AST.Location 4 48]
|
||||||
}
|
}
|
||||||
in validate queryString `shouldBe` [expected]
|
in validate queryString `shouldBe` [expected]
|
||||||
|
Loading…
Reference in New Issue
Block a user