diff --git a/.editorconfig b/.editorconfig index 3a7f70f0..b8228a5e 100644 --- a/.editorconfig +++ b/.editorconfig @@ -253,7 +253,7 @@ dotnet_naming_style.end_in_async_style.required_prefix = dotnet_naming_style.end_in_async_style.required_suffix = Async # dotnet_naming_rule..severity = -dotnet_naming_rule.async_methods_end_in_async.severity = warning +dotnet_naming_rule.async_methods_end_in_async.severity = suggestion # Remove unnecessary import https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0005 dotnet_diagnostic.IDE0005.severity = warning diff --git a/README.md b/README.md index 862e44f7..f91af82d 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ The parser from this library is used in [GraphQL for .NET](https://github.com/gr Preview versions of this package are available on [GitHub Packages](https://github.com/orgs/graphql-dotnet/packages?repo_name=parser). -## Lexer +## 1. Lexer Generates token based on input text. Lexer takes advantage of `ReadOnlyMemory` and in most cases does not allocate memory on the managed heap at all. @@ -38,7 +38,7 @@ var token = Lexer.Lex("\"str\""); Lex method always returns the first token it finds. In this case case the result would look like following. ![lexer example](assets/lexer-example.png) -## Parser +## 2. Parser Parses provided GraphQL expression into AST (abstract syntax tree). Parser also takes advantage of `ReadOnlyMemory` but still allocates memory for AST. @@ -61,51 +61,60 @@ By default `ParserOptions.Ignore` is `IgnoreOptions.IgnoreComments` to improve p If you don't need information about tokens locations in the source document, then use `IgnoreOptions.IgnoreCommentsAndLocations`. This will maximize the saving of memory allocated in the managed heap for AST. -### Example of json representation of the resulting AST +## 3. INodeVisitor -```json +`INodeVisitor` provides API to traverse AST of the parsed GraphQL document. +Default implementation of this interface is `DefaultNodeVisitor` that +traverses all AST nodes of the provided one. You can inherit from it and +implement your own AST processing algorithm. + +For printing SDL from AST, you can use `SDLWriter` visitor. +This is a highly optimized visitor for asynchronous non-blocking SDL output +into provided `TextWriter`. In the majority of cases it does not allocate +memory in the managed heap at all. + +You can also find a `StructureWriter` visitor that prints AST +into the provided `TextWriter` as a hierarchy of node types. It can be useful +when debugging for better understanding the AST structure. +Consider GraphQL document + +```graphql +query a { name age } +``` + +After `StructureWriter` processing the output text will be + +``` +Document + OperationDefinition + Name [a] + SelectionSet + Field + Name [name] + Field + Name [age] +``` + +### Usage + +```c# +public class Context : IWriteContext { - "Definitions": [{ - "Directives": [], - "Kind": 2, - "Name": null, - "Operation": 0, - "SelectionSet": { - "Kind": 5, - "Selections": [{ - "Alias": null, - "Arguments": [], - "Directives": [], - "Kind": 6, - "Name": { - "Kind": 0, - "Value": "field", - "Location": { - "End": 50, - "Start": 31 - } - }, - "SelectionSet": null, - "Location": { - "End": 50, - "Start": 31 - } - }], - "Location": { - "End": 50, - "Start": 13 - } - }, - "VariableDefinitions": null, - "Location": { - "End": 50, - "Start": 13 - } - }], - "Kind": 1, - "Location": { - "End": 50, - "Start": 13 - } + public TextWriter Writer { get; set; } = new StringWriter(); + + public Stack Parents { get; set; } = new Stack(); + + public CancellationToken CancellationToken { get; set; } +} + +public static void Parse(string text) +{ + var document = Parser.Parse(text); + + var context = new Context(); + var visitor = new SDLWriter() + await visitor.Visit(document, context); + var rendered = context.Writer.ToString(); + Console.WriteLine(rendered); } ``` diff --git a/src/GraphQLParser.ApiTests/GraphQLParser.approved.txt b/src/GraphQLParser.ApiTests/GraphQLParser.approved.txt index d3a87b0a..bdbab7ba 100644 --- a/src/GraphQLParser.ApiTests/GraphQLParser.approved.txt +++ b/src/GraphQLParser.ApiTests/GraphQLParser.approved.txt @@ -48,6 +48,14 @@ namespace GraphQLParser.AST DirectiveDefinition = 36, Comment = 37, Description = 38, + TypeCondition = 39, + Alias = 40, + } + public class GraphQLAlias : GraphQLParser.AST.ASTNode + { + public GraphQLAlias() { } + public override GraphQLParser.AST.ASTNodeKind Kind { get; } + public GraphQLParser.AST.GraphQLName? Name { get; set; } } public class GraphQLArgument : GraphQLParser.AST.ASTNode, GraphQLParser.AST.INamedNode { @@ -68,7 +76,7 @@ namespace GraphQLParser.AST public override GraphQLParser.AST.ASTNodeKind Kind { get; } public GraphQLParser.ROM Value { get; set; } } - public class GraphQLDirective : GraphQLParser.AST.ASTNode, GraphQLParser.AST.INamedNode + public class GraphQLDirective : GraphQLParser.AST.ASTNode, GraphQLParser.AST.IHasArgumentsNode, GraphQLParser.AST.INamedNode { public GraphQLDirective() { } public System.Collections.Generic.List? Arguments { get; set; } @@ -105,10 +113,10 @@ namespace GraphQLParser.AST public System.Collections.Generic.List? Directives { get; set; } public override GraphQLParser.AST.ASTNodeKind Kind { get; } } - public class GraphQLField : GraphQLParser.AST.ASTNode, GraphQLParser.AST.IHasDirectivesNode, GraphQLParser.AST.INamedNode + public class GraphQLField : GraphQLParser.AST.ASTNode, GraphQLParser.AST.IHasArgumentsNode, GraphQLParser.AST.IHasDirectivesNode, GraphQLParser.AST.INamedNode { public GraphQLField() { } - public GraphQLParser.AST.GraphQLName? Alias { get; set; } + public GraphQLParser.AST.GraphQLAlias? Alias { get; set; } public System.Collections.Generic.List? Arguments { get; set; } public System.Collections.Generic.List? Directives { get; set; } public override GraphQLParser.AST.ASTNodeKind Kind { get; } @@ -142,7 +150,7 @@ namespace GraphQLParser.AST public System.Collections.Generic.List? Directives { get; set; } public override GraphQLParser.AST.ASTNodeKind Kind { get; } public GraphQLParser.AST.GraphQLSelectionSet? SelectionSet { get; set; } - public GraphQLParser.AST.GraphQLNamedType? TypeCondition { get; set; } + public GraphQLParser.AST.GraphQLTypeCondition? TypeCondition { get; set; } } public class GraphQLInputObjectTypeDefinition : GraphQLParser.AST.GraphQLTypeDefinition, GraphQLParser.AST.IHasDirectivesNode { @@ -159,7 +167,7 @@ namespace GraphQLParser.AST public override GraphQLParser.AST.ASTNodeKind Kind { get; } public GraphQLParser.AST.GraphQLType? Type { get; set; } } - public class GraphQLInterfaceTypeDefinition : GraphQLParser.AST.GraphQLTypeDefinition, GraphQLParser.AST.IHasDirectivesNode + public class GraphQLInterfaceTypeDefinition : GraphQLParser.AST.GraphQLTypeDefinition, GraphQLParser.AST.IHasDirectivesNode, GraphQLParser.AST.IHasInterfacesNode { public GraphQLInterfaceTypeDefinition() { } public System.Collections.Generic.List? Directives { get; set; } @@ -176,7 +184,7 @@ namespace GraphQLParser.AST } public class GraphQLListValue : GraphQLParser.AST.GraphQLValue { - public GraphQLListValue(GraphQLParser.AST.ASTNodeKind kind) { } + public GraphQLListValue() { } public GraphQLParser.ROM AstValue { get; set; } public override GraphQLParser.AST.ASTNodeKind Kind { get; } public System.Collections.Generic.List? Values { get; set; } @@ -221,7 +229,7 @@ namespace GraphQLParser.AST public GraphQLParser.AST.GraphQLName? Name { get; set; } public GraphQLParser.AST.GraphQLValue? Value { get; set; } } - public class GraphQLObjectTypeDefinition : GraphQLParser.AST.GraphQLTypeDefinition, GraphQLParser.AST.IHasDirectivesNode + public class GraphQLObjectTypeDefinition : GraphQLParser.AST.GraphQLTypeDefinition, GraphQLParser.AST.IHasDirectivesNode, GraphQLParser.AST.IHasInterfacesNode { public GraphQLObjectTypeDefinition() { } public System.Collections.Generic.List? Directives { get; set; } @@ -283,6 +291,12 @@ namespace GraphQLParser.AST { protected GraphQLType() { } } + public class GraphQLTypeCondition : GraphQLParser.AST.ASTNode + { + public GraphQLTypeCondition() { } + public override GraphQLParser.AST.ASTNodeKind Kind { get; } + public GraphQLParser.AST.GraphQLNamedType? Type { get; set; } + } public abstract class GraphQLTypeDefinition : GraphQLParser.AST.ASTNode, GraphQLParser.AST.IHasDescriptionNode, GraphQLParser.AST.INamedNode { protected GraphQLTypeDefinition() { } @@ -312,14 +326,19 @@ namespace GraphQLParser.AST public override GraphQLParser.AST.ASTNodeKind Kind { get; } public GraphQLParser.AST.GraphQLName? Name { get; set; } } - public class GraphQLVariableDefinition : GraphQLParser.AST.ASTNode + public class GraphQLVariableDefinition : GraphQLParser.AST.ASTNode, GraphQLParser.AST.IHasDirectivesNode { public GraphQLVariableDefinition() { } - public object? DefaultValue { get; set; } + public GraphQLParser.AST.GraphQLValue? DefaultValue { get; set; } + public System.Collections.Generic.List? Directives { get; set; } public override GraphQLParser.AST.ASTNodeKind Kind { get; } public GraphQLParser.AST.GraphQLType? Type { get; set; } public GraphQLParser.AST.GraphQLVariable? Variable { get; set; } } + public interface IHasArgumentsNode + { + System.Collections.Generic.List? Arguments { get; set; } + } public interface IHasDescriptionNode { GraphQLParser.AST.GraphQLDescription? Description { get; set; } @@ -328,6 +347,10 @@ namespace GraphQLParser.AST { System.Collections.Generic.List? Directives { get; set; } } + public interface IHasInterfacesNode + { + System.Collections.Generic.List? Interfaces { get; set; } + } public interface INamedNode { GraphQLParser.AST.GraphQLName? Name { get; set; } @@ -351,42 +374,6 @@ namespace GraphQLParser.Exceptions } namespace GraphQLParser { - public class GraphQLAstVisitor - { - public GraphQLAstVisitor() { } - protected System.Collections.Generic.IDictionary Fragments { get; } - public virtual GraphQLParser.AST.GraphQLName BeginVisitAlias(GraphQLParser.AST.GraphQLName alias) { } - public virtual GraphQLParser.AST.GraphQLArgument BeginVisitArgument(GraphQLParser.AST.GraphQLArgument argument) { } - public virtual System.Collections.Generic.IEnumerable BeginVisitArguments(System.Collections.Generic.IEnumerable arguments) { } - public virtual GraphQLParser.AST.GraphQLScalarValue BeginVisitBooleanValue(GraphQLParser.AST.GraphQLScalarValue value) { } - public virtual GraphQLParser.AST.GraphQLDirective BeginVisitDirective(GraphQLParser.AST.GraphQLDirective directive) { } - public virtual System.Collections.Generic.IEnumerable BeginVisitDirectives(System.Collections.Generic.IEnumerable directives) { } - public virtual GraphQLParser.AST.GraphQLScalarValue BeginVisitEnumValue(GraphQLParser.AST.GraphQLScalarValue value) { } - public virtual GraphQLParser.AST.GraphQLField BeginVisitField(GraphQLParser.AST.GraphQLField selection) { } - public virtual GraphQLParser.AST.GraphQLScalarValue BeginVisitFloatValue(GraphQLParser.AST.GraphQLScalarValue value) { } - public virtual GraphQLParser.AST.GraphQLFragmentDefinition BeginVisitFragmentDefinition(GraphQLParser.AST.GraphQLFragmentDefinition node) { } - public virtual GraphQLParser.AST.GraphQLFragmentSpread BeginVisitFragmentSpread(GraphQLParser.AST.GraphQLFragmentSpread fragmentSpread) { } - public virtual GraphQLParser.AST.GraphQLInlineFragment BeginVisitInlineFragment(GraphQLParser.AST.GraphQLInlineFragment inlineFragment) { } - public virtual GraphQLParser.AST.GraphQLScalarValue BeginVisitIntValue(GraphQLParser.AST.GraphQLScalarValue value) { } - public virtual GraphQLParser.AST.GraphQLName BeginVisitName(GraphQLParser.AST.GraphQLName name) { } - public virtual GraphQLParser.AST.GraphQLNamedType BeginVisitNamedType(GraphQLParser.AST.GraphQLNamedType typeCondition) { } - public virtual GraphQLParser.AST.ASTNode? BeginVisitNode(GraphQLParser.AST.ASTNode? node) { } - public virtual GraphQLParser.AST.GraphQLObjectField BeginVisitObjectField(GraphQLParser.AST.GraphQLObjectField node) { } - public virtual GraphQLParser.AST.GraphQLObjectValue BeginVisitObjectValue(GraphQLParser.AST.GraphQLObjectValue node) { } - public virtual GraphQLParser.AST.GraphQLOperationDefinition BeginVisitOperationDefinition(GraphQLParser.AST.GraphQLOperationDefinition definition) { } - public virtual GraphQLParser.AST.GraphQLSelectionSet BeginVisitSelectionSet(GraphQLParser.AST.GraphQLSelectionSet selectionSet) { } - public virtual GraphQLParser.AST.GraphQLScalarValue BeginVisitStringValue(GraphQLParser.AST.GraphQLScalarValue value) { } - public virtual GraphQLParser.AST.GraphQLVariable BeginVisitVariable(GraphQLParser.AST.GraphQLVariable variable) { } - public virtual GraphQLParser.AST.GraphQLVariableDefinition BeginVisitVariableDefinition(GraphQLParser.AST.GraphQLVariableDefinition node) { } - public virtual System.Collections.Generic.IEnumerable BeginVisitVariableDefinitions(System.Collections.Generic.IEnumerable variableDefinitions) { } - public virtual GraphQLParser.AST.GraphQLArgument EndVisitArgument(GraphQLParser.AST.GraphQLArgument argument) { } - public virtual GraphQLParser.AST.GraphQLField EndVisitField(GraphQLParser.AST.GraphQLField selection) { } - public virtual GraphQLParser.AST.GraphQLListValue EndVisitListValue(GraphQLParser.AST.GraphQLListValue node) { } - public virtual GraphQLParser.AST.GraphQLObjectValue EndVisitObjectValue(GraphQLParser.AST.GraphQLObjectValue node) { } - public virtual GraphQLParser.AST.GraphQLOperationDefinition EndVisitOperationDefinition(GraphQLParser.AST.GraphQLOperationDefinition definition) { } - public virtual GraphQLParser.AST.GraphQLVariable EndVisitVariable(GraphQLParser.AST.GraphQLVariable variable) { } - public virtual void Visit(GraphQLParser.AST.GraphQLDocument ast) { } - } [System.Flags] public enum IgnoreOptions { @@ -472,4 +459,172 @@ namespace GraphQLParser UNKNOWN = 20, AMPERSAND = 21, } +} +namespace GraphQLParser.Visitors +{ + public class DefaultNodeVisitor : GraphQLParser.Visitors.INodeVisitor + where TContext : GraphQLParser.Visitors.INodeVisitorContext + { + public DefaultNodeVisitor() { } + public virtual System.Threading.Tasks.ValueTask Visit(GraphQLParser.AST.ASTNode? node, TContext context) { } + protected System.Threading.Tasks.ValueTask Visit(System.Collections.Generic.List? nodes, TContext context) + where T : GraphQLParser.AST.ASTNode { } + public virtual System.Threading.Tasks.ValueTask VisitAlias(GraphQLParser.AST.GraphQLAlias alias, TContext context) { } + public virtual System.Threading.Tasks.ValueTask VisitArgument(GraphQLParser.AST.GraphQLArgument argument, TContext context) { } + public virtual System.Threading.Tasks.ValueTask VisitBooleanValue(GraphQLParser.AST.GraphQLScalarValue booleanValue, TContext context) { } + public virtual System.Threading.Tasks.ValueTask VisitComment(GraphQLParser.AST.GraphQLComment comment, TContext context) { } + public virtual System.Threading.Tasks.ValueTask VisitDescription(GraphQLParser.AST.GraphQLDescription description, TContext context) { } + public virtual System.Threading.Tasks.ValueTask VisitDirective(GraphQLParser.AST.GraphQLDirective directive, TContext context) { } + public virtual System.Threading.Tasks.ValueTask VisitDirectiveDefinition(GraphQLParser.AST.GraphQLDirectiveDefinition directiveDefinition, TContext context) { } + public virtual System.Threading.Tasks.ValueTask VisitDocument(GraphQLParser.AST.GraphQLDocument document, TContext context) { } + public virtual System.Threading.Tasks.ValueTask VisitEnumTypeDefinition(GraphQLParser.AST.GraphQLEnumTypeDefinition enumTypeDefinition, TContext context) { } + public virtual System.Threading.Tasks.ValueTask VisitEnumValue(GraphQLParser.AST.GraphQLScalarValue enumValue, TContext context) { } + public virtual System.Threading.Tasks.ValueTask VisitEnumValueDefinition(GraphQLParser.AST.GraphQLEnumValueDefinition enumValueDefinition, TContext context) { } + public virtual System.Threading.Tasks.ValueTask VisitField(GraphQLParser.AST.GraphQLField field, TContext context) { } + public virtual System.Threading.Tasks.ValueTask VisitFieldDefinition(GraphQLParser.AST.GraphQLFieldDefinition fieldDefinition, TContext context) { } + public virtual System.Threading.Tasks.ValueTask VisitFloatValue(GraphQLParser.AST.GraphQLScalarValue floatValue, TContext context) { } + public virtual System.Threading.Tasks.ValueTask VisitFragmentDefinition(GraphQLParser.AST.GraphQLFragmentDefinition fragmentDefinition, TContext context) { } + public virtual System.Threading.Tasks.ValueTask VisitFragmentSpread(GraphQLParser.AST.GraphQLFragmentSpread fragmentSpread, TContext context) { } + public virtual System.Threading.Tasks.ValueTask VisitInlineFragment(GraphQLParser.AST.GraphQLInlineFragment inlineFragment, TContext context) { } + public virtual System.Threading.Tasks.ValueTask VisitInputObjectTypeDefinition(GraphQLParser.AST.GraphQLInputObjectTypeDefinition inputObjectTypeDefinition, TContext context) { } + public virtual System.Threading.Tasks.ValueTask VisitInputValueDefinition(GraphQLParser.AST.GraphQLInputValueDefinition inputValueDefinition, TContext context) { } + public virtual System.Threading.Tasks.ValueTask VisitIntValue(GraphQLParser.AST.GraphQLScalarValue intValue, TContext context) { } + public virtual System.Threading.Tasks.ValueTask VisitInterfaceTypeDefinition(GraphQLParser.AST.GraphQLInterfaceTypeDefinition interfaceTypeDefinition, TContext context) { } + public virtual System.Threading.Tasks.ValueTask VisitListType(GraphQLParser.AST.GraphQLListType listType, TContext context) { } + public virtual System.Threading.Tasks.ValueTask VisitListValue(GraphQLParser.AST.GraphQLListValue listValue, TContext context) { } + public virtual System.Threading.Tasks.ValueTask VisitName(GraphQLParser.AST.GraphQLName name, TContext context) { } + public virtual System.Threading.Tasks.ValueTask VisitNamedType(GraphQLParser.AST.GraphQLNamedType namedType, TContext context) { } + public virtual System.Threading.Tasks.ValueTask VisitNonNullType(GraphQLParser.AST.GraphQLNonNullType nonNullType, TContext context) { } + public virtual System.Threading.Tasks.ValueTask VisitNullValue(GraphQLParser.AST.GraphQLScalarValue nullValue, TContext context) { } + public virtual System.Threading.Tasks.ValueTask VisitObjectField(GraphQLParser.AST.GraphQLObjectField objectField, TContext context) { } + public virtual System.Threading.Tasks.ValueTask VisitObjectTypeDefinition(GraphQLParser.AST.GraphQLObjectTypeDefinition objectTypeDefinition, TContext context) { } + public virtual System.Threading.Tasks.ValueTask VisitObjectValue(GraphQLParser.AST.GraphQLObjectValue objectValue, TContext context) { } + public virtual System.Threading.Tasks.ValueTask VisitOperationDefinition(GraphQLParser.AST.GraphQLOperationDefinition operationDefinition, TContext context) { } + public virtual System.Threading.Tasks.ValueTask VisitRootOperationTypeDefinition(GraphQLParser.AST.GraphQLRootOperationTypeDefinition rootOperationTypeDefinition, TContext context) { } + public virtual System.Threading.Tasks.ValueTask VisitScalarTypeDefinition(GraphQLParser.AST.GraphQLScalarTypeDefinition scalarTypeDefinition, TContext context) { } + public virtual System.Threading.Tasks.ValueTask VisitSchemaDefinition(GraphQLParser.AST.GraphQLSchemaDefinition schemaDefinition, TContext context) { } + public virtual System.Threading.Tasks.ValueTask VisitSelectionSet(GraphQLParser.AST.GraphQLSelectionSet selectionSet, TContext context) { } + public virtual System.Threading.Tasks.ValueTask VisitStringValue(GraphQLParser.AST.GraphQLScalarValue stringValue, TContext context) { } + public virtual System.Threading.Tasks.ValueTask VisitTypeCondition(GraphQLParser.AST.GraphQLTypeCondition typeCondition, TContext context) { } + public virtual System.Threading.Tasks.ValueTask VisitUnionTypeDefinition(GraphQLParser.AST.GraphQLUnionTypeDefinition unionTypeDefinition, TContext context) { } + public virtual System.Threading.Tasks.ValueTask VisitVariable(GraphQLParser.AST.GraphQLVariable variable, TContext context) { } + public virtual System.Threading.Tasks.ValueTask VisitVariableDefinition(GraphQLParser.AST.GraphQLVariableDefinition variableDefinition, TContext context) { } + } + public interface INodeVisitorContext + { + System.Threading.CancellationToken CancellationToken { get; } + } + public interface INodeVisitor + where TContext : GraphQLParser.Visitors.INodeVisitorContext + { + System.Threading.Tasks.ValueTask VisitAlias(GraphQLParser.AST.GraphQLAlias alias, TContext context); + System.Threading.Tasks.ValueTask VisitArgument(GraphQLParser.AST.GraphQLArgument argument, TContext context); + System.Threading.Tasks.ValueTask VisitBooleanValue(GraphQLParser.AST.GraphQLScalarValue booleanValue, TContext context); + System.Threading.Tasks.ValueTask VisitComment(GraphQLParser.AST.GraphQLComment comment, TContext context); + System.Threading.Tasks.ValueTask VisitDescription(GraphQLParser.AST.GraphQLDescription description, TContext context); + System.Threading.Tasks.ValueTask VisitDirective(GraphQLParser.AST.GraphQLDirective directive, TContext context); + System.Threading.Tasks.ValueTask VisitDirectiveDefinition(GraphQLParser.AST.GraphQLDirectiveDefinition directiveDefinition, TContext context); + System.Threading.Tasks.ValueTask VisitDocument(GraphQLParser.AST.GraphQLDocument document, TContext context); + System.Threading.Tasks.ValueTask VisitEnumTypeDefinition(GraphQLParser.AST.GraphQLEnumTypeDefinition enumTypeDefinition, TContext context); + System.Threading.Tasks.ValueTask VisitEnumValue(GraphQLParser.AST.GraphQLScalarValue enumValue, TContext context); + System.Threading.Tasks.ValueTask VisitEnumValueDefinition(GraphQLParser.AST.GraphQLEnumValueDefinition enumValueDefinition, TContext context); + System.Threading.Tasks.ValueTask VisitField(GraphQLParser.AST.GraphQLField field, TContext context); + System.Threading.Tasks.ValueTask VisitFieldDefinition(GraphQLParser.AST.GraphQLFieldDefinition fieldDefinition, TContext context); + System.Threading.Tasks.ValueTask VisitFloatValue(GraphQLParser.AST.GraphQLScalarValue floatValue, TContext context); + System.Threading.Tasks.ValueTask VisitFragmentDefinition(GraphQLParser.AST.GraphQLFragmentDefinition fragmentDefinition, TContext context); + System.Threading.Tasks.ValueTask VisitFragmentSpread(GraphQLParser.AST.GraphQLFragmentSpread fragmentSpread, TContext context); + System.Threading.Tasks.ValueTask VisitInlineFragment(GraphQLParser.AST.GraphQLInlineFragment inlineFragment, TContext context); + System.Threading.Tasks.ValueTask VisitInputObjectTypeDefinition(GraphQLParser.AST.GraphQLInputObjectTypeDefinition inputObjectTypeDefinition, TContext context); + System.Threading.Tasks.ValueTask VisitInputValueDefinition(GraphQLParser.AST.GraphQLInputValueDefinition inputValueDefinition, TContext context); + System.Threading.Tasks.ValueTask VisitIntValue(GraphQLParser.AST.GraphQLScalarValue intValue, TContext context); + System.Threading.Tasks.ValueTask VisitInterfaceTypeDefinition(GraphQLParser.AST.GraphQLInterfaceTypeDefinition interfaceTypeDefinition, TContext context); + System.Threading.Tasks.ValueTask VisitListType(GraphQLParser.AST.GraphQLListType listType, TContext context); + System.Threading.Tasks.ValueTask VisitListValue(GraphQLParser.AST.GraphQLListValue listValue, TContext context); + System.Threading.Tasks.ValueTask VisitName(GraphQLParser.AST.GraphQLName name, TContext context); + System.Threading.Tasks.ValueTask VisitNamedType(GraphQLParser.AST.GraphQLNamedType namedType, TContext context); + System.Threading.Tasks.ValueTask VisitNonNullType(GraphQLParser.AST.GraphQLNonNullType nonNullType, TContext context); + System.Threading.Tasks.ValueTask VisitNullValue(GraphQLParser.AST.GraphQLScalarValue nullValue, TContext context); + System.Threading.Tasks.ValueTask VisitObjectField(GraphQLParser.AST.GraphQLObjectField objectField, TContext context); + System.Threading.Tasks.ValueTask VisitObjectTypeDefinition(GraphQLParser.AST.GraphQLObjectTypeDefinition objectTypeDefinition, TContext context); + System.Threading.Tasks.ValueTask VisitObjectValue(GraphQLParser.AST.GraphQLObjectValue objectValue, TContext context); + System.Threading.Tasks.ValueTask VisitOperationDefinition(GraphQLParser.AST.GraphQLOperationDefinition operationDefinition, TContext context); + System.Threading.Tasks.ValueTask VisitRootOperationTypeDefinition(GraphQLParser.AST.GraphQLRootOperationTypeDefinition rootOperationTypeDefinition, TContext context); + System.Threading.Tasks.ValueTask VisitScalarTypeDefinition(GraphQLParser.AST.GraphQLScalarTypeDefinition scalarTypeDefinition, TContext context); + System.Threading.Tasks.ValueTask VisitSchemaDefinition(GraphQLParser.AST.GraphQLSchemaDefinition schemaDefinition, TContext context); + System.Threading.Tasks.ValueTask VisitSelectionSet(GraphQLParser.AST.GraphQLSelectionSet selectionSet, TContext context); + System.Threading.Tasks.ValueTask VisitStringValue(GraphQLParser.AST.GraphQLScalarValue stringValue, TContext context); + System.Threading.Tasks.ValueTask VisitTypeCondition(GraphQLParser.AST.GraphQLTypeCondition typeCondition, TContext context); + System.Threading.Tasks.ValueTask VisitUnionTypeDefinition(GraphQLParser.AST.GraphQLUnionTypeDefinition unionTypeDefinition, TContext context); + System.Threading.Tasks.ValueTask VisitVariable(GraphQLParser.AST.GraphQLVariable variable, TContext context); + System.Threading.Tasks.ValueTask VisitVariableDefinition(GraphQLParser.AST.GraphQLVariableDefinition variableDefinition, TContext context); + } + public interface IWriteContext : GraphQLParser.Visitors.INodeVisitorContext + { + System.Collections.Generic.Stack Parents { get; } + System.IO.TextWriter Writer { get; } + } + public class SDLWriter : GraphQLParser.Visitors.DefaultNodeVisitor + where TContext : GraphQLParser.Visitors.IWriteContext + { + public SDLWriter() { } + public override System.Threading.Tasks.ValueTask Visit(GraphQLParser.AST.ASTNode? node, TContext context) { } + public override System.Threading.Tasks.ValueTask VisitAlias(GraphQLParser.AST.GraphQLAlias alias, TContext context) { } + public override System.Threading.Tasks.ValueTask VisitArgument(GraphQLParser.AST.GraphQLArgument argument, TContext context) { } + public override System.Threading.Tasks.ValueTask VisitBooleanValue(GraphQLParser.AST.GraphQLScalarValue booleanValue, TContext context) { } + public override System.Threading.Tasks.ValueTask VisitComment(GraphQLParser.AST.GraphQLComment comment, TContext context) { } + public override System.Threading.Tasks.ValueTask VisitDescription(GraphQLParser.AST.GraphQLDescription description, TContext context) { } + public override System.Threading.Tasks.ValueTask VisitDirective(GraphQLParser.AST.GraphQLDirective directive, TContext context) { } + public override System.Threading.Tasks.ValueTask VisitDirectiveDefinition(GraphQLParser.AST.GraphQLDirectiveDefinition directiveDefinition, TContext context) { } + public override System.Threading.Tasks.ValueTask VisitDocument(GraphQLParser.AST.GraphQLDocument document, TContext context) { } + public override System.Threading.Tasks.ValueTask VisitEnumTypeDefinition(GraphQLParser.AST.GraphQLEnumTypeDefinition enumTypeDefinition, TContext context) { } + public override System.Threading.Tasks.ValueTask VisitEnumValue(GraphQLParser.AST.GraphQLScalarValue enumValue, TContext context) { } + public override System.Threading.Tasks.ValueTask VisitEnumValueDefinition(GraphQLParser.AST.GraphQLEnumValueDefinition enumValueDefinition, TContext context) { } + public override System.Threading.Tasks.ValueTask VisitField(GraphQLParser.AST.GraphQLField field, TContext context) { } + public override System.Threading.Tasks.ValueTask VisitFieldDefinition(GraphQLParser.AST.GraphQLFieldDefinition fieldDefinition, TContext context) { } + public override System.Threading.Tasks.ValueTask VisitFloatValue(GraphQLParser.AST.GraphQLScalarValue floatValue, TContext context) { } + public override System.Threading.Tasks.ValueTask VisitFragmentDefinition(GraphQLParser.AST.GraphQLFragmentDefinition fragmentDefinition, TContext context) { } + public override System.Threading.Tasks.ValueTask VisitFragmentSpread(GraphQLParser.AST.GraphQLFragmentSpread fragmentSpread, TContext context) { } + public override System.Threading.Tasks.ValueTask VisitInlineFragment(GraphQLParser.AST.GraphQLInlineFragment inlineFragment, TContext context) { } + public override System.Threading.Tasks.ValueTask VisitInputObjectTypeDefinition(GraphQLParser.AST.GraphQLInputObjectTypeDefinition inputObjectTypeDefinition, TContext context) { } + public override System.Threading.Tasks.ValueTask VisitInputValueDefinition(GraphQLParser.AST.GraphQLInputValueDefinition inputValueDefinition, TContext context) { } + public override System.Threading.Tasks.ValueTask VisitIntValue(GraphQLParser.AST.GraphQLScalarValue intValue, TContext context) { } + public override System.Threading.Tasks.ValueTask VisitInterfaceTypeDefinition(GraphQLParser.AST.GraphQLInterfaceTypeDefinition interfaceTypeDefinition, TContext context) { } + public override System.Threading.Tasks.ValueTask VisitListType(GraphQLParser.AST.GraphQLListType listType, TContext context) { } + public override System.Threading.Tasks.ValueTask VisitListValue(GraphQLParser.AST.GraphQLListValue listValue, TContext context) { } + public override System.Threading.Tasks.ValueTask VisitName(GraphQLParser.AST.GraphQLName name, TContext context) { } + public override System.Threading.Tasks.ValueTask VisitNamedType(GraphQLParser.AST.GraphQLNamedType namedType, TContext context) { } + public override System.Threading.Tasks.ValueTask VisitNonNullType(GraphQLParser.AST.GraphQLNonNullType nonNullType, TContext context) { } + public override System.Threading.Tasks.ValueTask VisitNullValue(GraphQLParser.AST.GraphQLScalarValue nullValue, TContext context) { } + public override System.Threading.Tasks.ValueTask VisitObjectField(GraphQLParser.AST.GraphQLObjectField objectField, TContext context) { } + public override System.Threading.Tasks.ValueTask VisitObjectTypeDefinition(GraphQLParser.AST.GraphQLObjectTypeDefinition objectTypeDefinition, TContext context) { } + public override System.Threading.Tasks.ValueTask VisitObjectValue(GraphQLParser.AST.GraphQLObjectValue objectValue, TContext context) { } + public override System.Threading.Tasks.ValueTask VisitOperationDefinition(GraphQLParser.AST.GraphQLOperationDefinition operationDefinition, TContext context) { } + public override System.Threading.Tasks.ValueTask VisitRootOperationTypeDefinition(GraphQLParser.AST.GraphQLRootOperationTypeDefinition rootOperationTypeDefinition, TContext context) { } + public override System.Threading.Tasks.ValueTask VisitScalarTypeDefinition(GraphQLParser.AST.GraphQLScalarTypeDefinition scalarTypeDefinition, TContext context) { } + public override System.Threading.Tasks.ValueTask VisitSchemaDefinition(GraphQLParser.AST.GraphQLSchemaDefinition schemaDefinition, TContext context) { } + public override System.Threading.Tasks.ValueTask VisitSelectionSet(GraphQLParser.AST.GraphQLSelectionSet selectionSet, TContext context) { } + public override System.Threading.Tasks.ValueTask VisitStringValue(GraphQLParser.AST.GraphQLScalarValue stringValue, TContext context) { } + public override System.Threading.Tasks.ValueTask VisitTypeCondition(GraphQLParser.AST.GraphQLTypeCondition typeCondition, TContext context) { } + public override System.Threading.Tasks.ValueTask VisitUnionTypeDefinition(GraphQLParser.AST.GraphQLUnionTypeDefinition unionTypeDefinition, TContext context) { } + public override System.Threading.Tasks.ValueTask VisitVariable(GraphQLParser.AST.GraphQLVariable variable, TContext context) { } + public override System.Threading.Tasks.ValueTask VisitVariableDefinition(GraphQLParser.AST.GraphQLVariableDefinition variableDefinition, TContext context) { } + } + public class StructureWriterOptions + { + public StructureWriterOptions() { } + public bool WriteNames { get; set; } + } + public class StructureWriter : GraphQLParser.Visitors.DefaultNodeVisitor + where TContext : GraphQLParser.Visitors.IWriteContext + { + public StructureWriter(GraphQLParser.Visitors.StructureWriterOptions options) { } + public override System.Threading.Tasks.ValueTask Visit(GraphQLParser.AST.ASTNode? node, TContext context) { } + } + public static class WriteContextExtensions + { + public static System.Threading.Tasks.ValueTask Write(this TContext context, GraphQLParser.ROM value) + where TContext : GraphQLParser.Visitors.IWriteContext { } + public static System.Threading.Tasks.ValueTask WriteLine(this TContext context) + where TContext : GraphQLParser.Visitors.IWriteContext { } + } } \ No newline at end of file diff --git a/src/GraphQLParser.Tests/Files/CommentsOnAlias.graphql b/src/GraphQLParser.Tests/Files/CommentsOnAlias.graphql new file mode 100644 index 00000000..70758efd --- /dev/null +++ b/src/GraphQLParser.Tests/Files/CommentsOnAlias.graphql @@ -0,0 +1,9 @@ +query q +{ +#alias comment + a +#colon comment + : +#field comment + name +} diff --git a/src/GraphQLParser.Tests/GraphQLAstVisitorTests.cs b/src/GraphQLParser.Tests/GraphQLAstVisitorTests.cs deleted file mode 100644 index f14569f3..00000000 --- a/src/GraphQLParser.Tests/GraphQLAstVisitorTests.cs +++ /dev/null @@ -1,428 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using GraphQLParser.AST; -using NSubstitute; -using Shouldly; -using Xunit; - -namespace GraphQLParser.Tests -{ - public class GraphQLAstVisitorTests - { - private readonly List _visitedAliases; - private readonly List _visitedArguments; - private readonly List _visitedDefinitions; - private readonly List _visitedDirectives; - private readonly List _visitedEnumValues; - private readonly List _visitedFields; - private readonly List _visitedFloatValues; - private readonly List _visitedFragmentDefinitions; - private readonly List _visitedFragmentSpreads; - private readonly List _visitedFragmentTypeConditions; - private readonly List _visitedInlineFragments; - private readonly List _visitedIntValues; - private readonly List _visitedNames; - private readonly List _visitedSelectionSets; - private readonly List _visitedStringValues; - private readonly List _visitedVariables; - private readonly GraphQLAstVisitor _visitor; - private readonly List _visitedBooleanValues; - - public GraphQLAstVisitorTests() - { - _visitor = Substitute.ForPartsOf(); - - _visitedDefinitions = MockVisitMethod((visitor) => visitor.BeginVisitOperationDefinition(null)); - _visitedSelectionSets = MockVisitMethod((visitor) => visitor.BeginVisitSelectionSet(null)); - _visitedFields = MockVisitMethod((visitor) => visitor.BeginVisitField(null)); - _visitedNames = MockVisitMethod((visitor) => visitor.BeginVisitName(null)); - _visitedArguments = MockVisitMethod((visitor) => visitor.BeginVisitArgument(null)); - _visitedAliases = MockVisitMethod((visitor) => visitor.BeginVisitAlias(null)); - _visitedFragmentSpreads = MockVisitMethod((visitor) => visitor.BeginVisitFragmentSpread(null)); - _visitedFragmentDefinitions = MockVisitMethod((visitor) => visitor.BeginVisitFragmentDefinition(null)); - _visitedFragmentTypeConditions = MockVisitMethod((visitor) => visitor.BeginVisitNamedType(null)); - _visitedInlineFragments = MockVisitMethod((visitor) => visitor.BeginVisitInlineFragment(null)); - _visitedDirectives = MockVisitMethod((visitor) => visitor.BeginVisitDirective(null)); - _visitedVariables = MockVisitMethod((visitor) => visitor.BeginVisitVariable(null)); - _visitedIntValues = MockVisitMethod((visitor) => visitor.BeginVisitIntValue(null)); - _visitedFloatValues = MockVisitMethod((visitor) => visitor.BeginVisitFloatValue(null)); - _visitedStringValues = MockVisitMethod((visitor) => visitor.BeginVisitStringValue(null)); - _visitedBooleanValues = MockVisitMethod((visitor) => visitor.BeginVisitBooleanValue(null)); - _visitedEnumValues = MockVisitMethod((visitor) => visitor.BeginVisitEnumValue(null)); - } - - [Theory] - [InlineData(IgnoreOptions.None)] - [InlineData(IgnoreOptions.Comments)] - [InlineData(IgnoreOptions.Locations)] - [InlineData(IgnoreOptions.All)] - public void Visit_BooleanValueArgument_VisitsOneBooleanValue(IgnoreOptions options) - { - using var d = "{ stuff(id : true) }".Parse(new ParserOptions { Ignore = options }); - _visitor.Visit(d); - - _visitedBooleanValues.ShouldHaveSingleItem(); - } - - [Theory] - [InlineData(IgnoreOptions.None)] - [InlineData(IgnoreOptions.Comments)] - [InlineData(IgnoreOptions.Locations)] - [InlineData(IgnoreOptions.All)] - public void Visit_DefinitionWithSingleFragmentSpread_VisitsFragmentSpreadOneTime(IgnoreOptions options) - { - using var d = "{ foo { ...fragment } }".Parse(new ParserOptions { Ignore = options }); - _visitor.Visit(d); - - _visitedFragmentSpreads.ShouldHaveSingleItem(); - } - - [Theory] - [InlineData(IgnoreOptions.None)] - [InlineData(IgnoreOptions.Comments)] - [InlineData(IgnoreOptions.Locations)] - [InlineData(IgnoreOptions.All)] - public void Visit_DefinitionWithSingleFragmentSpread_VisitsNameOfPropertyAndFragmentSpread(IgnoreOptions options) - { - using var d = "{ foo { ...fragment } }".Parse(new ParserOptions { Ignore = options }); - _visitor.Visit(d); - - _visitedNames.Count.ShouldBe(2); - } - - [Theory] - [InlineData(IgnoreOptions.None)] - [InlineData(IgnoreOptions.Comments)] - [InlineData(IgnoreOptions.Locations)] - [InlineData(IgnoreOptions.All)] - public void Visit_DirectiveWithVariable_VisitsVariableOnce(IgnoreOptions options) - { - using var d = "{ ... @include(if : $stuff) { field } }".Parse(new ParserOptions { Ignore = options }); - _visitor.Visit(d); - - _visitedVariables.ShouldHaveSingleItem(); - } - - [Theory] - [InlineData(IgnoreOptions.None)] - [InlineData(IgnoreOptions.Comments)] - [InlineData(IgnoreOptions.Locations)] - [InlineData(IgnoreOptions.All)] - public void Visit_EnumValueArgument_VisitsOneEnumValue(IgnoreOptions options) - { - using var d = "{ stuff(id : TEST_ENUM) }".Parse(new ParserOptions { Ignore = options }); - _visitor.Visit(d); - - _visitedEnumValues.ShouldHaveSingleItem(); - } - - [Theory] - [InlineData(IgnoreOptions.None)] - [InlineData(IgnoreOptions.Comments)] - [InlineData(IgnoreOptions.Locations)] - [InlineData(IgnoreOptions.All)] - public void Visit_FloatValueArgument_VisitsOneFloatValue(IgnoreOptions options) - { - using var d = "{ stuff(id : 1.2) }".Parse(new ParserOptions { Ignore = options }); - _visitor.Visit(d); - - _visitedFloatValues.ShouldHaveSingleItem(); - } - - [Theory] - [InlineData(IgnoreOptions.None)] - [InlineData(IgnoreOptions.Comments)] - [InlineData(IgnoreOptions.Locations)] - [InlineData(IgnoreOptions.All)] - public void Visit_FragmentWithTypeCondition_VisitsFragmentDefinitionOnce(IgnoreOptions options) - { - using var d = "fragment testFragment on Stuff { field }".Parse(new ParserOptions { Ignore = options }); - _visitor.Visit(d); - - _visitedFragmentDefinitions.ShouldHaveSingleItem(); - } - - [Theory] - [InlineData(IgnoreOptions.None)] - [InlineData(IgnoreOptions.Comments)] - [InlineData(IgnoreOptions.Locations)] - [InlineData(IgnoreOptions.All)] - public void Visit_FragmentWithTypeCondition_VisitsTypeConditionOnce(IgnoreOptions options) - { - using var d = "fragment testFragment on Stuff { field }".Parse(new ParserOptions { Ignore = options }); - _visitor.Visit(d); - - _visitedFragmentTypeConditions.ShouldHaveSingleItem(); - } - - [Theory] - [InlineData(IgnoreOptions.None)] - [InlineData(IgnoreOptions.Comments)] - [InlineData(IgnoreOptions.Locations)] - [InlineData(IgnoreOptions.All)] - public void Visit_InlineFragmentWithDirectiveAndArgument_VisitsArgumentsOnce(IgnoreOptions options) - { - using var d = "{ ... @include(if : $stuff) { field } }".Parse(new ParserOptions { Ignore = options }); - _visitor.Visit(d); - - _visitedArguments.ShouldHaveSingleItem(); - } - - [Theory] - [InlineData(IgnoreOptions.None)] - [InlineData(IgnoreOptions.Comments)] - [InlineData(IgnoreOptions.Locations)] - [InlineData(IgnoreOptions.All)] - public void Visit_InlineFragmentWithDirectiveAndArgument_VisitsDirectiveOnce(IgnoreOptions options) - { - using var d = "{ ... @include(if : $stuff) { field } }".Parse(new ParserOptions { Ignore = options }); - _visitor.Visit(d); - - _visitedDirectives.ShouldHaveSingleItem(); - } - - [Theory] - [InlineData(IgnoreOptions.None)] - [InlineData(IgnoreOptions.Comments)] - [InlineData(IgnoreOptions.Locations)] - [InlineData(IgnoreOptions.All)] - public void Visit_InlineFragmentWithDirectiveAndArgument_VisitsNameThreeTimes(IgnoreOptions options) - { - using var d = "{ ... @include(if : $stuff) { field } }".Parse(new ParserOptions { Ignore = options }); - _visitor.Visit(d); - - _visitedNames.Count.ShouldBe(4); - } - - [Theory] - [InlineData(IgnoreOptions.None)] - [InlineData(IgnoreOptions.Comments)] - [InlineData(IgnoreOptions.Locations)] - [InlineData(IgnoreOptions.All)] - public void Visit_InlineFragmentWithOneField_VisitsOneField(IgnoreOptions options) - { - using var d = "{ ... @include(if : $stuff) { field } }".Parse(new ParserOptions { Ignore = options }); - _visitor.Visit(d); - - _visitedFields.ShouldHaveSingleItem(); - } - - [Theory] - [InlineData(IgnoreOptions.None)] - [InlineData(IgnoreOptions.Comments)] - [InlineData(IgnoreOptions.Locations)] - [InlineData(IgnoreOptions.All)] - public void Visit_InlineFragmentWithTypeCondition_VisitsInlineFragmentOnce(IgnoreOptions options) - { - using var d = "{ ... on Stuff { field } }".Parse(new ParserOptions { Ignore = options }); - _visitor.Visit(d); - - _visitedInlineFragments.ShouldHaveSingleItem(); - } - - [Theory] - [InlineData(IgnoreOptions.None)] - [InlineData(IgnoreOptions.Comments)] - [InlineData(IgnoreOptions.Locations)] - [InlineData(IgnoreOptions.All)] - public void Visit_InlineFragmentWithTypeCondition_VisitsTypeConditionOnce(IgnoreOptions options) - { - using var d = "{ ... on Stuff { field } }".Parse(new ParserOptions { Ignore = options }); - _visitor.Visit(d); - - _visitedFragmentTypeConditions.ShouldHaveSingleItem(); - } - - [Theory] - [InlineData(IgnoreOptions.None)] - [InlineData(IgnoreOptions.Comments)] - [InlineData(IgnoreOptions.Locations)] - [InlineData(IgnoreOptions.All)] - public void Visit_IntValueArgument_VisitsOneIntValue(IgnoreOptions options) - { - using var d = "{ stuff(id : 1) }".Parse(new ParserOptions { Ignore = options }); - _visitor.Visit(d); - - _visitedIntValues.ShouldHaveSingleItem(); - } - - [Theory] - [InlineData(IgnoreOptions.None)] - [InlineData(IgnoreOptions.Comments)] - [InlineData(IgnoreOptions.Locations)] - [InlineData(IgnoreOptions.All)] - public void Visit_OneDefinition_CallsVisitDefinitionOnce(IgnoreOptions options) - { - using var d = "{ a }".Parse(new ParserOptions { Ignore = options }); - _visitor.Visit(d); - - _visitedDefinitions.ShouldHaveSingleItem(); - } - - [Theory] - [InlineData(IgnoreOptions.None)] - [InlineData(IgnoreOptions.Comments)] - [InlineData(IgnoreOptions.Locations)] - [InlineData(IgnoreOptions.All)] - public void Visit_OneDefinition_ProvidesCorrectDefinitionAsParameter(IgnoreOptions options) - { - using var d = "{ a }".Parse(new ParserOptions { Ignore = options }); - _visitor.Visit(d); - - _visitedDefinitions.Single().ShouldBe(d.Definitions.Single()); - } - - [Theory] - [InlineData(IgnoreOptions.None)] - [InlineData(IgnoreOptions.Comments)] - [InlineData(IgnoreOptions.Locations)] - [InlineData(IgnoreOptions.All)] - public void Visit_OneDefinition_VisitsOneSelectionSet(IgnoreOptions options) - { - using var d = "{ a, b }".Parse(new ParserOptions { Ignore = options }); - _visitor.Visit(d); - - _visitedSelectionSets.ShouldHaveSingleItem(); - } - - [Theory] - [InlineData(IgnoreOptions.None)] - [InlineData(IgnoreOptions.Comments)] - [InlineData(IgnoreOptions.Locations)] - [InlineData(IgnoreOptions.All)] - public void Visit_OneDefinitionWithOneAliasedField_VisitsOneAlias(IgnoreOptions options) - { - using var d = "{ foo, foo : bar }".Parse(new ParserOptions { Ignore = options }); - _visitor.Visit(d); - - _visitedAliases.ShouldHaveSingleItem(); - } - - [Theory] - [InlineData(IgnoreOptions.None)] - [InlineData(IgnoreOptions.Comments)] - [InlineData(IgnoreOptions.Locations)] - [InlineData(IgnoreOptions.All)] - public void Visit_OneDefinitionWithOneArgument_VisitsOneArgument(IgnoreOptions options) - { - using var d = "{ foo(id : 1) { name } }".Parse(new ParserOptions { Ignore = options }); - _visitor.Visit(d); - - _visitedArguments.ShouldHaveSingleItem(); - } - - [Theory] - [InlineData(IgnoreOptions.None)] - [InlineData(IgnoreOptions.Comments)] - [InlineData(IgnoreOptions.Locations)] - [InlineData(IgnoreOptions.All)] - public void Visit_OneDefinitionWithOneNestedArgument_VisitsOneArgument(IgnoreOptions options) - { - using var d = "{ foo{ names(size: 10) } }".Parse(new ParserOptions { Ignore = options }); - _visitor.Visit(d); - - _visitedArguments.ShouldHaveSingleItem(); - } - - [Theory] - [InlineData(IgnoreOptions.None)] - [InlineData(IgnoreOptions.Comments)] - [InlineData(IgnoreOptions.Locations)] - [InlineData(IgnoreOptions.All)] - public void Visit_StringValueArgument_VisitsOneStringValue(IgnoreOptions options) - { - using var d = "{ stuff(id : \"abc\") }".Parse(new ParserOptions { Ignore = options }); - _visitor.Visit(d); - - _visitedStringValues.ShouldHaveSingleItem(); - } - - [Theory] - [InlineData(IgnoreOptions.None)] - [InlineData(IgnoreOptions.Comments)] - [InlineData(IgnoreOptions.Locations)] - [InlineData(IgnoreOptions.All)] - public void Visit_TwoDefinitions_CallsVisitDefinitionTwice(IgnoreOptions options) - { - using var d = "{ a }\n{ b }".Parse(new ParserOptions { Ignore = options }); - _visitor.Visit(d); - - _visitedDefinitions.Count.ShouldBe(2); - } - - [Theory] - [InlineData(IgnoreOptions.None)] - [InlineData(IgnoreOptions.Comments)] - [InlineData(IgnoreOptions.Locations)] - [InlineData(IgnoreOptions.All)] - public void Visit_TwoFields_VisitsFieldTwice(IgnoreOptions options) - { - using var d = "{ a, b }".Parse(new ParserOptions { Ignore = options }); - _visitor.Visit(d); - - _visitedFields.Count.ShouldBe(2); - } - - [Theory] - [InlineData(IgnoreOptions.None)] - [InlineData(IgnoreOptions.Comments)] - [InlineData(IgnoreOptions.Locations)] - [InlineData(IgnoreOptions.All)] - public void Visit_TwoFields_VisitsTwoFieldNames(IgnoreOptions options) - { - using var d = "{ a, b }".Parse(new ParserOptions { Ignore = options }); - _visitor.Visit(d); - - _visitedNames.Count.ShouldBe(2); - } - - [Theory] - [InlineData(IgnoreOptions.None)] - [InlineData(IgnoreOptions.Comments)] - [InlineData(IgnoreOptions.Locations)] - [InlineData(IgnoreOptions.All)] - public void Visit_TwoFields_VisitsTwoFieldNamesAndDefinitionName(IgnoreOptions options) - { - using var d = "query foo { a, b }".Parse(new ParserOptions { Ignore = options }); - _visitor.Visit(d); - - _visitedNames.Count.ShouldBe(3); - } - - [Theory] - [InlineData(IgnoreOptions.None)] - [InlineData(IgnoreOptions.Comments)] - [InlineData(IgnoreOptions.Locations)] - [InlineData(IgnoreOptions.All)] - public void Visit_TwoFieldsWithOneNested_VisitsFiveFields(IgnoreOptions options) - { - using var d = "{a, nested { x, y }, b}".Parse(new ParserOptions { Ignore = options }); - _visitor.Visit(d); - - _visitedFields.Count.ShouldBe(5); - } - - [Theory] - [InlineData(IgnoreOptions.None)] - [InlineData(IgnoreOptions.Comments)] - [InlineData(IgnoreOptions.Locations)] - [InlineData(IgnoreOptions.All)] - public void Visit_TwoFieldsWithOneNested_VisitsFiveNames(IgnoreOptions options) - { - using var d = "{a, nested { x, y }, b}".Parse(new ParserOptions { Ignore = options }); - _visitor.Visit(d); - - _visitedNames.Count.ShouldBe(5); - } - - private List MockVisitMethod(Action visitorMethod) - { - var collection = new List(); - _visitor.WhenForAnyArgs(visitorMethod) - .Do(e => collection.Add(e.Arg())); - - return collection; - } - } -} diff --git a/src/GraphQLParser.Tests/GraphQLParser.Tests.csproj b/src/GraphQLParser.Tests/GraphQLParser.Tests.csproj index dcef7b33..55e99624 100644 --- a/src/GraphQLParser.Tests/GraphQLParser.Tests.csproj +++ b/src/GraphQLParser.Tests/GraphQLParser.Tests.csproj @@ -19,7 +19,6 @@ - diff --git a/src/GraphQLParser.Tests/LexerTests.cs b/src/GraphQLParser.Tests/LexerTests.cs index 5169e7f1..cdba237f 100644 --- a/src/GraphQLParser.Tests/LexerTests.cs +++ b/src/GraphQLParser.Tests/LexerTests.cs @@ -975,38 +975,40 @@ public void Lex_WhiteSpaceStringToken_HasStringKind() } [Theory] - [InlineData("test", "test")] - [InlineData("te\\\"\"\"st", "te\"\"\"st")] - [InlineData("\ntest", "test")] - [InlineData("\r\ntest", "test")] - [InlineData(" \ntest", "test")] - [InlineData("\t\ntest", "test")] - [InlineData("\n\ntest", "test")] - [InlineData("test\nline2", "test\nline2")] - [InlineData("test\rline2", "test\nline2")] - [InlineData("test\r\nline2", "test\nline2")] - [InlineData("test\r\r\nline2", "test\n\nline2")] - [InlineData("test\r\n\nline2", "test\n\nline2")] - [InlineData("test\n", "test")] - [InlineData("test\n ", "test")] - [InlineData("test\n\t", "test")] - [InlineData("test\n\n", "test")] - [InlineData("test\n line2", "test\nline2")] - [InlineData("test\n\t\tline2", "test\nline2")] - [InlineData("test\n \tline2", "test\nline2")] - [InlineData(" test\nline2", " test\nline2")] - [InlineData(" test\n line2", " test\nline2")] - [InlineData("\n test\n line2", "test\nline2")] - [InlineData(" test\n line2\n\t\tline3\n line4", " test\nline2\n\tline3\n line4")] - [InlineData(" test\n Hello,\n\n world!\n ", " test\nHello,\n\n world!")] - [InlineData(" \n Hello,\r\n\n world!\n ", "Hello,\n\n world!")] - [InlineData(" \n Hello,\r\n\n wor___ld!\n ", "Hello,\n\n wor___ld!")] - [InlineData("\r\n Hello,\r\n World!\r\n\r\n Yours,\r\n GraphQL.\r\n ", "Hello,\n World!\n\nYours,\n GraphQL.")] - [InlineData("Test \\n escaping", "Test \\n escaping")] - [InlineData("Test \\u1234 escaping", "Test \\u1234 escaping")] - [InlineData("Test \\ escaping", "Test \\ escaping")] - public void Lex_BlockString_Tests(string input, string expected) - { + [InlineData(1, "test", "test")] + [InlineData(2, "te\\\"\"\"st", "te\"\"\"st")] + [InlineData(3, "\ntest", "test")] + [InlineData(4, "\r\ntest", "test")] + [InlineData(5, " \ntest", "test")] + [InlineData(6, "\t\ntest", "test")] + [InlineData(7, "\n\ntest", "test")] + [InlineData(8, "test\nline2", "test\nline2")] + [InlineData(9, "test\rline2", "test\nline2")] + [InlineData(10, "test\r\nline2", "test\nline2")] + [InlineData(11, "test\r\r\nline2", "test\n\nline2")] + [InlineData(12, "test\r\n\nline2", "test\n\nline2")] + [InlineData(13, "test\n", "test")] + [InlineData(14, "test\n ", "test")] + [InlineData(15, "test\n\t", "test")] + [InlineData(16, "test\n\n", "test")] + [InlineData(17, "test\n line2", "test\nline2")] + [InlineData(18, "test\n\t\tline2", "test\nline2")] + [InlineData(19, "test\n \tline2", "test\nline2")] + [InlineData(20, " test\nline2", " test\nline2")] + [InlineData(21, " test\n line2", " test\nline2")] + [InlineData(22, "\n test\n line2", "test\nline2")] + [InlineData(23, " test\n line2\n\t\tline3\n line4", " test\nline2\n\tline3\n line4")] + [InlineData(24, " test\n Hello,\n\n world!\n ", " test\nHello,\n\n world!")] + [InlineData(25, " \n Hello,\r\n\n world!\n ", "Hello,\n\n world!")] + [InlineData(26, " \n Hello,\r\n\n wor___ld!\n ", "Hello,\n\n wor___ld!")] + [InlineData(27, "\r\n Hello,\r\n World!\r\n\r\n Yours,\r\n GraphQL.\r\n ", "Hello,\n World!\n\nYours,\n GraphQL.")] + [InlineData(28, "Test \\n escaping", "Test \\n escaping")] + [InlineData(29, "Test \\u1234 escaping", "Test \\u1234 escaping")] + [InlineData(30, "Test \\ escaping", "Test \\ escaping")] + public void Lex_BlockString_Tests(int number, string input, string expected) + { + number.ShouldBeGreaterThan(0); + input = input.Replace("___", new string('_', 9000)); expected = expected.Replace("___", new string('_', 9000)); input = "\"\"\"" + input + "\"\"\""; diff --git a/src/GraphQLParser.Tests/ParserTests.cs b/src/GraphQLParser.Tests/ParserTests.cs index 5fc91994..e210feaf 100644 --- a/src/GraphQLParser.Tests/ParserTests.cs +++ b/src/GraphQLParser.Tests/ParserTests.cs @@ -168,7 +168,7 @@ public void Comments_on_NamedTypes_Should_Read_Correctly(IgnoreOptions options) var def1 = document.Definitions[0] as GraphQLOperationDefinition; var field = def1.SelectionSet.Selections[0] as GraphQLField; var frag = field.SelectionSet.Selections[0] as GraphQLInlineFragment; - frag.TypeCondition.Comment.Text.ShouldBe("comment for named type from TypeCondition"); + frag.TypeCondition.Type.Comment.Text.ShouldBe("comment for named type from TypeCondition"); var def2 = document.Definitions[1] as GraphQLObjectTypeDefinition; def2.Interfaces[0].Comment.Text.ShouldBe("comment for named type from ImplementsInterfaces"); @@ -246,6 +246,29 @@ public void Comments_on_Type_Should_Read_Correctly(IgnoreOptions options) (def.Fields[2].Type as GraphQLListType).Type.Comment.Text.ShouldBe("comment for item type"); } + [Theory] + [InlineData(IgnoreOptions.None)] + //[InlineData(IgnoreOptions.Comments)] + [InlineData(IgnoreOptions.Locations)] + //[InlineData(IgnoreOptions.All)] + public void Comments_on_Alias_Should_Read_Correctly(IgnoreOptions options) + { + string query = "CommentsOnAlias".ReadGraphQLFile(); + + using var document = query.Parse(new ParserOptions { Ignore = options }); + document.Definitions.Count.ShouldBe(1); + var def = document.Definitions[0] as GraphQLOperationDefinition; + def.SelectionSet.Selections.Count.ShouldBe(1); + var field = def.SelectionSet.Selections[0].ShouldBeAssignableTo(); + field.Name.Value.ShouldBe("name"); + field.Comment.Text.ShouldBe("field comment"); + field.Alias.Name.Value.ShouldBe("a"); + field.Alias.Comment.Text.ShouldBe("alias comment"); + + document.UnattachedComments.Count.ShouldBe(1); + document.UnattachedComments[0].Text.ShouldBe("colon comment"); + } + [Theory] [InlineData(IgnoreOptions.None)] //[InlineData(IgnoreOptions.Comments)] @@ -547,6 +570,25 @@ public void Parse_VariableInlineValues_DoesNotThrowError(IgnoreOptions options) } } + [Theory] + [InlineData(IgnoreOptions.None)] + [InlineData(IgnoreOptions.Comments)] + [InlineData(IgnoreOptions.Locations)] + [InlineData(IgnoreOptions.All)] + public void Should_Read_Directives_on_VariableDefinition(IgnoreOptions options) + { + using (var document = "query A($id: String @a @b(priority: 1, managed: true)) { name }".Parse(new ParserOptions { Ignore = options })) + { + document.Definitions.Count.ShouldBe(1); + var def = document.Definitions[0].ShouldBeAssignableTo(); + def.VariableDefinitions.Count.ShouldBe(1); + def.VariableDefinitions[0].Directives.Count.ShouldBe(2); + def.VariableDefinitions[0].Directives[0].Name.Value.ShouldBe("a"); + def.VariableDefinitions[0].Directives[1].Name.Value.ShouldBe("b"); + def.VariableDefinitions[0].Directives[1].Arguments.Count.ShouldBe(2); + } + } + [Theory] [InlineData(IgnoreOptions.None)] [InlineData(IgnoreOptions.Comments)] diff --git a/src/GraphQLParser.Tests/Visitors/GraphQLAstVisitorTests.cs b/src/GraphQLParser.Tests/Visitors/GraphQLAstVisitorTests.cs new file mode 100644 index 00000000..cdaa2586 --- /dev/null +++ b/src/GraphQLParser.Tests/Visitors/GraphQLAstVisitorTests.cs @@ -0,0 +1,580 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using GraphQLParser.AST; +using GraphQLParser.Visitors; +using Shouldly; +using Xunit; + +namespace GraphQLParser.Tests.Visitors +{ + public class GraphQLAstVisitorTests + { + public class CountVisitor : DefaultNodeVisitor + { + public override async ValueTask VisitBooleanValue(GraphQLScalarValue booleanValue, CountContext context) + { + context.VisitedBooleanValues.Add(booleanValue); + await base.VisitBooleanValue(booleanValue, context); + } + + public override async ValueTask VisitIntValue(GraphQLScalarValue intValue, CountContext context) + { + context.VisitedIntValues.Add(intValue); + await base.VisitIntValue(intValue, context); + } + + public override async ValueTask VisitFragmentSpread(GraphQLFragmentSpread fragmentSpread, CountContext context) + { + context.VisitedFragmentSpreads.Add(fragmentSpread); + await base.VisitFragmentSpread(fragmentSpread, context); + } + + public override async ValueTask VisitArgument(GraphQLArgument argument, CountContext context) + { + context.VisitedArguments.Add(argument); + await base.VisitArgument(argument, context); + } + + public override async ValueTask VisitVariable(GraphQLVariable variable, CountContext context) + { + context.VisitedVariables.Add(variable); + await base.VisitVariable(variable, context); + } + + public override async ValueTask VisitSelectionSet(GraphQLSelectionSet selectionSet, CountContext context) + { + context.VisitedSelectionSets.Add(selectionSet); + await base.VisitSelectionSet(selectionSet, context); + } + + public override async ValueTask VisitDirective(GraphQLDirective directive, CountContext context) + { + context.VisitedDirectives.Add(directive); + await base.VisitDirective(directive, context); + } + + public override async ValueTask VisitEnumValue(GraphQLScalarValue enumValue, CountContext context) + { + context.VisitedEnumValues.Add(enumValue); + await base.VisitEnumValue(enumValue, context); + } + + public override async ValueTask VisitStringValue(GraphQLScalarValue stringValue, CountContext context) + { + context.VisitedStringValues.Add(stringValue); + await base.VisitStringValue(stringValue, context); + } + + public override async ValueTask VisitName(GraphQLName name, CountContext context) + { + context.VisitedNames.Add(name); + await base.VisitName(name, context); + } + + public override async ValueTask VisitField(GraphQLField field, CountContext context) + { + context.VisitedFields.Add(field); + if (field.Alias != null) + context.VisitedAliases.Add(field.Alias); + await base.VisitField(field, context); + } + + public override async ValueTask VisitFloatValue(GraphQLScalarValue floatValue, CountContext context) + { + context.VisitedFloatValues.Add(floatValue); + await base.VisitFloatValue(floatValue, context); + } + + public override async ValueTask VisitEnumTypeDefinition(GraphQLEnumTypeDefinition enumTypeDefinition, CountContext context) + { + context.VisitedDefinitions.Add(enumTypeDefinition); + await base.VisitEnumTypeDefinition(enumTypeDefinition, context); + } + + public override async ValueTask VisitInlineFragment(GraphQLInlineFragment inlineFragment, CountContext context) + { + context.VisitedInlineFragments.Add(inlineFragment); + context.VisitedFragmentTypeConditions.Add(inlineFragment.TypeCondition); + await base.VisitInlineFragment(inlineFragment, context); + } + + public override async ValueTask VisitFragmentDefinition(GraphQLFragmentDefinition fragmentDefinition, CountContext context) + { + context.VisitedFragmentDefinitions.Add(fragmentDefinition); + context.VisitedFragmentTypeConditions.Add(fragmentDefinition.TypeCondition); + await base.VisitFragmentDefinition(fragmentDefinition, context); + } + + public override async ValueTask VisitFieldDefinition(GraphQLFieldDefinition fieldDefinition, CountContext context) + { + context.VisitedDefinitions.Add(fieldDefinition); + await base.VisitFieldDefinition(fieldDefinition, context); + } + + public override async ValueTask VisitDirectiveDefinition(GraphQLDirectiveDefinition directiveDefinition, CountContext context) + { + context.VisitedDefinitions.Add(directiveDefinition); + await base.VisitDirectiveDefinition(directiveDefinition, context); + } + + public override async ValueTask VisitEnumValueDefinition(GraphQLEnumValueDefinition enumValueDefinition, CountContext context) + { + context.VisitedDefinitions.Add(enumValueDefinition); + await base.VisitEnumValueDefinition(enumValueDefinition, context); + } + + public override async ValueTask VisitInputObjectTypeDefinition(GraphQLInputObjectTypeDefinition inputObjectTypeDefinition, CountContext context) + { + context.VisitedDefinitions.Add(inputObjectTypeDefinition); + await base.VisitInputObjectTypeDefinition(inputObjectTypeDefinition, context); + } + + public override async ValueTask VisitInputValueDefinition(GraphQLInputValueDefinition inputValueDefinition, CountContext context) + { + context.VisitedDefinitions.Add(inputValueDefinition); + await base.VisitInputValueDefinition(inputValueDefinition, context); + } + + public override async ValueTask VisitInterfaceTypeDefinition(GraphQLInterfaceTypeDefinition interfaceTypeDefinition, CountContext context) + { + context.VisitedDefinitions.Add(interfaceTypeDefinition); + await base.VisitInterfaceTypeDefinition(interfaceTypeDefinition, context); + } + + public override async ValueTask VisitObjectTypeDefinition(GraphQLObjectTypeDefinition objectTypeDefinition, CountContext context) + { + context.VisitedDefinitions.Add(objectTypeDefinition); + await base.VisitObjectTypeDefinition(objectTypeDefinition, context); + } + + public override async ValueTask VisitOperationDefinition(GraphQLOperationDefinition operationDefinition, CountContext context) + { + context.VisitedDefinitions.Add(operationDefinition); + await base.VisitOperationDefinition(operationDefinition, context); + } + + public override async ValueTask VisitScalarTypeDefinition(GraphQLScalarTypeDefinition scalarTypeDefinition, CountContext context) + { + context.VisitedDefinitions.Add(scalarTypeDefinition); + await base.VisitScalarTypeDefinition(scalarTypeDefinition, context); + } + + public override async ValueTask VisitRootOperationTypeDefinition(GraphQLRootOperationTypeDefinition rootOperationTypeDefinition, CountContext context) + { + context.VisitedDefinitions.Add(rootOperationTypeDefinition); + await base.VisitRootOperationTypeDefinition(rootOperationTypeDefinition, context); + } + + public override async ValueTask VisitVariableDefinition(GraphQLVariableDefinition variableDefinition, CountContext context) + { + context.VisitedDefinitions.Add(variableDefinition); + await base.VisitVariableDefinition(variableDefinition, context); + } + + public override async ValueTask VisitUnionTypeDefinition(GraphQLUnionTypeDefinition unionTypeDefinition, CountContext context) + { + context.VisitedDefinitions.Add(unionTypeDefinition); + await base.VisitUnionTypeDefinition(unionTypeDefinition, context); + } + + public override async ValueTask VisitSchemaDefinition(GraphQLSchemaDefinition schemaDefinition, CountContext context) + { + context.VisitedDefinitions.Add(schemaDefinition); + await base.VisitSchemaDefinition(schemaDefinition, context); + } + } + + public class CountContext : INodeVisitorContext + { + public List VisitedAliases = new(); + public List VisitedArguments = new(); + public List VisitedDefinitions = new(); + public List VisitedDirectives = new(); + public List VisitedEnumValues = new(); + public List VisitedFields = new(); + public List VisitedFloatValues = new(); + public List VisitedFragmentDefinitions = new(); + public List VisitedFragmentSpreads = new(); + public List VisitedFragmentTypeConditions = new(); + public List VisitedInlineFragments = new(); + public List VisitedIntValues = new(); + public List VisitedNames = new(); + public List VisitedSelectionSets = new(); + public List VisitedStringValues = new(); + public List VisitedVariables = new(); + public List VisitedBooleanValues = new(); + + public CancellationToken CancellationToken { get; set; } + } + + private readonly CountVisitor _visitor = new(); + + public CountContext Context = new(); + + [Theory] + [InlineData(IgnoreOptions.None)] + [InlineData(IgnoreOptions.Comments)] + [InlineData(IgnoreOptions.Locations)] + [InlineData(IgnoreOptions.All)] + public void Visit_BooleanValueArgument_VisitsOneBooleanValue(IgnoreOptions options) + { + using var d = "{ stuff(id : true) }".Parse(new ParserOptions { Ignore = options }); + _visitor.Visit(d, Context); + + Context.VisitedBooleanValues.ShouldHaveSingleItem(); + } + + [Theory] + [InlineData(IgnoreOptions.None)] + [InlineData(IgnoreOptions.Comments)] + [InlineData(IgnoreOptions.Locations)] + [InlineData(IgnoreOptions.All)] + public void Visit_DefinitionWithSingleFragmentSpread_VisitsFragmentSpreadOneTime(IgnoreOptions options) + { + using var d = "{ foo { ...fragment } }".Parse(new ParserOptions { Ignore = options }); + _visitor.Visit(d, Context); + + Context.VisitedFragmentSpreads.ShouldHaveSingleItem(); + } + + [Theory] + [InlineData(IgnoreOptions.None)] + [InlineData(IgnoreOptions.Comments)] + [InlineData(IgnoreOptions.Locations)] + [InlineData(IgnoreOptions.All)] + public void Visit_DefinitionWithSingleFragmentSpread_VisitsNameOfPropertyAndFragmentSpread(IgnoreOptions options) + { + using var d = "{ foo { ...fragment } }".Parse(new ParserOptions { Ignore = options }); + _visitor.Visit(d, Context); + + Context.VisitedNames.Count.ShouldBe(2); + } + + [Theory] + [InlineData(IgnoreOptions.None)] + [InlineData(IgnoreOptions.Comments)] + [InlineData(IgnoreOptions.Locations)] + [InlineData(IgnoreOptions.All)] + public void Visit_DirectiveWithVariable_VisitsVariableOnce(IgnoreOptions options) + { + using var d = "{ ... @include(if : $stuff) { field } }".Parse(new ParserOptions { Ignore = options }); + _visitor.Visit(d, Context); + + Context.VisitedVariables.ShouldHaveSingleItem(); + } + + [Theory] + [InlineData(IgnoreOptions.None)] + [InlineData(IgnoreOptions.Comments)] + [InlineData(IgnoreOptions.Locations)] + [InlineData(IgnoreOptions.All)] + public void Visit_EnumValueArgument_VisitsOneEnumValue(IgnoreOptions options) + { + using var d = "{ stuff(id : TEST_ENUM) }".Parse(new ParserOptions { Ignore = options }); + _visitor.Visit(d, Context); + + Context.VisitedEnumValues.ShouldHaveSingleItem(); + } + + [Theory] + [InlineData(IgnoreOptions.None)] + [InlineData(IgnoreOptions.Comments)] + [InlineData(IgnoreOptions.Locations)] + [InlineData(IgnoreOptions.All)] + public void Visit_FloatValueArgument_VisitsOneFloatValue(IgnoreOptions options) + { + using var d = "{ stuff(id : 1.2) }".Parse(new ParserOptions { Ignore = options }); + _visitor.Visit(d, Context); + + Context.VisitedFloatValues.ShouldHaveSingleItem(); + } + + [Theory] + [InlineData(IgnoreOptions.None)] + [InlineData(IgnoreOptions.Comments)] + [InlineData(IgnoreOptions.Locations)] + [InlineData(IgnoreOptions.All)] + public void Visit_FragmentWithTypeCondition_VisitsFragmentDefinitionOnce(IgnoreOptions options) + { + using var d = "fragment testFragment on Stuff { field }".Parse(new ParserOptions { Ignore = options }); + _visitor.Visit(d, Context); + + Context.VisitedFragmentDefinitions.ShouldHaveSingleItem(); + } + + [Theory] + [InlineData(IgnoreOptions.None)] + [InlineData(IgnoreOptions.Comments)] + [InlineData(IgnoreOptions.Locations)] + [InlineData(IgnoreOptions.All)] + public void Visit_FragmentWithTypeCondition_VisitsTypeConditionOnce(IgnoreOptions options) + { + using var d = "fragment testFragment on Stuff { field }".Parse(new ParserOptions { Ignore = options }); + _visitor.Visit(d, Context); + + Context.VisitedFragmentTypeConditions.ShouldHaveSingleItem(); + } + + [Theory] + [InlineData(IgnoreOptions.None)] + [InlineData(IgnoreOptions.Comments)] + [InlineData(IgnoreOptions.Locations)] + [InlineData(IgnoreOptions.All)] + public void Visit_InlineFragmentWithDirectiveAndArgument_VisitsArgumentsOnce(IgnoreOptions options) + { + using var d = "{ ... @include(if : $stuff) { field } }".Parse(new ParserOptions { Ignore = options }); + _visitor.Visit(d, Context); + + Context.VisitedArguments.ShouldHaveSingleItem(); + } + + [Theory] + [InlineData(IgnoreOptions.None)] + [InlineData(IgnoreOptions.Comments)] + [InlineData(IgnoreOptions.Locations)] + [InlineData(IgnoreOptions.All)] + public void Visit_InlineFragmentWithDirectiveAndArgument_VisitsDirectiveOnce(IgnoreOptions options) + { + using var d = "{ ... @include(if : $stuff) { field } }".Parse(new ParserOptions { Ignore = options }); + _visitor.Visit(d, Context); + + Context.VisitedDirectives.ShouldHaveSingleItem(); + } + + [Theory] + [InlineData(IgnoreOptions.None)] + [InlineData(IgnoreOptions.Comments)] + [InlineData(IgnoreOptions.Locations)] + [InlineData(IgnoreOptions.All)] + public void Visit_InlineFragmentWithDirectiveAndArgument_VisitsNameThreeTimes(IgnoreOptions options) + { + using var d = "{ ... @include(if : $stuff) { field } }".Parse(new ParserOptions { Ignore = options }); + _visitor.Visit(d, Context); + + Context.VisitedNames.Count.ShouldBe(4); + } + + [Theory] + [InlineData(IgnoreOptions.None)] + [InlineData(IgnoreOptions.Comments)] + [InlineData(IgnoreOptions.Locations)] + [InlineData(IgnoreOptions.All)] + public void Visit_InlineFragmentWithOneField_VisitsOneField(IgnoreOptions options) + { + using var d = "{ ... @include(if : $stuff) { field } }".Parse(new ParserOptions { Ignore = options }); + _visitor.Visit(d, Context); + + Context.VisitedFields.ShouldHaveSingleItem(); + } + + [Theory] + [InlineData(IgnoreOptions.None)] + [InlineData(IgnoreOptions.Comments)] + [InlineData(IgnoreOptions.Locations)] + [InlineData(IgnoreOptions.All)] + public void Visit_InlineFragmentWithTypeCondition_VisitsInlineFragmentOnce(IgnoreOptions options) + { + using var d = "{ ... on Stuff { field } }".Parse(new ParserOptions { Ignore = options }); + _visitor.Visit(d, Context); + + Context.VisitedInlineFragments.ShouldHaveSingleItem(); + } + + [Theory] + [InlineData(IgnoreOptions.None)] + [InlineData(IgnoreOptions.Comments)] + [InlineData(IgnoreOptions.Locations)] + [InlineData(IgnoreOptions.All)] + public void Visit_InlineFragmentWithTypeCondition_VisitsTypeConditionOnce(IgnoreOptions options) + { + using var d = "{ ... on Stuff { field } }".Parse(new ParserOptions { Ignore = options }); + _visitor.Visit(d, Context); + + Context.VisitedFragmentTypeConditions.ShouldHaveSingleItem(); + } + + [Theory] + [InlineData(IgnoreOptions.None)] + [InlineData(IgnoreOptions.Comments)] + [InlineData(IgnoreOptions.Locations)] + [InlineData(IgnoreOptions.All)] + public void Visit_IntValueArgument_VisitsOneIntValue(IgnoreOptions options) + { + using var d = "{ stuff(id : 1) }".Parse(new ParserOptions { Ignore = options }); + _visitor.Visit(d, Context); + + Context.VisitedIntValues.ShouldHaveSingleItem(); + } + + [Theory] + [InlineData(IgnoreOptions.None)] + [InlineData(IgnoreOptions.Comments)] + [InlineData(IgnoreOptions.Locations)] + [InlineData(IgnoreOptions.All)] + public void Visit_OneDefinition_CallsVisitDefinitionOnce(IgnoreOptions options) + { + using var d = "{ a }".Parse(new ParserOptions { Ignore = options }); + _visitor.Visit(d, Context); + + Context.VisitedDefinitions.ShouldHaveSingleItem(); + } + + [Theory] + [InlineData(IgnoreOptions.None)] + [InlineData(IgnoreOptions.Comments)] + [InlineData(IgnoreOptions.Locations)] + [InlineData(IgnoreOptions.All)] + public void Visit_OneDefinition_ProvidesCorrectDefinitionAsParameter(IgnoreOptions options) + { + using var d = "{ a }".Parse(new ParserOptions { Ignore = options }); + _visitor.Visit(d, Context); + + Context.VisitedDefinitions.Single().ShouldBe(d.Definitions.Single()); + } + + [Theory] + [InlineData(IgnoreOptions.None)] + [InlineData(IgnoreOptions.Comments)] + [InlineData(IgnoreOptions.Locations)] + [InlineData(IgnoreOptions.All)] + public void Visit_OneDefinition_VisitsOneSelectionSet(IgnoreOptions options) + { + using var d = "{ a, b }".Parse(new ParserOptions { Ignore = options }); + _visitor.Visit(d, Context); + + Context.VisitedSelectionSets.ShouldHaveSingleItem(); + } + + [Theory] + [InlineData(IgnoreOptions.None)] + [InlineData(IgnoreOptions.Comments)] + [InlineData(IgnoreOptions.Locations)] + [InlineData(IgnoreOptions.All)] + public void Visit_OneDefinitionWithOneAliasedField_VisitsOneAlias(IgnoreOptions options) + { + using var d = "{ foo, foo : bar }".Parse(new ParserOptions { Ignore = options }); + _visitor.Visit(d, Context); + + Context.VisitedAliases.ShouldHaveSingleItem(); + } + + [Theory] + [InlineData(IgnoreOptions.None)] + [InlineData(IgnoreOptions.Comments)] + [InlineData(IgnoreOptions.Locations)] + [InlineData(IgnoreOptions.All)] + public void Visit_OneDefinitionWithOneArgument_VisitsOneArgument(IgnoreOptions options) + { + using var d = "{ foo(id : 1) { name } }".Parse(new ParserOptions { Ignore = options }); + _visitor.Visit(d, Context); + + Context.VisitedArguments.ShouldHaveSingleItem(); + } + + [Theory] + [InlineData(IgnoreOptions.None)] + [InlineData(IgnoreOptions.Comments)] + [InlineData(IgnoreOptions.Locations)] + [InlineData(IgnoreOptions.All)] + public void Visit_OneDefinitionWithOneNestedArgument_VisitsOneArgument(IgnoreOptions options) + { + using var d = "{ foo{ names(size: 10) } }".Parse(new ParserOptions { Ignore = options }); + _visitor.Visit(d, Context); + + Context.VisitedArguments.ShouldHaveSingleItem(); + } + + [Theory] + [InlineData(IgnoreOptions.None)] + [InlineData(IgnoreOptions.Comments)] + [InlineData(IgnoreOptions.Locations)] + [InlineData(IgnoreOptions.All)] + public void Visit_StringValueArgument_VisitsOneStringValue(IgnoreOptions options) + { + using var d = "{ stuff(id : \"abc\") }".Parse(new ParserOptions { Ignore = options }); + _visitor.Visit(d, Context); + + Context.VisitedStringValues.ShouldHaveSingleItem(); + } + + [Theory] + [InlineData(IgnoreOptions.None)] + [InlineData(IgnoreOptions.Comments)] + [InlineData(IgnoreOptions.Locations)] + [InlineData(IgnoreOptions.All)] + public void Visit_TwoDefinitions_CallsVisitDefinitionTwice(IgnoreOptions options) + { + using var d = "{ a }\n{ b }".Parse(new ParserOptions { Ignore = options }); + _visitor.Visit(d, Context); + + Context.VisitedDefinitions.Count.ShouldBe(2); + } + + [Theory] + [InlineData(IgnoreOptions.None)] + [InlineData(IgnoreOptions.Comments)] + [InlineData(IgnoreOptions.Locations)] + [InlineData(IgnoreOptions.All)] + public void Visit_TwoFields_VisitsFieldTwice(IgnoreOptions options) + { + using var d = "{ a, b }".Parse(new ParserOptions { Ignore = options }); + _visitor.Visit(d, Context); + + Context.VisitedFields.Count.ShouldBe(2); + } + + [Theory] + [InlineData(IgnoreOptions.None)] + [InlineData(IgnoreOptions.Comments)] + [InlineData(IgnoreOptions.Locations)] + [InlineData(IgnoreOptions.All)] + public void Visit_TwoFields_VisitsTwoFieldNames(IgnoreOptions options) + { + using var d = "{ a, b }".Parse(new ParserOptions { Ignore = options }); + _visitor.Visit(d, Context); + + Context.VisitedNames.Count.ShouldBe(2); + } + + [Theory] + [InlineData(IgnoreOptions.None)] + [InlineData(IgnoreOptions.Comments)] + [InlineData(IgnoreOptions.Locations)] + [InlineData(IgnoreOptions.All)] + public void Visit_TwoFields_VisitsTwoFieldNamesAndDefinitionName(IgnoreOptions options) + { + using var d = "query foo { a, b }".Parse(new ParserOptions { Ignore = options }); + _visitor.Visit(d, Context); + + Context.VisitedNames.Count.ShouldBe(3); + } + + [Theory] + [InlineData(IgnoreOptions.None)] + [InlineData(IgnoreOptions.Comments)] + [InlineData(IgnoreOptions.Locations)] + [InlineData(IgnoreOptions.All)] + public void Visit_TwoFieldsWithOneNested_VisitsFiveFields(IgnoreOptions options) + { + using var d = "{a, nested { x, y }, b}".Parse(new ParserOptions { Ignore = options }); + _visitor.Visit(d, Context); + + Context.VisitedFields.Count.ShouldBe(5); + } + + [Theory] + [InlineData(IgnoreOptions.None)] + [InlineData(IgnoreOptions.Comments)] + [InlineData(IgnoreOptions.Locations)] + [InlineData(IgnoreOptions.All)] + public void Visit_TwoFieldsWithOneNested_VisitsFiveNames(IgnoreOptions options) + { + using var d = "{a, nested { x, y }, b}".Parse(new ParserOptions { Ignore = options }); + _visitor.Visit(d, Context); + + Context.VisitedNames.Count.ShouldBe(5); + } + } +} diff --git a/src/GraphQLParser.Tests/Visitors/SDLWriterTests.cs b/src/GraphQLParser.Tests/Visitors/SDLWriterTests.cs new file mode 100644 index 00000000..4e9236a4 --- /dev/null +++ b/src/GraphQLParser.Tests/Visitors/SDLWriterTests.cs @@ -0,0 +1,330 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using GraphQLParser.Visitors; +using Shouldly; +using Xunit; + +namespace GraphQLParser.Tests.Visitors +{ + public class SDLWriterTests + { + private class TestContext : IWriteContext + { + public TextWriter Writer { get; set; } = new StringWriter(); + + public Stack Parents { get; set; } = new Stack(); + + public CancellationToken CancellationToken { get; set; } + } + + private static readonly SDLWriter _sdlWriter = new(); + + [Theory] + [InlineData(@"#comment +input Example @x { + self: [Example!]! + value: String = ""xyz"" +} +input B +input C +", +@"#comment +input Example @x +{ + self: [Example!]! + value: String = ""xyz"" +} + +input B + +input C +")] + [InlineData(@"query inlineFragmentTyping { + profiles(handles: [""zuck"", ""coca - cola""]) { + handle + ... on User { + friends { + count + } +} +... on Page +{ + likers + { + count + } +} + } +} +", @"query inlineFragmentTyping +{ + profiles(handles: [""zuck"", ""coca - cola""]) + { + handle + ... on User + { + friends + { + count + } + } + ... on Page + { + likers + { + count + } + } + } +} +")] + [InlineData(@"scalar a scalar b scalar c", @"scalar a + +scalar b + +scalar c +")] + [InlineData(@"{ + foo + #comment on fragment + ...Frag + qux +} + +fragment Frag on Query { + bar + baz +} +", + +@" +{ + foo + #comment on fragment + ...Frag + qux +} + +fragment Frag on Query +{ + bar + baz +} +")] + [InlineData(@"union Animal @immutable = |Cat | Dog", @"union Animal @immutable = Cat | Dog")] + [InlineData(@"query + q +{ + a : name + b + c : age +}", @"query q +{ + a: name + b + c: age +} +")] + [InlineData(@"schema @checked { mutation: MyMutation subscription: MySub }", @"schema @checked +{ + mutation: MyMutation + subscription: MySub +} +")] + [InlineData(@"interface Dog implements & Eat & Bark { volume: Int! }", + @"interface Dog implements Eat & Bark +{ + volume: Int! +} +")] + [InlineData(@"enum Color { RED, +#good color +GREEN @directive(list: [1,2,3,null,{}, {name:""tom"" age:42}]), +"""""" +another good color +"""""" +BLUE }", +@"enum Color +{ + RED + #good color + GREEN @directive(list: [1, 2, 3, null, { }, { name: ""tom"", age: 42 }]) + ""another good color"" + BLUE +} +")] + [InlineData(@"# super query +# +# multiline +query summary($id: ID!) { name(full:true,kind: UPPER) age1:age address { street @short(length:5,x:""a"", pi: 3.14) +#need +building } }", + +@"# super query +# +# multiline +query summary($id: ID!) +{ + name(full: true, kind: UPPER) + age1: age + address + { + street @short(length: 5, x: ""a"", pi: 3.14) + #need + building + } +} +")] + [InlineData(@" +"""""" + description + indent 2 + indent4 +"""""" +scalar JSON @exportable +# A dog +type Dog implements &Animal { + """"""inline docs"""""" + volume: Float + """""" + multiline + docs + """""" + friends: [Dog!] + age: Int! +}", + +@""""""" +description + indent 2 + indent4 +"""""" +scalar JSON @exportable + +# A dog +type Dog implements Animal +{ + ""inline docs"" + volume: Float + """""" + multiline + docs + """""" + friends: [Dog!] + age: Int! +} +")] + public async Task WriteDocumentVisitor_Should_Print_Document(string text, string expected) + { + var context = new TestContext(); + + using (var document = text.Parse()) + { + await _sdlWriter.Visit(document, context).ConfigureAwait(false); + var actual = context.Writer.ToString(); + actual.ShouldBe(expected); + + using (var parsedBack = actual.Parse()) + { + // should be parsed back + } + } + } + + [Theory] + [InlineData(1, "test", "test", false)] + [InlineData(2, "te\\\"\"\"st", "te\\\"\\\"\\\"st", false)] + [InlineData(3, "\ntest", "test", false)] + [InlineData(4, "\r\ntest", "test", false)] + [InlineData(5, " \ntest", "test", false)] + [InlineData(6, "\t\ntest", "test", false)] + [InlineData(7, "\n\ntest", "test", false)] + [InlineData(8, "\ntest\nline2", "\ntest\nline2\n", true)] + [InlineData(9, "test\rline2", "\ntest\nline2\n", true)] + [InlineData(10, "test\r\nline2", "\ntest\nline2\n", true)] + [InlineData(11, "test\r\r\nline2", "\ntest\n\nline2\n", true)] + [InlineData(12, "test\r\n\nline2", "\ntest\n\nline2\n", true)] + [InlineData(13, "test\n", "test", false)] + [InlineData(14, "test\n ", "test", false)] + [InlineData(15, "test\n\t", "test", false)] + [InlineData(16, "test\n\n", "test", false)] + [InlineData(17, "test\n line2", "\ntest\nline2\n", true)] + [InlineData(18, "test\n\t\tline2", "\ntest\nline2\n", true)] + [InlineData(19, "test\n \tline2", "\ntest\nline2\n", true)] + [InlineData(20, " test\nline2", "\n test\nline2\n", true)] + [InlineData(21, " test\n line2", "\n test\nline2\n", true)] + [InlineData(22, "\n test\n line2", "\ntest\nline2\n", true)] + [InlineData(23, " test\n line2\n\t\tline3\n line4", "\n test\nline2\n\tline3\n line4\n", true)] + [InlineData(24, " test\n Hello,\n\n world!\n ", "\n test\nHello,\n\n world!\n", true)] + [InlineData(25, " \n Hello,\r\n\n world!\n ", "\nHello,\n\n world!\n", true)] + [InlineData(26, " \n Hello,\r\n\n wor___ld!\n ", "\nHello,\n\n wor___ld!\n", true)] + [InlineData(27, "\r\n Hello,\r\n World!\r\n\r\n Yours,\r\n GraphQL.\r\n ", "\nHello,\n World!\n\nYours,\n GraphQL.\n", true)] + [InlineData(28, "Test \\n escaping", "Test \\\\n escaping", false)] + [InlineData(29, "Test \\u1234 escaping", "Test \\\\u1234 escaping", false)] + [InlineData(30, "Test \\ escaping", "Test \\\\ escaping", false)] + public async Task WriteDocumentVisitor_Should_Print_BlockStrings(int number, string input, string expected, bool isBlockString) + { + number.ShouldBeGreaterThan(0); + + input = input.Replace("___", new string('_', 9000)); + expected = expected.Replace("___", new string('_', 9000)); + + input = "\"\"\"" + input + "\"\"\""; + if (isBlockString) + expected = "\"\"\"" + expected + "\"\"\""; + else + expected = "\"" + expected + "\""; + + var context = new TestContext(); + + using (var document = (input + " scalar a").Parse()) + { + await _sdlWriter.Visit(document, context).ConfigureAwait(false); + var renderedOriginal = context.Writer.ToString(); + + var lines = renderedOriginal.Split(Environment.NewLine); + var renderedDescription = string.Join(Environment.NewLine, lines.SkipLast(2)); + renderedDescription = renderedDescription.Replace("\r\n", "\n"); + renderedDescription.ShouldBe(expected); + + using (var parsedBack = renderedOriginal.Parse()) + { + // should be parsed back + } + } + } + + [Theory] + [InlineData("\"\"")] + [InlineData("\"\\\\\"")] + [InlineData("\"\\n\\b\\f\\r\\t\"")] + [InlineData("\" \u1234 \"")] + [InlineData("\"normal text\"")] + public async Task WriteDocumentVisitor_Should_Print_EscapedStrings(string stringValue) + { + string query = $"{{a(p:{stringValue})}}"; + string expected = @$" +{{ + a(p: {stringValue}) +}} +"; + var context = new TestContext(); + + using (var document = query.Parse()) + { + await _sdlWriter.Visit(document, context).ConfigureAwait(false); + var rendered = context.Writer.ToString(); + rendered.ShouldBe(expected); + + using (var parsedBack = rendered.Parse()) + { + // should be parsed back + } + } + } + } +} diff --git a/src/GraphQLParser.Tests/Visitors/StructureWriterTests.cs b/src/GraphQLParser.Tests/Visitors/StructureWriterTests.cs new file mode 100644 index 00000000..f71f1c74 --- /dev/null +++ b/src/GraphQLParser.Tests/Visitors/StructureWriterTests.cs @@ -0,0 +1,52 @@ +using System.Collections.Generic; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using GraphQLParser.Visitors; +using Shouldly; +using Xunit; + +namespace GraphQLParser.Tests.Visitors +{ + public class StructureWriterTests + { + private class TestContext : IWriteContext + { + public TextWriter Writer { get; set; } = new StringWriter(); + + public Stack Parents { get; set; } = new Stack(); + + public CancellationToken CancellationToken { get; set; } + } + + private static readonly StructureWriter _structWriter = new(new StructureWriterOptions()); + + [Theory] + [InlineData("query a { name age }", @"Document + OperationDefinition + Name [a] + SelectionSet + Field + Name [name] + Field + Name [age] +")] + [InlineData("scalar JSON @exportable", @"Document + ScalarTypeDefinition + Name [JSON] + Directive + Name [exportable] +")] + public async Task WriteTreeVisitor_Should_Print_Tree(string text, string expected) + { + var context = new TestContext(); + + using (var document = text.Parse()) + { + await _structWriter.Visit(document, context).ConfigureAwait(false); + var actual = context.Writer.ToString(); + actual.ShouldBe(expected); + } + } + } +} diff --git a/src/GraphQLParser/AST/ASTNodeKind.cs b/src/GraphQLParser/AST/ASTNodeKind.cs index fdeee4e9..7b278cdb 100644 --- a/src/GraphQLParser/AST/ASTNodeKind.cs +++ b/src/GraphQLParser/AST/ASTNodeKind.cs @@ -296,8 +296,10 @@ public enum ASTNodeKind /// InputObjectTypeDefinition, - // TODO: change - TypeExtensionDefinition, + /// + /// TO BE DONE + /// + TypeExtensionDefinition, // TODO: change /// /// Directive definition. Directives provide a way to describe alternate runtime execution @@ -325,5 +327,21 @@ public enum ASTNodeKind /// /// Description, + + /// + /// Fragments must specify the type they apply to. + ///
+ /// + ///
+ TypeCondition, + + /// + /// By default a field's response key in the response object will use that + /// field's name. However, you can define a different response key by + /// specifying an alias.Fragments must specify the type they apply to. + ///
+ /// + ///
+ Alias, } } diff --git a/src/GraphQLParser/AST/GraphQLAlias.cs b/src/GraphQLParser/AST/GraphQLAlias.cs new file mode 100644 index 00000000..79d20d09 --- /dev/null +++ b/src/GraphQLParser/AST/GraphQLAlias.cs @@ -0,0 +1,56 @@ +namespace GraphQLParser.AST +{ + /// + /// AST node for . + /// + public class GraphQLAlias : ASTNode + { + /// + public override ASTNodeKind Kind => ASTNodeKind.TypeCondition; + + /// + /// Alias name. + /// + public GraphQLName? Name { get; set; } + } + + internal sealed class GraphQLAliasWithLocation : GraphQLAlias + { + private GraphQLLocation _location; + + public override GraphQLLocation Location + { + get => _location; + set => _location = value; + } + } + + internal sealed class GraphQLAliasWithComment : GraphQLAlias + { + private GraphQLComment? _comment; + + public override GraphQLComment? Comment + { + get => _comment; + set => _comment = value; + } + } + + internal sealed class GraphQLAliasFull : GraphQLAlias + { + private GraphQLLocation _location; + private GraphQLComment? _comment; + + public override GraphQLLocation Location + { + get => _location; + set => _location = value; + } + + public override GraphQLComment? Comment + { + get => _comment; + set => _comment = value; + } + } +} diff --git a/src/GraphQLParser/AST/GraphQLArgument.cs b/src/GraphQLParser/AST/GraphQLArgument.cs index 0a961f49..c0bc6e64 100644 --- a/src/GraphQLParser/AST/GraphQLArgument.cs +++ b/src/GraphQLParser/AST/GraphQLArgument.cs @@ -1,5 +1,8 @@ namespace GraphQLParser.AST { + /// + /// AST node for . + /// public class GraphQLArgument : ASTNode, INamedNode { /// diff --git a/src/GraphQLParser/AST/GraphQLComment.cs b/src/GraphQLParser/AST/GraphQLComment.cs index 942a1081..ec053ec6 100644 --- a/src/GraphQLParser/AST/GraphQLComment.cs +++ b/src/GraphQLParser/AST/GraphQLComment.cs @@ -2,7 +2,9 @@ namespace GraphQLParser.AST { - /// + /// + /// AST node for . + /// [DebuggerDisplay("{Text}")] public class GraphQLComment : ASTNode { diff --git a/src/GraphQLParser/AST/GraphQLDescription.cs b/src/GraphQLParser/AST/GraphQLDescription.cs index 8c620d4b..befcf2ce 100644 --- a/src/GraphQLParser/AST/GraphQLDescription.cs +++ b/src/GraphQLParser/AST/GraphQLDescription.cs @@ -2,7 +2,9 @@ namespace GraphQLParser.AST { - /// + /// + /// AST node for . + /// [DebuggerDisplay("{Value}")] public class GraphQLDescription : ASTNode { diff --git a/src/GraphQLParser/AST/GraphQLDirective.cs b/src/GraphQLParser/AST/GraphQLDirective.cs index a20d5279..5ab863c5 100644 --- a/src/GraphQLParser/AST/GraphQLDirective.cs +++ b/src/GraphQLParser/AST/GraphQLDirective.cs @@ -3,17 +3,20 @@ namespace GraphQLParser.AST { /// - /// Represents a directive, applied to some GraphQL element. + /// AST node for . /// - public class GraphQLDirective : ASTNode, INamedNode + public class GraphQLDirective : ASTNode, INamedNode, IHasArgumentsNode { - public List? Arguments { get; set; } - /// public override ASTNodeKind Kind => ASTNodeKind.Directive; /// public GraphQLName? Name { get; set; } + + /// + /// List of arguments for this directive. + /// + public List? Arguments { get; set; } } internal sealed class GraphQLDirectiveWithLocation : GraphQLDirective diff --git a/src/GraphQLParser/AST/GraphQLDirectiveDefinition.cs b/src/GraphQLParser/AST/GraphQLDirectiveDefinition.cs index a27dcda1..ee985066 100644 --- a/src/GraphQLParser/AST/GraphQLDirectiveDefinition.cs +++ b/src/GraphQLParser/AST/GraphQLDirectiveDefinition.cs @@ -3,19 +3,17 @@ namespace GraphQLParser.AST { /// - /// Represents a directive definition. + /// AST node for . /// public class GraphQLDirectiveDefinition : GraphQLTypeDefinition { - public List? Arguments { get; set; } - /// public override ASTNodeKind Kind => ASTNodeKind.DirectiveDefinition; /// - /// Returns a list of locations representing the valid locations this directive may be placed. + /// List of arguments for this directive definition. /// - public List? Locations { get; set; } + public List? Arguments { get; set; } /// /// Indicates if the directive may be used repeatedly at a single location. @@ -26,6 +24,11 @@ public class GraphQLDirectiveDefinition : GraphQLTypeDefinition /// provided to a type or schema extension via a directive /// public bool Repeatable { get; set; } + + /// + /// Returns a list of locations representing the valid locations this directive may be placed. + /// + public List? Locations { get; set; } } internal sealed class GraphQLDirectiveDefinitionWithLocation : GraphQLDirectiveDefinition diff --git a/src/GraphQLParser/AST/GraphQLDocument.cs b/src/GraphQLParser/AST/GraphQLDocument.cs index 21383763..4215b748 100644 --- a/src/GraphQLParser/AST/GraphQLDocument.cs +++ b/src/GraphQLParser/AST/GraphQLDocument.cs @@ -5,14 +5,12 @@ namespace GraphQLParser.AST { /// - /// Represents the root of AST (Abstract Syntax Tree) for GraphQL document. + /// AST node for . /// public class GraphQLDocument : ASTNode, IDisposable { - // In some cases, the parser is forced to change the text (escape symbols, comments), - // so it is impossible to simply point to the desired section (span) of the source text. - // In this case, array pools are used, memory from which then need to be returned to the pool. - internal List<(IMemoryOwner owner, ASTNode rentedBy)>? RentedMemoryTracker { get; set; } + /// + public override ASTNodeKind Kind => ASTNodeKind.Document; public List? Definitions { get; set; } @@ -21,8 +19,10 @@ public class GraphQLDocument : ASTNode, IDisposable /// public List? UnattachedComments { get; set; } - /// - public override ASTNodeKind Kind => ASTNodeKind.Document; + // In some cases, the parser is forced to change the text (escape symbols, comments), + // so it is impossible to simply point to the desired section (span) of the source text. + // In this case, array pools are used, memory from which then need to be returned to the pool. + internal List<(IMemoryOwner owner, ASTNode rentedBy)>? RentedMemoryTracker { get; set; } protected virtual void Dispose(bool disposing) { diff --git a/src/GraphQLParser/AST/GraphQLEnumTypeDefinition.cs b/src/GraphQLParser/AST/GraphQLEnumTypeDefinition.cs index 1d61d691..1afd2c13 100644 --- a/src/GraphQLParser/AST/GraphQLEnumTypeDefinition.cs +++ b/src/GraphQLParser/AST/GraphQLEnumTypeDefinition.cs @@ -2,13 +2,16 @@ namespace GraphQLParser.AST { + /// + /// AST node for . + /// public class GraphQLEnumTypeDefinition : GraphQLTypeDefinition, IHasDirectivesNode { /// - public List? Directives { get; set; } + public override ASTNodeKind Kind => ASTNodeKind.EnumTypeDefinition; /// - public override ASTNodeKind Kind => ASTNodeKind.EnumTypeDefinition; + public List? Directives { get; set; } public List? Values { get; set; } } diff --git a/src/GraphQLParser/AST/GraphQLEnumValueDefinition.cs b/src/GraphQLParser/AST/GraphQLEnumValueDefinition.cs index d7b3230f..f6c7ee17 100644 --- a/src/GraphQLParser/AST/GraphQLEnumValueDefinition.cs +++ b/src/GraphQLParser/AST/GraphQLEnumValueDefinition.cs @@ -2,13 +2,16 @@ namespace GraphQLParser.AST { + /// + /// AST node for . + /// public class GraphQLEnumValueDefinition : GraphQLTypeDefinition, IHasDirectivesNode { /// - public List? Directives { get; set; } + public override ASTNodeKind Kind => ASTNodeKind.EnumValueDefinition; /// - public override ASTNodeKind Kind => ASTNodeKind.EnumValueDefinition; + public List? Directives { get; set; } } internal sealed class GraphQLEnumValueDefinitionWithLocation : GraphQLEnumValueDefinition diff --git a/src/GraphQLParser/AST/GraphQLField.cs b/src/GraphQLParser/AST/GraphQLField.cs index 993e8c63..3ffd26b0 100644 --- a/src/GraphQLParser/AST/GraphQLField.cs +++ b/src/GraphQLParser/AST/GraphQLField.cs @@ -2,20 +2,23 @@ namespace GraphQLParser.AST { - public class GraphQLField : ASTNode, IHasDirectivesNode, INamedNode + /// + /// AST node for . + /// + public class GraphQLField : ASTNode, IHasDirectivesNode, IHasArgumentsNode, INamedNode { - public GraphQLName? Alias { get; set; } + /// + public override ASTNodeKind Kind => ASTNodeKind.Field; - public List? Arguments { get; set; } + public GraphQLAlias? Alias { get; set; } /// - public List? Directives { get; set; } + public GraphQLName? Name { get; set; } - /// - public override ASTNodeKind Kind => ASTNodeKind.Field; + public List? Arguments { get; set; } /// - public GraphQLName? Name { get; set; } + public List? Directives { get; set; } public GraphQLSelectionSet? SelectionSet { get; set; } } diff --git a/src/GraphQLParser/AST/GraphQLFieldDefinition.cs b/src/GraphQLParser/AST/GraphQLFieldDefinition.cs index f637d477..23b4f0f9 100644 --- a/src/GraphQLParser/AST/GraphQLFieldDefinition.cs +++ b/src/GraphQLParser/AST/GraphQLFieldDefinition.cs @@ -2,17 +2,20 @@ namespace GraphQLParser.AST { + /// + /// AST node for . + /// public class GraphQLFieldDefinition : GraphQLTypeDefinition, IHasDirectivesNode { - public List? Arguments { get; set; } - - /// - public List? Directives { get; set; } - /// public override ASTNodeKind Kind => ASTNodeKind.FieldDefinition; + public List? Arguments { get; set; } + public GraphQLType? Type { get; set; } + + /// + public List? Directives { get; set; } } internal sealed class GraphQLFieldDefinitionWithLocation : GraphQLFieldDefinition diff --git a/src/GraphQLParser/AST/GraphQLFragmentDefinition.cs b/src/GraphQLParser/AST/GraphQLFragmentDefinition.cs index 120d4040..b07f0fce 100644 --- a/src/GraphQLParser/AST/GraphQLFragmentDefinition.cs +++ b/src/GraphQLParser/AST/GraphQLFragmentDefinition.cs @@ -1,5 +1,8 @@ namespace GraphQLParser.AST { + /// + /// AST node for . + /// public class GraphQLFragmentDefinition : GraphQLInlineFragment, INamedNode { /// diff --git a/src/GraphQLParser/AST/GraphQLFragmentSpread.cs b/src/GraphQLParser/AST/GraphQLFragmentSpread.cs index 2604677d..cd35f3ab 100644 --- a/src/GraphQLParser/AST/GraphQLFragmentSpread.cs +++ b/src/GraphQLParser/AST/GraphQLFragmentSpread.cs @@ -2,16 +2,19 @@ namespace GraphQLParser.AST { + /// + /// AST node for . + /// public class GraphQLFragmentSpread : ASTNode, IHasDirectivesNode, INamedNode { - /// - public List? Directives { get; set; } - /// public override ASTNodeKind Kind => ASTNodeKind.FragmentSpread; /// public GraphQLName? Name { get; set; } + + /// + public List? Directives { get; set; } } internal sealed class GraphQLFragmentSpreadWithLocation : GraphQLFragmentSpread diff --git a/src/GraphQLParser/AST/GraphQLInlineFragment.cs b/src/GraphQLParser/AST/GraphQLInlineFragment.cs index 79c431d3..a82d8edd 100644 --- a/src/GraphQLParser/AST/GraphQLInlineFragment.cs +++ b/src/GraphQLParser/AST/GraphQLInlineFragment.cs @@ -2,17 +2,20 @@ namespace GraphQLParser.AST { + /// + /// AST node for . + /// public class GraphQLInlineFragment : ASTNode, IHasDirectivesNode { /// - public List? Directives { get; set; } + public override ASTNodeKind Kind => ASTNodeKind.InlineFragment; + + public GraphQLTypeCondition? TypeCondition { get; set; } /// - public override ASTNodeKind Kind => ASTNodeKind.InlineFragment; + public List? Directives { get; set; } public GraphQLSelectionSet? SelectionSet { get; set; } - - public GraphQLNamedType? TypeCondition { get; set; } } internal sealed class GraphQLInlineFragmentWithLocation : GraphQLInlineFragment diff --git a/src/GraphQLParser/AST/GraphQLInputObjectTypeDefinition.cs b/src/GraphQLParser/AST/GraphQLInputObjectTypeDefinition.cs index 12a80d81..9d2da666 100644 --- a/src/GraphQLParser/AST/GraphQLInputObjectTypeDefinition.cs +++ b/src/GraphQLParser/AST/GraphQLInputObjectTypeDefinition.cs @@ -2,15 +2,18 @@ namespace GraphQLParser.AST { + /// + /// AST node for . + /// public class GraphQLInputObjectTypeDefinition : GraphQLTypeDefinition, IHasDirectivesNode { + /// + public override ASTNodeKind Kind => ASTNodeKind.InputObjectTypeDefinition; + /// public List? Directives { get; set; } public List? Fields { get; set; } - - /// - public override ASTNodeKind Kind => ASTNodeKind.InputObjectTypeDefinition; } internal sealed class GraphQLInputObjectTypeDefinitionWithLocation : GraphQLInputObjectTypeDefinition diff --git a/src/GraphQLParser/AST/GraphQLInputValueDefinition.cs b/src/GraphQLParser/AST/GraphQLInputValueDefinition.cs index 9f096589..06a691ad 100644 --- a/src/GraphQLParser/AST/GraphQLInputValueDefinition.cs +++ b/src/GraphQLParser/AST/GraphQLInputValueDefinition.cs @@ -2,17 +2,20 @@ namespace GraphQLParser.AST { + /// + /// AST node for . + /// public class GraphQLInputValueDefinition : GraphQLTypeDefinition, IHasDirectivesNode { - public GraphQLValue? DefaultValue { get; set; } - - /// - public List? Directives { get; set; } - /// public override ASTNodeKind Kind => ASTNodeKind.InputValueDefinition; public GraphQLType? Type { get; set; } + + public GraphQLValue? DefaultValue { get; set; } + + /// + public List? Directives { get; set; } } internal sealed class GraphQLInputValueDefinitionWithLocation : GraphQLInputValueDefinition diff --git a/src/GraphQLParser/AST/GraphQLInterfaceTypeDefinition.cs b/src/GraphQLParser/AST/GraphQLInterfaceTypeDefinition.cs index 2132c513..6d44d4ce 100644 --- a/src/GraphQLParser/AST/GraphQLInterfaceTypeDefinition.cs +++ b/src/GraphQLParser/AST/GraphQLInterfaceTypeDefinition.cs @@ -2,17 +2,20 @@ namespace GraphQLParser.AST { - public class GraphQLInterfaceTypeDefinition : GraphQLTypeDefinition, IHasDirectivesNode + /// + /// AST node for . + /// + public class GraphQLInterfaceTypeDefinition : GraphQLTypeDefinition, IHasDirectivesNode, IHasInterfacesNode { /// - public List? Directives { get; set; } - - public List? Fields { get; set; } + public override ASTNodeKind Kind => ASTNodeKind.InterfaceTypeDefinition; public List? Interfaces { get; set; } /// - public override ASTNodeKind Kind => ASTNodeKind.InterfaceTypeDefinition; + public List? Directives { get; set; } + + public List? Fields { get; set; } } internal sealed class GraphQLInterfaceTypeDefinitionWithLocation : GraphQLInterfaceTypeDefinition diff --git a/src/GraphQLParser/AST/GraphQLListType.cs b/src/GraphQLParser/AST/GraphQLListType.cs index 9b3c70ec..26603e0c 100644 --- a/src/GraphQLParser/AST/GraphQLListType.cs +++ b/src/GraphQLParser/AST/GraphQLListType.cs @@ -1,5 +1,8 @@ namespace GraphQLParser.AST { + /// + /// AST node for . + /// public class GraphQLListType : GraphQLType { /// diff --git a/src/GraphQLParser/AST/GraphQLListValue.cs b/src/GraphQLParser/AST/GraphQLListValue.cs index 4b65e5bb..62aa1750 100644 --- a/src/GraphQLParser/AST/GraphQLListValue.cs +++ b/src/GraphQLParser/AST/GraphQLListValue.cs @@ -2,20 +2,16 @@ namespace GraphQLParser.AST { + /// + /// AST node for . + /// public class GraphQLListValue : GraphQLValue { - private readonly ASTNodeKind _kind; - - public GraphQLListValue(ASTNodeKind kind) - { - _kind = kind; - } + /// + public override ASTNodeKind Kind => ASTNodeKind.ListValue; public ROM AstValue { get; set; } - /// - public override ASTNodeKind Kind => _kind; - public List? Values { get; set; } /// @@ -26,11 +22,6 @@ internal sealed class GraphQLListValueWithLocation : GraphQLListValue { private GraphQLLocation _location; - public GraphQLListValueWithLocation(ASTNodeKind kind) - : base(kind) - { - } - public override GraphQLLocation Location { get => _location; @@ -42,11 +33,6 @@ internal sealed class GraphQLListValueWithComment : GraphQLListValue { private GraphQLComment? _comment; - public GraphQLListValueWithComment(ASTNodeKind kind) - : base(kind) - { - } - public override GraphQLComment? Comment { get => _comment; @@ -59,11 +45,6 @@ internal sealed class GraphQLListValueFull : GraphQLListValue private GraphQLLocation _location; private GraphQLComment? _comment; - public GraphQLListValueFull(ASTNodeKind kind) - : base(kind) - { - } - public override GraphQLLocation Location { get => _location; diff --git a/src/GraphQLParser/AST/GraphQLName.cs b/src/GraphQLParser/AST/GraphQLName.cs index 2a64ddb2..882795a9 100644 --- a/src/GraphQLParser/AST/GraphQLName.cs +++ b/src/GraphQLParser/AST/GraphQLName.cs @@ -2,7 +2,9 @@ namespace GraphQLParser.AST { - /// + /// + /// AST node for . + /// [DebuggerDisplay("{Value}")] public class GraphQLName : ASTNode { diff --git a/src/GraphQLParser/AST/GraphQLNamedType.cs b/src/GraphQLParser/AST/GraphQLNamedType.cs index 836c77db..8fcbe6e5 100644 --- a/src/GraphQLParser/AST/GraphQLNamedType.cs +++ b/src/GraphQLParser/AST/GraphQLNamedType.cs @@ -1,5 +1,8 @@ namespace GraphQLParser.AST { + /// + /// AST node for . + /// public class GraphQLNamedType : GraphQLType, INamedNode { /// diff --git a/src/GraphQLParser/AST/GraphQLNonNullType.cs b/src/GraphQLParser/AST/GraphQLNonNullType.cs index 8c746356..4970c50f 100644 --- a/src/GraphQLParser/AST/GraphQLNonNullType.cs +++ b/src/GraphQLParser/AST/GraphQLNonNullType.cs @@ -1,5 +1,8 @@ namespace GraphQLParser.AST { + /// + /// AST node for . + /// public class GraphQLNonNullType : GraphQLType { /// diff --git a/src/GraphQLParser/AST/GraphQLObjectField.cs b/src/GraphQLParser/AST/GraphQLObjectField.cs index 04183bf5..64a1759c 100644 --- a/src/GraphQLParser/AST/GraphQLObjectField.cs +++ b/src/GraphQLParser/AST/GraphQLObjectField.cs @@ -1,5 +1,8 @@ namespace GraphQLParser.AST { + /// + /// AST node for . + /// public class GraphQLObjectField : ASTNode, INamedNode { /// diff --git a/src/GraphQLParser/AST/GraphQLObjectTypeDefinition.cs b/src/GraphQLParser/AST/GraphQLObjectTypeDefinition.cs index 5a55d18e..1e8508ea 100644 --- a/src/GraphQLParser/AST/GraphQLObjectTypeDefinition.cs +++ b/src/GraphQLParser/AST/GraphQLObjectTypeDefinition.cs @@ -2,17 +2,20 @@ namespace GraphQLParser.AST { - public class GraphQLObjectTypeDefinition : GraphQLTypeDefinition, IHasDirectivesNode + /// + /// AST node for . + /// + public class GraphQLObjectTypeDefinition : GraphQLTypeDefinition, IHasDirectivesNode, IHasInterfacesNode { /// - public List? Directives { get; set; } - - public List? Fields { get; set; } + public override ASTNodeKind Kind => ASTNodeKind.ObjectTypeDefinition; public List? Interfaces { get; set; } /// - public override ASTNodeKind Kind => ASTNodeKind.ObjectTypeDefinition; + public List? Directives { get; set; } + + public List? Fields { get; set; } } internal sealed class GraphQLObjectTypeDefinitionWithLocation : GraphQLObjectTypeDefinition diff --git a/src/GraphQLParser/AST/GraphQLObjectValue.cs b/src/GraphQLParser/AST/GraphQLObjectValue.cs index fd1f04ce..01f41ccd 100644 --- a/src/GraphQLParser/AST/GraphQLObjectValue.cs +++ b/src/GraphQLParser/AST/GraphQLObjectValue.cs @@ -2,12 +2,15 @@ namespace GraphQLParser.AST { + /// + /// AST node for . + /// public class GraphQLObjectValue : GraphQLValue { - public List? Fields { get; set; } - /// public override ASTNodeKind Kind => ASTNodeKind.ObjectValue; + + public List? Fields { get; set; } } internal sealed class GraphQLObjectValueWithLocation : GraphQLObjectValue diff --git a/src/GraphQLParser/AST/GraphQLOperationDefinition.cs b/src/GraphQLParser/AST/GraphQLOperationDefinition.cs index f91df112..71c953fb 100644 --- a/src/GraphQLParser/AST/GraphQLOperationDefinition.cs +++ b/src/GraphQLParser/AST/GraphQLOperationDefinition.cs @@ -2,22 +2,25 @@ namespace GraphQLParser.AST { + /// + /// AST node for . + /// public class GraphQLOperationDefinition : ASTNode, IHasDirectivesNode, INamedNode { - /// - public List? Directives { get; set; } - /// public override ASTNodeKind Kind => ASTNodeKind.OperationDefinition; + public OperationType Operation { get; set; } + /// public GraphQLName? Name { get; set; } - public OperationType Operation { get; set; } + public List? VariableDefinitions { get; set; } - public GraphQLSelectionSet? SelectionSet { get; set; } + /// + public List? Directives { get; set; } - public List? VariableDefinitions { get; set; } + public GraphQLSelectionSet? SelectionSet { get; set; } } internal sealed class GraphQLOperationDefinitionWithLocation : GraphQLOperationDefinition diff --git a/src/GraphQLParser/AST/GraphQLRootOperationTypeDefinition.cs b/src/GraphQLParser/AST/GraphQLRootOperationTypeDefinition.cs index 9f226a0d..3983a886 100644 --- a/src/GraphQLParser/AST/GraphQLRootOperationTypeDefinition.cs +++ b/src/GraphQLParser/AST/GraphQLRootOperationTypeDefinition.cs @@ -1,12 +1,21 @@ namespace GraphQLParser.AST { + /// + /// AST node for . + /// public class GraphQLRootOperationTypeDefinition : ASTNode { /// public override ASTNodeKind Kind => ASTNodeKind.RootOperationTypeDefinition; + /// + /// Kind of operation: query, mutation or subscription. + /// public OperationType Operation { get; set; } + /// + /// Type of this root operation. + /// public GraphQLNamedType? Type { get; set; } } diff --git a/src/GraphQLParser/AST/GraphQLScalarTypeDefinition.cs b/src/GraphQLParser/AST/GraphQLScalarTypeDefinition.cs index 96048ade..cfee0c59 100644 --- a/src/GraphQLParser/AST/GraphQLScalarTypeDefinition.cs +++ b/src/GraphQLParser/AST/GraphQLScalarTypeDefinition.cs @@ -2,13 +2,16 @@ namespace GraphQLParser.AST { + /// + /// AST node for . + /// public class GraphQLScalarTypeDefinition : GraphQLTypeDefinition, IHasDirectivesNode { /// - public List? Directives { get; set; } + public override ASTNodeKind Kind => ASTNodeKind.ScalarTypeDefinition; /// - public override ASTNodeKind Kind => ASTNodeKind.ScalarTypeDefinition; + public List? Directives { get; set; } } internal sealed class GraphQLScalarTypeDefinitionWithLocation : GraphQLScalarTypeDefinition diff --git a/src/GraphQLParser/AST/GraphQLSchemaDefinition.cs b/src/GraphQLParser/AST/GraphQLSchemaDefinition.cs index a2cc04c5..87c7587f 100644 --- a/src/GraphQLParser/AST/GraphQLSchemaDefinition.cs +++ b/src/GraphQLParser/AST/GraphQLSchemaDefinition.cs @@ -2,16 +2,19 @@ namespace GraphQLParser.AST { + /// + /// AST node for . + /// public class GraphQLSchemaDefinition : ASTNode, IHasDirectivesNode, IHasDescriptionNode { /// - public List? Directives { get; set; } + public override ASTNodeKind Kind => ASTNodeKind.SchemaDefinition; /// public GraphQLDescription? Description { get; set; } /// - public override ASTNodeKind Kind => ASTNodeKind.SchemaDefinition; + public List? Directives { get; set; } public List? OperationTypes { get; set; } } diff --git a/src/GraphQLParser/AST/GraphQLSelectionSet.cs b/src/GraphQLParser/AST/GraphQLSelectionSet.cs index 8b4ba21f..521d98ea 100644 --- a/src/GraphQLParser/AST/GraphQLSelectionSet.cs +++ b/src/GraphQLParser/AST/GraphQLSelectionSet.cs @@ -2,6 +2,9 @@ namespace GraphQLParser.AST { + /// + /// AST node for . + /// public class GraphQLSelectionSet : ASTNode { /// diff --git a/src/GraphQLParser/AST/GraphQLType.cs b/src/GraphQLParser/AST/GraphQLType.cs index 63daa723..d64ec2fa 100644 --- a/src/GraphQLParser/AST/GraphQLType.cs +++ b/src/GraphQLParser/AST/GraphQLType.cs @@ -1,5 +1,14 @@ namespace GraphQLParser.AST { + /// + /// Base AST node for three type nodes: + ///
+ /// Named + ///
+ /// NonNull + ///
+ /// List + ///
public abstract class GraphQLType : ASTNode { } diff --git a/src/GraphQLParser/AST/GraphQLTypeCondition.cs b/src/GraphQLParser/AST/GraphQLTypeCondition.cs new file mode 100644 index 00000000..4715bba7 --- /dev/null +++ b/src/GraphQLParser/AST/GraphQLTypeCondition.cs @@ -0,0 +1,56 @@ +namespace GraphQLParser.AST +{ + /// + /// AST node for . + /// + public class GraphQLTypeCondition : ASTNode + { + /// + public override ASTNodeKind Kind => ASTNodeKind.TypeCondition; + + /// + /// Type to which this condition is applied. + /// + public GraphQLNamedType? Type { get; set; } + } + + internal sealed class GraphQLTypeConditionWithLocation : GraphQLTypeCondition + { + private GraphQLLocation _location; + + public override GraphQLLocation Location + { + get => _location; + set => _location = value; + } + } + + internal sealed class GraphQLTypeConditionWithComment : GraphQLTypeCondition + { + private GraphQLComment? _comment; + + public override GraphQLComment? Comment + { + get => _comment; + set => _comment = value; + } + } + + internal sealed class GraphQLTypeConditionFull : GraphQLTypeCondition + { + private GraphQLLocation _location; + private GraphQLComment? _comment; + + public override GraphQLLocation Location + { + get => _location; + set => _location = value; + } + + public override GraphQLComment? Comment + { + get => _comment; + set => _comment = value; + } + } +} diff --git a/src/GraphQLParser/AST/GraphQLTypeDefinition.cs b/src/GraphQLParser/AST/GraphQLTypeDefinition.cs index 2f81b7a7..da937d2b 100644 --- a/src/GraphQLParser/AST/GraphQLTypeDefinition.cs +++ b/src/GraphQLParser/AST/GraphQLTypeDefinition.cs @@ -1,11 +1,14 @@ namespace GraphQLParser.AST { + /// + /// Base AST node for all type definition nodes. + /// public abstract class GraphQLTypeDefinition : ASTNode, INamedNode, IHasDescriptionNode { /// - public GraphQLName? Name { get; set; } + public GraphQLDescription? Description { get; set; } /// - public GraphQLDescription? Description { get; set; } + public GraphQLName? Name { get; set; } } } diff --git a/src/GraphQLParser/AST/GraphQLTypeExtensionDefinition.cs b/src/GraphQLParser/AST/GraphQLTypeExtensionDefinition.cs index e6f96760..718cb4c6 100644 --- a/src/GraphQLParser/AST/GraphQLTypeExtensionDefinition.cs +++ b/src/GraphQLParser/AST/GraphQLTypeExtensionDefinition.cs @@ -1,11 +1,12 @@ namespace GraphQLParser.AST { + //TODO: change public class GraphQLTypeExtensionDefinition : GraphQLTypeDefinition { - public GraphQLObjectTypeDefinition? Definition { get; set; } - /// public override ASTNodeKind Kind => ASTNodeKind.TypeExtensionDefinition; + + public GraphQLObjectTypeDefinition? Definition { get; set; } } internal sealed class GraphQLTypeExtensionDefinitionWithLocation : GraphQLTypeExtensionDefinition diff --git a/src/GraphQLParser/AST/GraphQLUnionTypeDefinition.cs b/src/GraphQLParser/AST/GraphQLUnionTypeDefinition.cs index cc88601c..fec4adb3 100644 --- a/src/GraphQLParser/AST/GraphQLUnionTypeDefinition.cs +++ b/src/GraphQLParser/AST/GraphQLUnionTypeDefinition.cs @@ -2,13 +2,16 @@ namespace GraphQLParser.AST { + /// + /// AST node for . + /// public class GraphQLUnionTypeDefinition : GraphQLTypeDefinition, IHasDirectivesNode { /// - public List? Directives { get; set; } + public override ASTNodeKind Kind => ASTNodeKind.UnionTypeDefinition; /// - public override ASTNodeKind Kind => ASTNodeKind.UnionTypeDefinition; + public List? Directives { get; set; } public List? Types { get; set; } } diff --git a/src/GraphQLParser/AST/GraphQLValue.cs b/src/GraphQLParser/AST/GraphQLValue.cs index 89c975c6..8b154e9d 100644 --- a/src/GraphQLParser/AST/GraphQLValue.cs +++ b/src/GraphQLParser/AST/GraphQLValue.cs @@ -1,5 +1,16 @@ namespace GraphQLParser.AST { + /// + /// Base AST node for four value nodes: + ///
+ /// Scalar + ///
+ /// List + ///
+ /// Object + ///
+ /// Variable + ///
public abstract class GraphQLValue : ASTNode { } diff --git a/src/GraphQLParser/AST/GraphQLVariable.cs b/src/GraphQLParser/AST/GraphQLVariable.cs index 5dba68f1..094e4894 100644 --- a/src/GraphQLParser/AST/GraphQLVariable.cs +++ b/src/GraphQLParser/AST/GraphQLVariable.cs @@ -1,5 +1,8 @@ namespace GraphQLParser.AST { + /// + /// AST node for . + /// public class GraphQLVariable : GraphQLValue, INamedNode { /// diff --git a/src/GraphQLParser/AST/GraphQLVariableDefinition.cs b/src/GraphQLParser/AST/GraphQLVariableDefinition.cs index 2da67930..fffb5806 100644 --- a/src/GraphQLParser/AST/GraphQLVariableDefinition.cs +++ b/src/GraphQLParser/AST/GraphQLVariableDefinition.cs @@ -1,15 +1,23 @@ +using System.Collections.Generic; + namespace GraphQLParser.AST { - public class GraphQLVariableDefinition : ASTNode + /// + /// AST node for . + /// + public class GraphQLVariableDefinition : ASTNode, IHasDirectivesNode { - public object? DefaultValue { get; set; } - /// public override ASTNodeKind Kind => ASTNodeKind.VariableDefinition; + public GraphQLVariable? Variable { get; set; } + public GraphQLType? Type { get; set; } - public GraphQLVariable? Variable { get; set; } + public GraphQLValue? DefaultValue { get; set; } + + /// + public List? Directives { get; set; } } internal sealed class GraphQLVariableDefinitionWithLocation : GraphQLVariableDefinition diff --git a/src/GraphQLParser/AST/IHasArgumentsNode.cs b/src/GraphQLParser/AST/IHasArgumentsNode.cs new file mode 100644 index 00000000..612e0e7e --- /dev/null +++ b/src/GraphQLParser/AST/IHasArgumentsNode.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; + +namespace GraphQLParser.AST +{ + /// + /// Represents an AST node that may have arguments. + /// + public interface IHasArgumentsNode + { + /// + /// Arguments of the node represented as a list of nested nodes. + /// + List? Arguments { get; set; } + } +} diff --git a/src/GraphQLParser/AST/IHasInterfacesNode.cs b/src/GraphQLParser/AST/IHasInterfacesNode.cs new file mode 100644 index 00000000..294d7595 --- /dev/null +++ b/src/GraphQLParser/AST/IHasInterfacesNode.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; + +namespace GraphQLParser.AST +{ + /// + /// Represents an AST node that may implement interfaces. + /// + public interface IHasInterfacesNode + { + /// + /// Implemented interfaces of the node represented as a list of nested nodes. + /// + List? Interfaces { get; set; } + } +} diff --git a/src/GraphQLParser/GraphQLAstVisitor.cs b/src/GraphQLParser/GraphQLAstVisitor.cs deleted file mode 100644 index 089709d3..00000000 --- a/src/GraphQLParser/GraphQLAstVisitor.cs +++ /dev/null @@ -1,264 +0,0 @@ -using System; -using System.Collections.Generic; -using GraphQLParser.AST; - -namespace GraphQLParser -{ - public class GraphQLAstVisitor // TODO: not used - { - protected IDictionary Fragments { get; private set; } - - public GraphQLAstVisitor() - { - Fragments = new Dictionary(); - } - - public virtual GraphQLName BeginVisitAlias(GraphQLName alias) => alias; - - public virtual GraphQLArgument BeginVisitArgument(GraphQLArgument argument) - { - if (argument.Name != null) - BeginVisitNode(argument.Name); - - if (argument.Value != null) - BeginVisitNode(argument.Value); - - return EndVisitArgument(argument); - } - - public virtual IEnumerable BeginVisitArguments(IEnumerable arguments) - { - foreach (var argument in arguments) - BeginVisitNode(argument); - - return arguments; - } - - public virtual GraphQLScalarValue BeginVisitBooleanValue(GraphQLScalarValue value) => value; - - public virtual GraphQLDirective BeginVisitDirective(GraphQLDirective directive) - { - if (directive.Name != null) - BeginVisitNode(directive.Name); - - if (directive.Arguments != null) - BeginVisitArguments(directive.Arguments); - - return directive; - } - - public virtual IEnumerable BeginVisitDirectives(IEnumerable directives) - { - foreach (var directive in directives) - BeginVisitNode(directive); - - return directives; - } - - public virtual GraphQLScalarValue BeginVisitEnumValue(GraphQLScalarValue value) => value; - - public virtual GraphQLField BeginVisitField(GraphQLField selection) - { - BeginVisitNode(selection.Name); - - if (selection.Alias != null) - BeginVisitAlias((GraphQLName)BeginVisitNode(selection.Alias)!); - - if (selection.Arguments != null) - BeginVisitArguments(selection.Arguments); - - if (selection.SelectionSet != null) - BeginVisitNode(selection.SelectionSet); - - if (selection.Directives != null) - BeginVisitDirectives(selection.Directives); - - return EndVisitField(selection); - } - - public virtual GraphQLScalarValue BeginVisitFloatValue(GraphQLScalarValue value) => value; - - public virtual GraphQLFragmentDefinition BeginVisitFragmentDefinition(GraphQLFragmentDefinition node) - { - BeginVisitNode(node.TypeCondition); - BeginVisitNode(node.Name); - - if (node.SelectionSet != null) - BeginVisitNode(node.SelectionSet); - - return node; - } - - public virtual GraphQLFragmentSpread BeginVisitFragmentSpread(GraphQLFragmentSpread fragmentSpread) - { - BeginVisitNode(fragmentSpread.Name); - return fragmentSpread; - } - - public virtual GraphQLInlineFragment BeginVisitInlineFragment(GraphQLInlineFragment inlineFragment) - { - if (inlineFragment.TypeCondition != null) - BeginVisitNode(inlineFragment.TypeCondition); - - if (inlineFragment.Directives != null) - BeginVisitDirectives(inlineFragment.Directives); - - if (inlineFragment.SelectionSet != null) - BeginVisitSelectionSet(inlineFragment.SelectionSet); - - return inlineFragment; - } - - public virtual GraphQLScalarValue BeginVisitIntValue(GraphQLScalarValue value) => value; - - public virtual GraphQLName BeginVisitName(GraphQLName name) => name; - - public virtual GraphQLNamedType BeginVisitNamedType(GraphQLNamedType typeCondition) => typeCondition; - - public virtual ASTNode? BeginVisitNode(ASTNode? node) => node?.Kind switch - { - ASTNodeKind.OperationDefinition => BeginVisitOperationDefinition((GraphQLOperationDefinition)node), - ASTNodeKind.SelectionSet => BeginVisitSelectionSet((GraphQLSelectionSet)node), - ASTNodeKind.Field => BeginVisitNonIntrospectionField((GraphQLField)node), - ASTNodeKind.Name => BeginVisitName((GraphQLName)node), - ASTNodeKind.Argument => BeginVisitArgument((GraphQLArgument)node), - ASTNodeKind.FragmentSpread => BeginVisitFragmentSpread((GraphQLFragmentSpread)node), - ASTNodeKind.FragmentDefinition => BeginVisitFragmentDefinition((GraphQLFragmentDefinition)node), - ASTNodeKind.InlineFragment => BeginVisitInlineFragment((GraphQLInlineFragment)node), - ASTNodeKind.NamedType => BeginVisitNamedType((GraphQLNamedType)node), - ASTNodeKind.Directive => BeginVisitDirective((GraphQLDirective)node), - ASTNodeKind.Variable => BeginVisitVariable((GraphQLVariable)node), - ASTNodeKind.IntValue => BeginVisitIntValue((GraphQLScalarValue)node), - ASTNodeKind.FloatValue => BeginVisitFloatValue((GraphQLScalarValue)node), - ASTNodeKind.StringValue => BeginVisitStringValue((GraphQLScalarValue)node), - ASTNodeKind.BooleanValue => BeginVisitBooleanValue((GraphQLScalarValue)node), - ASTNodeKind.EnumValue => BeginVisitEnumValue((GraphQLScalarValue)node), - ASTNodeKind.ListValue => BeginVisitListValue((GraphQLListValue)node), - ASTNodeKind.ObjectValue => BeginVisitObjectValue((GraphQLObjectValue)node), - ASTNodeKind.ObjectField => BeginVisitObjectField((GraphQLObjectField)node), - ASTNodeKind.VariableDefinition => BeginVisitVariableDefinition((GraphQLVariableDefinition)node), - _ => null - }; - - public virtual GraphQLOperationDefinition BeginVisitOperationDefinition(GraphQLOperationDefinition definition) - { - if (definition.Name != null) - BeginVisitNode(definition.Name); - - if (definition.VariableDefinitions != null) - BeginVisitVariableDefinitions(definition.VariableDefinitions); - - BeginVisitNode(definition.SelectionSet); - - return EndVisitOperationDefinition(definition); - } - - public virtual GraphQLOperationDefinition EndVisitOperationDefinition(GraphQLOperationDefinition definition) => definition; - - public virtual GraphQLSelectionSet BeginVisitSelectionSet(GraphQLSelectionSet selectionSet) - { - if (selectionSet.Selections != null) - { - foreach (var selection in selectionSet.Selections) - BeginVisitNode(selection); - } - - return selectionSet; - } - - public virtual GraphQLScalarValue BeginVisitStringValue(GraphQLScalarValue value) => value; - - public virtual GraphQLVariable BeginVisitVariable(GraphQLVariable variable) - { - if (variable.Name != null) - BeginVisitNode(variable.Name); - - return EndVisitVariable(variable); - } - - public virtual GraphQLVariableDefinition BeginVisitVariableDefinition(GraphQLVariableDefinition node) - { - BeginVisitNode(node.Type); - - return node; - } - - public virtual IEnumerable BeginVisitVariableDefinitions(IEnumerable variableDefinitions) - { - foreach (var definition in variableDefinitions) - BeginVisitNode(definition); - - return variableDefinitions; - } - - public virtual GraphQLArgument EndVisitArgument(GraphQLArgument argument) => argument; - - public virtual GraphQLField EndVisitField(GraphQLField selection) => selection; - - public virtual GraphQLVariable EndVisitVariable(GraphQLVariable variable) => variable; - - public virtual void Visit(GraphQLDocument ast) - { - if (ast.Definitions != null) - { - foreach (var definition in ast.Definitions) - { - if (definition.Kind == ASTNodeKind.FragmentDefinition) - { - var fragment = (GraphQLFragmentDefinition)definition; - string? name = fragment.Name?.Value.ToString(); // TODO: heap allocation - if (name == null) - throw new InvalidOperationException("Fragment name cannot be null"); - - Fragments.Add(name, fragment); - } - } - - foreach (var definition in ast.Definitions) - { - BeginVisitNode(definition); - } - } - } - - public virtual GraphQLObjectField BeginVisitObjectField(GraphQLObjectField node) - { - BeginVisitNode(node.Name); - - BeginVisitNode(node.Value); - - return node; - } - - public virtual GraphQLObjectValue BeginVisitObjectValue(GraphQLObjectValue node) - { - if (node.Fields != null) - { - foreach (var field in node.Fields) - BeginVisitNode(field); - } - - return EndVisitObjectValue(node); - } - - public virtual GraphQLObjectValue EndVisitObjectValue(GraphQLObjectValue node) => node; - - public virtual GraphQLListValue EndVisitListValue(GraphQLListValue node) => node; - - private ASTNode BeginVisitListValue(GraphQLListValue node) - { - if (node.Values != null) - { - foreach (var value in node.Values) - BeginVisitNode(value); - } - - return EndVisitListValue(node); - } - - private ASTNode BeginVisitNonIntrospectionField(GraphQLField selection) - { - return BeginVisitField(selection); - } - } -} diff --git a/src/GraphQLParser/GraphQLParser.csproj b/src/GraphQLParser/GraphQLParser.csproj index 21ce3e42..334aa81a 100644 --- a/src/GraphQLParser/GraphQLParser.csproj +++ b/src/GraphQLParser/GraphQLParser.csproj @@ -10,6 +10,7 @@ + diff --git a/src/GraphQLParser/NodeHelper.cs b/src/GraphQLParser/NodeHelper.cs index aa69ccf2..d3f5d809 100644 --- a/src/GraphQLParser/NodeHelper.cs +++ b/src/GraphQLParser/NodeHelper.cs @@ -192,14 +192,14 @@ public static GraphQLName CreateGraphQLName(IgnoreOptions options) } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static GraphQLListValue CreateGraphQLListValue(IgnoreOptions options, ASTNodeKind kind) + public static GraphQLListValue CreateGraphQLListValue(IgnoreOptions options) { return options switch { - IgnoreOptions.All => new GraphQLListValue(kind), - IgnoreOptions.Comments => new GraphQLListValueWithLocation(kind), - IgnoreOptions.Locations => new GraphQLListValueWithComment(kind), - _ => new GraphQLListValueFull(kind), + IgnoreOptions.All => new GraphQLListValue(), + IgnoreOptions.Comments => new GraphQLListValueWithLocation(), + IgnoreOptions.Locations => new GraphQLListValueWithComment(), + _ => new GraphQLListValueFull(), }; } @@ -419,6 +419,30 @@ public static GraphQLInputValueDefinition CreateGraphQLInputValueDefinition(Igno }; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static GraphQLTypeCondition CreateGraphQLTypeCondition(IgnoreOptions options) + { + return options switch + { + IgnoreOptions.All => new GraphQLTypeCondition(), + IgnoreOptions.Comments => new GraphQLTypeConditionWithLocation(), + IgnoreOptions.Locations => new GraphQLTypeConditionWithComment(), + _ => new GraphQLTypeConditionFull(), + }; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static GraphQLAlias CreateGraphQLAlias(IgnoreOptions options) + { + return options switch + { + IgnoreOptions.All => new GraphQLAlias(), + IgnoreOptions.Comments => new GraphQLAliasWithLocation(), + IgnoreOptions.Locations => new GraphQLAliasWithComment(), + _ => new GraphQLAliasFull(), + }; + } + #endregion } } diff --git a/src/GraphQLParser/ParserContext.Parse.cs b/src/GraphQLParser/ParserContext.Parse.cs index 0b965a51..e51dfef6 100644 --- a/src/GraphQLParser/ParserContext.Parse.cs +++ b/src/GraphQLParser/ParserContext.Parse.cs @@ -29,23 +29,18 @@ public GraphQLDocument ParseDocument() } // http://spec.graphql.org/October2021/#TypeCondition - private GraphQLNamedType? ParseTypeCondition(bool optional) + private GraphQLTypeCondition? ParseTypeCondition(bool optional) { - if (optional) - { - GraphQLNamedType? typeCondition = null; - if (_currentToken.Value == "on") - { - Advance(); - typeCondition = ParseNamedType(); - } - return typeCondition; - } - else - { - ExpectKeyword("on"); - return ParseNamedType(); - } + if (optional && _currentToken.Value != "on") + return null; + + int start = _currentToken.Start; + var condition = NodeHelper.CreateGraphQLTypeCondition(_ignoreOptions); + condition.Comment = GetComment(); + ExpectKeyword("on"); + condition.Type = ParseNamedType(); + condition.Location = GetLocation(start); + return condition; } // http://spec.graphql.org/October2021/#Argument @@ -405,32 +400,57 @@ private GraphQLFieldDefinition ParseFieldDefinition() } // http://spec.graphql.org/October2021/#Field + // http://spec.graphql.org/October2021/#Alias private GraphQLField ParseField() { + // start of alias (if exists) equals start of field int start = _currentToken.Start; - var comment = GetComment(); + + var nameOrAliasComment = GetComment(); var nameOrAlias = ParseName(); + GraphQLName name; GraphQLName? alias; + GraphQLComment? nameComment; + GraphQLComment? aliasComment; + + GraphQLLocation aliasLocation = default; + if (Skip(TokenKind.COLON)) { + aliasLocation = GetLocation(start); + + nameComment = GetComment(); + aliasComment = nameOrAliasComment; + name = ParseName(); alias = nameOrAlias; } else { + aliasComment = null; + nameComment = nameOrAliasComment; + alias = null; name = nameOrAlias; } var field = NodeHelper.CreateGraphQLField(_ignoreOptions); - field.Alias = alias; + if (alias != null) + { + var aliasNode = NodeHelper.CreateGraphQLAlias(_ignoreOptions); + aliasNode.Comment = aliasComment; + aliasNode.Name = alias; + aliasNode.Location = aliasLocation; + + field.Alias = aliasNode; + } + field.Comment = nameComment; field.Name = name; field.Arguments = ParseArguments(); field.Directives = ParseDirectives(); field.SelectionSet = Peek(TokenKind.BRACE_L) ? ParseSelectionSet() : null; - field.Comment = comment; field.Location = GetLocation(start); return field; } @@ -633,7 +653,7 @@ private GraphQLValue ParseListValue(bool isConstant) ParseCallback constant = (ref ParserContext context) => context.ParseValueLiteral(true); ParseCallback value = (ref ParserContext context) => context.ParseValueLiteral(false); - var val = NodeHelper.CreateGraphQLListValue(_ignoreOptions, ASTNodeKind.ListValue); + var val = NodeHelper.CreateGraphQLListValue(_ignoreOptions); val.Values = ZeroOrMore(TokenKind.BRACKET_L, isConstant ? constant : value, TokenKind.BRACKET_R); val.AstValue = _source.Slice(start, _currentToken.End - start - 1); val.Comment = comment; @@ -1149,6 +1169,7 @@ private GraphQLVariableDefinition ParseVariableDefinition() def.Type = ParseType(); def.DefaultValue = Skip(TokenKind.EQUALS) ? ParseValueLiteral(true) : null; + def.Directives = ParseDirectives(); def.Comment = comment; def.Location = GetLocation(start); return def; diff --git a/src/GraphQLParser/Visitors/DefaultNodeVisitor.cs b/src/GraphQLParser/Visitors/DefaultNodeVisitor.cs new file mode 100644 index 00000000..da51661c --- /dev/null +++ b/src/GraphQLParser/Visitors/DefaultNodeVisitor.cs @@ -0,0 +1,413 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using GraphQLParser.AST; + +namespace GraphQLParser.Visitors +{ + /// + /// Default implementation of . + /// Traverses all AST nodes of the provided one. + /// + /// Type of the context object passed into all VisitXXX methods. + public class DefaultNodeVisitor : INodeVisitor + where TContext : INodeVisitorContext + { + /// + public virtual async ValueTask VisitDocument(GraphQLDocument document, TContext context) + { + await Visit(document.Definitions, context).ConfigureAwait(false); + } + + /// + public virtual async ValueTask VisitArgument(GraphQLArgument argument, TContext context) + { + await Visit(argument.Comment, context).ConfigureAwait(false); + await Visit(argument.Name, context).ConfigureAwait(false); + await Visit(argument.Value, context).ConfigureAwait(false); + } + + /// + public virtual ValueTask VisitComment(GraphQLComment comment, TContext context) + { + return new ValueTask(Task.CompletedTask); + } + + /// + public virtual ValueTask VisitDescription(GraphQLDescription description, TContext context) + { + return new ValueTask(Task.CompletedTask); + } + + /// + public virtual async ValueTask VisitOperationDefinition(GraphQLOperationDefinition operationDefinition, TContext context) + { + await Visit(operationDefinition.Comment, context).ConfigureAwait(false); + await Visit(operationDefinition.Name, context).ConfigureAwait(false); + await Visit(operationDefinition.VariableDefinitions, context).ConfigureAwait(false); + await Visit(operationDefinition.Directives, context).ConfigureAwait(false); + await Visit(operationDefinition.SelectionSet, context).ConfigureAwait(false); + } + + /// + public virtual async ValueTask VisitName(GraphQLName name, TContext context) + { + await Visit(name.Comment, context).ConfigureAwait(false); + } + + /// + public virtual async ValueTask VisitVariableDefinition(GraphQLVariableDefinition variableDefinition, TContext context) + { + await Visit(variableDefinition.Comment, context).ConfigureAwait(false); + await Visit(variableDefinition.Variable, context).ConfigureAwait(false); + await Visit(variableDefinition.Type, context).ConfigureAwait(false); + await Visit(variableDefinition.DefaultValue, context).ConfigureAwait(false); + await Visit(variableDefinition.Directives, context).ConfigureAwait(false); + } + + /// + public virtual async ValueTask VisitVariable(GraphQLVariable variable, TContext context) + { + await Visit(variable.Comment, context).ConfigureAwait(false); + await Visit(variable.Name, context).ConfigureAwait(false); + } + + /// + public virtual async ValueTask VisitSelectionSet(GraphQLSelectionSet selectionSet, TContext context) + { + await Visit(selectionSet.Comment, context).ConfigureAwait(false); + await Visit(selectionSet.Selections, context).ConfigureAwait(false); + } + + /// + public virtual async ValueTask VisitAlias(GraphQLAlias alias, TContext context) + { + await Visit(alias.Comment, context).ConfigureAwait(false); + await Visit(alias.Name, context).ConfigureAwait(false); + } + + /// + public virtual async ValueTask VisitField(GraphQLField field, TContext context) + { + await Visit(field.Comment, context).ConfigureAwait(false); + await Visit(field.Alias, context).ConfigureAwait(false); + await Visit(field.Name, context).ConfigureAwait(false); + await Visit(field.Arguments, context).ConfigureAwait(false); + await Visit(field.Directives, context).ConfigureAwait(false); + await Visit(field.SelectionSet, context).ConfigureAwait(false); + } + + /// + public virtual async ValueTask VisitFragmentSpread(GraphQLFragmentSpread fragmentSpread, TContext context) + { + await Visit(fragmentSpread.Comment, context).ConfigureAwait(false); + await Visit(fragmentSpread.Name, context).ConfigureAwait(false); + await Visit(fragmentSpread.Directives, context).ConfigureAwait(false); + } + + /// + public virtual async ValueTask VisitInlineFragment(GraphQLInlineFragment inlineFragment, TContext context) + { + await Visit(inlineFragment.Comment, context).ConfigureAwait(false); + await Visit(inlineFragment.TypeCondition, context).ConfigureAwait(false); + await Visit(inlineFragment.Directives, context).ConfigureAwait(false); + await Visit(inlineFragment.SelectionSet, context).ConfigureAwait(false); + } + + /// + public virtual async ValueTask VisitTypeCondition(GraphQLTypeCondition typeCondition, TContext context) + { + await Visit(typeCondition.Comment, context).ConfigureAwait(false); + await Visit(typeCondition.Type, context).ConfigureAwait(false); + } + + /// + public virtual async ValueTask VisitFragmentDefinition(GraphQLFragmentDefinition fragmentDefinition, TContext context) + { + await Visit(fragmentDefinition.Comment, context).ConfigureAwait(false); + await Visit(fragmentDefinition.Name, context).ConfigureAwait(false); + await Visit(fragmentDefinition.TypeCondition, context).ConfigureAwait(false); + await Visit(fragmentDefinition.Directives, context).ConfigureAwait(false); + await Visit(fragmentDefinition.SelectionSet, context).ConfigureAwait(false); + } + + /// + public virtual async ValueTask VisitIntValue(GraphQLScalarValue intValue, TContext context) + { + await Visit(intValue.Comment, context).ConfigureAwait(false); + } + + /// + public virtual async ValueTask VisitFloatValue(GraphQLScalarValue floatValue, TContext context) + { + await Visit(floatValue.Comment, context).ConfigureAwait(false); + } + + /// + public virtual async ValueTask VisitStringValue(GraphQLScalarValue stringValue, TContext context) + { + await Visit(stringValue.Comment, context).ConfigureAwait(false); + } + + /// + public virtual async ValueTask VisitBooleanValue(GraphQLScalarValue booleanValue, TContext context) + { + await Visit(booleanValue.Comment, context).ConfigureAwait(false); + } + + /// + public virtual async ValueTask VisitEnumValue(GraphQLScalarValue enumValue, TContext context) + { + await Visit(enumValue.Comment, context).ConfigureAwait(false); + } + + /// + public virtual async ValueTask VisitListValue(GraphQLListValue listValue, TContext context) + { + await Visit(listValue.Comment, context).ConfigureAwait(false); + await Visit(listValue.Values, context).ConfigureAwait(false); + } + + /// + public virtual async ValueTask VisitObjectValue(GraphQLObjectValue objectValue, TContext context) + { + await Visit(objectValue.Comment, context).ConfigureAwait(false); + await Visit(objectValue.Fields, context).ConfigureAwait(false); + } + + /// + public virtual async ValueTask VisitObjectField(GraphQLObjectField objectField, TContext context) + { + await Visit(objectField.Comment, context).ConfigureAwait(false); + await Visit(objectField.Name, context).ConfigureAwait(false); + await Visit(objectField.Value, context).ConfigureAwait(false); + } + + /// + public virtual async ValueTask VisitDirective(GraphQLDirective directive, TContext context) + { + await Visit(directive.Comment, context).ConfigureAwait(false); + await Visit(directive.Name, context).ConfigureAwait(false); + await Visit(directive.Arguments, context).ConfigureAwait(false); + } + + /// + public virtual async ValueTask VisitNamedType(GraphQLNamedType namedType, TContext context) + { + await Visit(namedType.Comment, context).ConfigureAwait(false); + await Visit(namedType.Name, context).ConfigureAwait(false); + } + + /// + public virtual async ValueTask VisitListType(GraphQLListType listType, TContext context) + { + await Visit(listType.Comment, context).ConfigureAwait(false); + await Visit(listType.Type, context).ConfigureAwait(false); + } + + /// + public virtual async ValueTask VisitNonNullType(GraphQLNonNullType nonNullType, TContext context) + { + await Visit(nonNullType.Comment, context).ConfigureAwait(false); + await Visit(nonNullType.Type, context).ConfigureAwait(false); + } + + /// + public virtual async ValueTask VisitNullValue(GraphQLScalarValue nullValue, TContext context) + { + await Visit(nullValue.Comment, context).ConfigureAwait(false); + } + + /// + public virtual async ValueTask VisitSchemaDefinition(GraphQLSchemaDefinition schemaDefinition, TContext context) + { + await Visit(schemaDefinition.Comment, context).ConfigureAwait(false); + await Visit(schemaDefinition.Description, context).ConfigureAwait(false); + await Visit(schemaDefinition.Directives, context).ConfigureAwait(false); + await Visit(schemaDefinition.OperationTypes, context).ConfigureAwait(false); + } + + /// + public virtual async ValueTask VisitRootOperationTypeDefinition(GraphQLRootOperationTypeDefinition rootOperationTypeDefinition, TContext context) + { + await Visit(rootOperationTypeDefinition.Comment, context).ConfigureAwait(false); + await Visit(rootOperationTypeDefinition.Type, context).ConfigureAwait(false); + } + + /// + public virtual async ValueTask VisitScalarTypeDefinition(GraphQLScalarTypeDefinition scalarTypeDefinition, TContext context) + { + await Visit(scalarTypeDefinition.Comment, context).ConfigureAwait(false); + await Visit(scalarTypeDefinition.Description, context).ConfigureAwait(false); + await Visit(scalarTypeDefinition.Name, context).ConfigureAwait(false); + await Visit(scalarTypeDefinition.Directives, context).ConfigureAwait(false); + } + + /// + public virtual async ValueTask VisitObjectTypeDefinition(GraphQLObjectTypeDefinition objectTypeDefinition, TContext context) + { + await Visit(objectTypeDefinition.Comment, context).ConfigureAwait(false); + await Visit(objectTypeDefinition.Description, context).ConfigureAwait(false); + await Visit(objectTypeDefinition.Name, context).ConfigureAwait(false); + await Visit(objectTypeDefinition.Interfaces, context).ConfigureAwait(false); + await Visit(objectTypeDefinition.Directives, context).ConfigureAwait(false); + await Visit(objectTypeDefinition.Fields, context).ConfigureAwait(false); + } + + /// + public virtual async ValueTask VisitFieldDefinition(GraphQLFieldDefinition fieldDefinition, TContext context) + { + await Visit(fieldDefinition.Comment, context).ConfigureAwait(false); + await Visit(fieldDefinition.Description, context).ConfigureAwait(false); + await Visit(fieldDefinition.Name, context).ConfigureAwait(false); + await Visit(fieldDefinition.Arguments, context).ConfigureAwait(false); + await Visit(fieldDefinition.Type, context).ConfigureAwait(false); + await Visit(fieldDefinition.Directives, context).ConfigureAwait(false); + } + + /// + public virtual async ValueTask VisitInputValueDefinition(GraphQLInputValueDefinition inputValueDefinition, TContext context) + { + await Visit(inputValueDefinition.Comment, context).ConfigureAwait(false); + await Visit(inputValueDefinition.Description, context).ConfigureAwait(false); + await Visit(inputValueDefinition.Name, context).ConfigureAwait(false); + await Visit(inputValueDefinition.Type, context).ConfigureAwait(false); + await Visit(inputValueDefinition.DefaultValue, context).ConfigureAwait(false); + await Visit(inputValueDefinition.Directives, context).ConfigureAwait(false); + } + + /// + public virtual async ValueTask VisitInterfaceTypeDefinition(GraphQLInterfaceTypeDefinition interfaceTypeDefinition, TContext context) + { + await Visit(interfaceTypeDefinition.Comment, context).ConfigureAwait(false); + await Visit(interfaceTypeDefinition.Description, context).ConfigureAwait(false); + await Visit(interfaceTypeDefinition.Name, context).ConfigureAwait(false); + await Visit(interfaceTypeDefinition.Interfaces, context).ConfigureAwait(false); + await Visit(interfaceTypeDefinition.Directives, context).ConfigureAwait(false); + await Visit(interfaceTypeDefinition.Fields, context).ConfigureAwait(false); + } + + /// + public virtual async ValueTask VisitUnionTypeDefinition(GraphQLUnionTypeDefinition unionTypeDefinition, TContext context) + { + await Visit(unionTypeDefinition.Comment, context).ConfigureAwait(false); + await Visit(unionTypeDefinition.Description, context).ConfigureAwait(false); + await Visit(unionTypeDefinition.Name, context).ConfigureAwait(false); + await Visit(unionTypeDefinition.Directives, context).ConfigureAwait(false); + await Visit(unionTypeDefinition.Types, context).ConfigureAwait(false); + } + + /// + public virtual async ValueTask VisitEnumTypeDefinition(GraphQLEnumTypeDefinition enumTypeDefinition, TContext context) + { + await Visit(enumTypeDefinition.Comment, context).ConfigureAwait(false); + await Visit(enumTypeDefinition.Description, context).ConfigureAwait(false); + await Visit(enumTypeDefinition.Name, context).ConfigureAwait(false); + await Visit(enumTypeDefinition.Directives, context).ConfigureAwait(false); + await Visit(enumTypeDefinition.Values, context).ConfigureAwait(false); + } + + /// + public virtual async ValueTask VisitEnumValueDefinition(GraphQLEnumValueDefinition enumValueDefinition, TContext context) + { + await Visit(enumValueDefinition.Comment, context).ConfigureAwait(false); + await Visit(enumValueDefinition.Description, context).ConfigureAwait(false); + await Visit(enumValueDefinition.Name, context).ConfigureAwait(false); + await Visit(enumValueDefinition.Directives, context).ConfigureAwait(false); + } + + /// + public virtual async ValueTask VisitInputObjectTypeDefinition(GraphQLInputObjectTypeDefinition inputObjectTypeDefinition, TContext context) + { + await Visit(inputObjectTypeDefinition.Comment, context).ConfigureAwait(false); + await Visit(inputObjectTypeDefinition.Description, context).ConfigureAwait(false); + await Visit(inputObjectTypeDefinition.Name, context).ConfigureAwait(false); + await Visit(inputObjectTypeDefinition.Directives, context).ConfigureAwait(false); + await Visit(inputObjectTypeDefinition.Fields, context).ConfigureAwait(false); + } + + /// + public virtual async ValueTask VisitDirectiveDefinition(GraphQLDirectiveDefinition directiveDefinition, TContext context) + { + await Visit(directiveDefinition.Comment, context).ConfigureAwait(false); + await Visit(directiveDefinition.Description, context).ConfigureAwait(false); + await Visit(directiveDefinition.Name, context).ConfigureAwait(false); + await Visit(directiveDefinition.Arguments, context).ConfigureAwait(false); + } + + /// + /// Dispatches node to the appropriate VisitXXX method. + /// + /// AST node to dispatch. + /// Context passed into all INodeVisitor.VisitXXX methods. + public virtual ValueTask Visit(ASTNode? node, TContext context) + { + return node == null + ? new ValueTask(Task.CompletedTask) + : node switch + { + GraphQLArgument argument => VisitArgument(argument, context), + GraphQLComment comment => VisitComment(comment, context), + GraphQLDescription description => VisitDescription(description, context), + GraphQLDirective directive => VisitDirective(directive, context), + GraphQLDirectiveDefinition directiveDefinition => VisitDirectiveDefinition(directiveDefinition, context), + GraphQLDocument document => VisitDocument(document, context), + GraphQLEnumTypeDefinition enumTypeDefinition => VisitEnumTypeDefinition(enumTypeDefinition, context), + GraphQLEnumValueDefinition enumValueDefinition => VisitEnumValueDefinition(enumValueDefinition, context), + GraphQLAlias alias => VisitAlias(alias, context), + GraphQLField field => VisitField(field, context), + GraphQLFieldDefinition fieldDefinition => VisitFieldDefinition(fieldDefinition, context), + GraphQLFragmentDefinition fragmentDefinition => VisitFragmentDefinition(fragmentDefinition, context), // inherits from GraphQLInlineFragment so should be above + GraphQLFragmentSpread fragmentSpread => VisitFragmentSpread(fragmentSpread, context), + GraphQLInlineFragment inlineFragment => VisitInlineFragment(inlineFragment, context), + GraphQLTypeCondition typeCondition => VisitTypeCondition(typeCondition, context), + GraphQLInputObjectTypeDefinition inputObjectTypeDefinition => VisitInputObjectTypeDefinition(inputObjectTypeDefinition, context), + GraphQLInputValueDefinition inputValueDefinition => VisitInputValueDefinition(inputValueDefinition, context), + GraphQLInterfaceTypeDefinition interfaceTypeDefinition => VisitInterfaceTypeDefinition(interfaceTypeDefinition, context), + GraphQLListType listType => VisitListType(listType, context), + GraphQLListValue listValue => VisitListValue(listValue, context), + GraphQLName name => VisitName(name, context), + GraphQLNamedType namedType => VisitNamedType(namedType, context), + GraphQLNonNullType nonNullType => VisitNonNullType(nonNullType, context), + GraphQLObjectField objectField => VisitObjectField(objectField, context), + GraphQLObjectTypeDefinition objectTypeDefinition => VisitObjectTypeDefinition(objectTypeDefinition, context), + GraphQLObjectValue objectValue => VisitObjectValue(objectValue, context), + GraphQLOperationDefinition operationDefinition => VisitOperationDefinition(operationDefinition, context), + GraphQLRootOperationTypeDefinition rootOperationTypeDefinition => VisitRootOperationTypeDefinition(rootOperationTypeDefinition, context), + GraphQLScalarTypeDefinition scalarTypeDefinition => VisitScalarTypeDefinition(scalarTypeDefinition, context), + GraphQLScalarValue scalarValue => scalarValue.Kind switch + { + ASTNodeKind.BooleanValue => VisitBooleanValue(scalarValue, context), + ASTNodeKind.EnumValue => VisitEnumValue(scalarValue, context), + ASTNodeKind.FloatValue => VisitFloatValue(scalarValue, context), + ASTNodeKind.IntValue => VisitIntValue(scalarValue, context), + ASTNodeKind.NullValue => VisitNullValue(scalarValue, context), + ASTNodeKind.StringValue => VisitStringValue(scalarValue, context), + _ => throw new NotSupportedException($"Unknown GraphQLScalarValue of kind '{scalarValue.Kind}'."), + }, + GraphQLSchemaDefinition schemaDefinition => VisitSchemaDefinition(schemaDefinition, context), + GraphQLSelectionSet selectionSet => VisitSelectionSet(selectionSet, context), + //GraphQLTypeExtensionDefinition n => VisitTypeDE + GraphQLUnionTypeDefinition unionTypeDefinition => VisitUnionTypeDefinition(unionTypeDefinition, context), + GraphQLVariable variable => VisitVariable(variable, context), + GraphQLVariableDefinition variableDefinition => VisitVariableDefinition(variableDefinition, context), + _ => throw new NotSupportedException($"Unknown node '{node.GetType().Name}'."), + }; + } + + /// + /// Visits all nodes from the provided list. As a rule, these are nested + /// sibling nodes of some parent node, for example, argument nodes for + /// parent field node or value nodes for parent list node. + /// + protected async ValueTask Visit(List? nodes, TContext context) + where T : ASTNode + { + if (nodes != null) + { + foreach (var node in nodes) + await Visit(node, context).ConfigureAwait(false); + } + } + } +} diff --git a/src/GraphQLParser/Visitors/INodeVisitor.cs b/src/GraphQLParser/Visitors/INodeVisitor.cs new file mode 100644 index 00000000..7c1ba493 --- /dev/null +++ b/src/GraphQLParser/Visitors/INodeVisitor.cs @@ -0,0 +1,218 @@ +using System.Threading.Tasks; +using GraphQLParser.AST; + +namespace GraphQLParser.Visitors +{ + /// + /// Visitor which methods are called when traversing AST. + /// Type of the context object passed into all VisitXXX methods. + /// + public interface INodeVisitor + where TContext : INodeVisitorContext + { + /// + /// Visits node. + /// + ValueTask VisitName(GraphQLName name, TContext context); + + /// + /// Visits node. + /// + ValueTask VisitDocument(GraphQLDocument document, TContext context); + + /// + /// Visits node. + /// + ValueTask VisitOperationDefinition(GraphQLOperationDefinition operationDefinition, TContext context); + + /// + /// Visits node. + /// + ValueTask VisitVariableDefinition(GraphQLVariableDefinition variableDefinition, TContext context); + + /// + /// Visits node. + /// + ValueTask VisitVariable(GraphQLVariable variable, TContext context); + + /// + /// Visits node. + /// + ValueTask VisitSelectionSet(GraphQLSelectionSet selectionSet, TContext context); + + /// + /// Visits node. + /// + ValueTask VisitField(GraphQLField field, TContext context); + + /// + /// Visits node. + /// + ValueTask VisitArgument(GraphQLArgument argument, TContext context); + + /// + /// Visits node. + /// + ValueTask VisitFragmentSpread(GraphQLFragmentSpread fragmentSpread, TContext context); + + /// + /// Visits node. + /// + ValueTask VisitInlineFragment(GraphQLInlineFragment inlineFragment, TContext context); + + /// + /// Visits node. + /// + ValueTask VisitFragmentDefinition(GraphQLFragmentDefinition fragmentDefinition, TContext context); + + /// + /// Visits node. + /// + ValueTask VisitIntValue(GraphQLScalarValue intValue, TContext context); + + /// + /// Visits node. + /// + ValueTask VisitFloatValue(GraphQLScalarValue floatValue, TContext context); + + /// + /// Visits node. + /// + ValueTask VisitStringValue(GraphQLScalarValue stringValue, TContext context); + + /// + /// Visits node. + /// + ValueTask VisitBooleanValue(GraphQLScalarValue booleanValue, TContext context); + + /// + /// Visits node. + /// + ValueTask VisitEnumValue(GraphQLScalarValue enumValue, TContext context); + + /// + /// Visits node. + /// + ValueTask VisitListValue(GraphQLListValue listValue, TContext context); + + /// + /// Visits node. + /// + ValueTask VisitObjectValue(GraphQLObjectValue objectValue, TContext context); + + /// + /// Visits node. + /// + ValueTask VisitObjectField(GraphQLObjectField objectField, TContext context); + + /// + /// Visits node. + /// + ValueTask VisitDirective(GraphQLDirective directive, TContext context); + + /// + /// Visits node. + /// + ValueTask VisitNamedType(GraphQLNamedType namedType, TContext context); + + /// + /// Visits node. + /// + ValueTask VisitListType(GraphQLListType listType, TContext context); + + /// + /// Visits node. + /// + ValueTask VisitNonNullType(GraphQLNonNullType nonNullType, TContext context); + + /// + /// Visits node. + /// + ValueTask VisitNullValue(GraphQLScalarValue nullValue, TContext context); + + /// + /// Visits node. + /// + ValueTask VisitSchemaDefinition(GraphQLSchemaDefinition schemaDefinition, TContext context); + + /// + /// Visits node. + /// + ValueTask VisitRootOperationTypeDefinition(GraphQLRootOperationTypeDefinition rootOperationTypeDefinition, TContext context); + + /// + /// Visits node. + /// + ValueTask VisitScalarTypeDefinition(GraphQLScalarTypeDefinition scalarTypeDefinition, TContext context); + + /// + /// Visits node. + /// + ValueTask VisitObjectTypeDefinition(GraphQLObjectTypeDefinition objectTypeDefinition, TContext context); + + /// + /// Visits node. + /// + ValueTask VisitFieldDefinition(GraphQLFieldDefinition fieldDefinition, TContext context); + + /// + /// Visits node. + /// + ValueTask VisitInputValueDefinition(GraphQLInputValueDefinition inputValueDefinition, TContext context); + + /// + /// Visits node. + /// + ValueTask VisitInterfaceTypeDefinition(GraphQLInterfaceTypeDefinition interfaceTypeDefinition, TContext context); + + /// + /// Visits node. + /// + ValueTask VisitUnionTypeDefinition(GraphQLUnionTypeDefinition unionTypeDefinition, TContext context); + + /// + /// Visits node. + /// + ValueTask VisitEnumTypeDefinition(GraphQLEnumTypeDefinition enumTypeDefinition, TContext context); + + /// + /// Visits node. + /// + ValueTask VisitEnumValueDefinition(GraphQLEnumValueDefinition enumValueDefinition, TContext context); + + /// + /// Visits node. + /// + ValueTask VisitInputObjectTypeDefinition(GraphQLInputObjectTypeDefinition inputObjectTypeDefinition, TContext context); + + // + // Visits node. + // + //ValueTask VisitTypeExtensionDefinition(); //TODO + + /// + /// Visits node. + /// + ValueTask VisitDirectiveDefinition(GraphQLDirectiveDefinition directiveDefinition, TContext context); + + /// + /// Visits node. + /// + ValueTask VisitComment(GraphQLComment comment, TContext context); + + /// + /// Visits node. + /// + ValueTask VisitDescription(GraphQLDescription description, TContext context); + + /// + /// Visits node. + /// + ValueTask VisitTypeCondition(GraphQLTypeCondition typeCondition, TContext context); + + /// + /// Visits node. + /// + ValueTask VisitAlias(GraphQLAlias alias, TContext context); + } +} diff --git a/src/GraphQLParser/Visitors/INodeVisitorContext.cs b/src/GraphQLParser/Visitors/INodeVisitorContext.cs new file mode 100644 index 00000000..dc596446 --- /dev/null +++ b/src/GraphQLParser/Visitors/INodeVisitorContext.cs @@ -0,0 +1,15 @@ +using System.Threading; + +namespace GraphQLParser.Visitors +{ + /// + /// Context passed into all INodeVisitor.VisitXXX methods. + /// + public interface INodeVisitorContext + { + /// + /// The token to monitor for cancellation requests. + /// + CancellationToken CancellationToken { get; } + } +} diff --git a/src/GraphQLParser/Visitors/IWriteContext.cs b/src/GraphQLParser/Visitors/IWriteContext.cs new file mode 100644 index 00000000..62a1d4cb --- /dev/null +++ b/src/GraphQLParser/Visitors/IWriteContext.cs @@ -0,0 +1,21 @@ +using System.Collections.Generic; +using System.IO; + +namespace GraphQLParser.Visitors +{ + /// + /// Context used by and . + /// + public interface IWriteContext : INodeVisitorContext + { + /// + /// A writer to write document. + /// + TextWriter Writer { get; } + + /// + /// Stack of AST nodes to track the current visitor position. + /// + Stack Parents { get; } + } +} diff --git a/src/GraphQLParser/Visitors/SDLWriter.cs b/src/GraphQLParser/Visitors/SDLWriter.cs new file mode 100644 index 00000000..d242950f --- /dev/null +++ b/src/GraphQLParser/Visitors/SDLWriter.cs @@ -0,0 +1,848 @@ +using System.IO; +using System.Threading.Tasks; +using GraphQLParser.AST; + +namespace GraphQLParser.Visitors +{ + /// + /// Prints AST into the provided as a SDL document. + /// + /// Type of the context object passed into all VisitXXX methods. + public class SDLWriter : DefaultNodeVisitor + where TContext : IWriteContext + { + /// + public override async ValueTask VisitDocument(GraphQLDocument document, TContext context) + { + if (document.Definitions?.Count > 0) + { + for (int i = 0; i < document.Definitions.Count; ++i) + { + await Visit(document.Definitions[i], context).ConfigureAwait(false); + + if (i < document.Definitions.Count - 1) + { + await context.WriteLine().ConfigureAwait(false); + } + } + } + } + + /// + public override async ValueTask VisitComment(GraphQLComment comment, TContext context) + { + int level = GetLevel(context); + + bool needStartNewLine = true; + int length = comment.Text.Span.Length; + for (int i = 0; i < length; ++i) + { + if (needStartNewLine) + { + await WriteIndent(context, level).ConfigureAwait(false); + await context.Write("#").ConfigureAwait(false); + needStartNewLine = false; + } + + char code = comment.Text.Span[i]; + switch (code) + { + case '\r': + break; + + case '\n': + await context.WriteLine().ConfigureAwait(false); + needStartNewLine = true; + break; + + default: + await context.Write(comment.Text.Slice(i, 1)/*code*/).ConfigureAwait(false); + break; + } + } + + await context.WriteLine().ConfigureAwait(false); + } + + /// + public override async ValueTask VisitDescription(GraphQLDescription description, TContext context) + { + bool ShouldBeMultilineBlockString() + { + bool newLineDetected = false; + var span = description.Value.Span; + for (int i = 0; i < span.Length; ++i) + { + char code = span[i]; + + if (code < 0x0020 && code != 0x0009 && code != 0x000A && code != 0x000D) + return false; + + if (code == 0x000A) + newLineDetected = true; + } + + // Note that string value without escape symbols and newline symbols + // MAY BE represented as a single-line BlockString, for example, + // """SOME TEXT""", but it was decided to use more brief "SOME TEXT" + // for such cases. WriteMultilineBlockString method ALWAYS builds + // BlockString printing """ on new line. + return newLineDetected; + } + + async ValueTask WriteMultilineBlockString() + { + int level = GetLevel(context); + + await WriteIndent(context, level).ConfigureAwait(false); + await context.Write("\"\"\"").ConfigureAwait(false); + await context.WriteLine().ConfigureAwait(false); + + bool needStartNewLine = true; + int length = description.Value.Span.Length; + // http://spec.graphql.org/October2021/#BlockStringCharacter + for (int i = 0; i < length; ++i) + { + if (needStartNewLine) + { + await WriteIndent(context, level).ConfigureAwait(false); + needStartNewLine = false; + } + + char code = description.Value.Span[i]; + switch (code) + { + case '\r': + break; + + case '\n': + await context.WriteLine().ConfigureAwait(false); + needStartNewLine = true; + break; + + case '"': + if (i < length - 2 && description.Value.Span[i + 1] == '"' && description.Value.Span[i + 2] == '"') + { + await context.Write("\\\"").ConfigureAwait(false); + } + else + { + await context.Write(description.Value.Slice(i, 1)/*code*/).ConfigureAwait(false); //TODO: change + } + break; + + default: + await context.Write(description.Value.Slice(i, 1)/*code*/).ConfigureAwait(false); //TODO: change + break; + } + } + + await context.WriteLine().ConfigureAwait(false); + await WriteIndent(context, level).ConfigureAwait(false); + await context.Write("\"\"\"").ConfigureAwait(false); + await context.WriteLine().ConfigureAwait(false); + } + + async ValueTask WriteString() + { + int level = GetLevel(context); + await WriteIndent(context, level).ConfigureAwait(false); + await WriteEncodedString(context, description.Value).ConfigureAwait(false); + await context.WriteLine().ConfigureAwait(false); + } + + // http://spec.graphql.org/October2021/#StringValue + if (ShouldBeMultilineBlockString()) + await WriteMultilineBlockString(); + else + await WriteString(); + } + + /// + public override async ValueTask VisitName(GraphQLName name, TContext context) + { + await Visit(name.Comment, context).ConfigureAwait(false); + await context.Write(name.Value).ConfigureAwait(false); + } + + /// + public override async ValueTask VisitFragmentDefinition(GraphQLFragmentDefinition fragmentDefinition, TContext context) + { + await Visit(fragmentDefinition.Comment, context).ConfigureAwait(false); + await context.Write("fragment ").ConfigureAwait(false); + await Visit(fragmentDefinition.Name, context).ConfigureAwait(false); + await context.Write(" ").ConfigureAwait(false); + await Visit(fragmentDefinition.TypeCondition, context).ConfigureAwait(false); + await VisitDirectives(fragmentDefinition, context).ConfigureAwait(false); + await Visit(fragmentDefinition.SelectionSet, context).ConfigureAwait(false); + } + + /// + public override async ValueTask VisitFragmentSpread(GraphQLFragmentSpread fragmentSpread, TContext context) + { + await Visit(fragmentSpread.Comment, context).ConfigureAwait(false); + + int level = GetLevel(context); + await WriteIndent(context, level).ConfigureAwait(false); + + await context.Write("...").ConfigureAwait(false); + await Visit(fragmentSpread.Name, context).ConfigureAwait(false); + await VisitDirectives(fragmentSpread, context).ConfigureAwait(false); + await context.WriteLine().ConfigureAwait(false); + } + + /// + public override async ValueTask VisitInlineFragment(GraphQLInlineFragment inlineFragment, TContext context) + { + await Visit(inlineFragment.Comment, context).ConfigureAwait(false); + + int level = GetLevel(context); + await WriteIndent(context, level).ConfigureAwait(false); + + await context.Write("... ").ConfigureAwait(false); + await Visit(inlineFragment.TypeCondition, context).ConfigureAwait(false); + await VisitDirectives(inlineFragment, context).ConfigureAwait(false); + await Visit(inlineFragment.SelectionSet, context).ConfigureAwait(false); + } + + /// + public override async ValueTask VisitTypeCondition(GraphQLTypeCondition typeCondition, TContext context) + { + await Visit(typeCondition.Comment, context).ConfigureAwait(false); + await context.Write("on ").ConfigureAwait(false); + await Visit(typeCondition.Type, context).ConfigureAwait(false); + } + + /// + public override async ValueTask VisitSelectionSet(GraphQLSelectionSet selectionSet, TContext context) + { + await Visit(selectionSet.Comment, context).ConfigureAwait(false); + await context.WriteLine().ConfigureAwait(false); + int level = GetLevel(context); + await WriteIndent(context, level).ConfigureAwait(false); + await context.Write("{").ConfigureAwait(false); + await context.WriteLine().ConfigureAwait(false); + + if (selectionSet.Selections?.Count > 0) + { + foreach (var selection in selectionSet.Selections) + await Visit(selection, context).ConfigureAwait(false); + } + + await WriteIndent(context, level).ConfigureAwait(false); + await context.Write("}").ConfigureAwait(false); + await context.WriteLine().ConfigureAwait(false); + } + + public override async ValueTask VisitAlias(GraphQLAlias alias, TContext context) + { + await Visit(alias.Comment, context).ConfigureAwait(false); + + var level = GetLevel(context); + await WriteIndent(context, level).ConfigureAwait(false); + + await Visit(alias.Name, context).ConfigureAwait(false); + await context.Write(": ").ConfigureAwait(false); + } + + /// + public override async ValueTask VisitField(GraphQLField field, TContext context) + { + await Visit(field.Comment, context).ConfigureAwait(false); + await Visit(field.Alias, context).ConfigureAwait(false); + if (field.Alias == null) + { + var level = GetLevel(context); + await WriteIndent(context, level).ConfigureAwait(false); + } + await Visit(field.Name, context).ConfigureAwait(false); + if (field.Arguments != null) + { + await context.Write("(").ConfigureAwait(false); + await VisitArguments(field, context).ConfigureAwait(false); + await context.Write(")").ConfigureAwait(false); + } + await VisitDirectives(field, context).ConfigureAwait(false); + + if (field.SelectionSet == null) + await context.WriteLine().ConfigureAwait(false); + else + await Visit(field.SelectionSet, context).ConfigureAwait(false); + } + + /// + public override async ValueTask VisitOperationDefinition(GraphQLOperationDefinition operationDefinition, TContext context) + { + await Visit(operationDefinition.Comment, context).ConfigureAwait(false); + + if (operationDefinition.Name != null) + { + await context.Write(GetOperationType(operationDefinition.Operation)).ConfigureAwait(false); + await context.Write(" ").ConfigureAwait(false); + await Visit(operationDefinition.Name, context).ConfigureAwait(false); + } + + if (operationDefinition.VariableDefinitions?.Count > 0) + { + await context.Write("(").ConfigureAwait(false); + if (operationDefinition.VariableDefinitions?.Count > 0) + { + for (int i = 0; i < operationDefinition.VariableDefinitions.Count; ++i) + { + await Visit(operationDefinition.VariableDefinitions[i], context).ConfigureAwait(false); + if (i < operationDefinition.VariableDefinitions.Count - 1) + await context.Write(", ").ConfigureAwait(false); + } + } + await context.Write(")").ConfigureAwait(false); + } + + await VisitDirectives(operationDefinition, context).ConfigureAwait(false); + await Visit(operationDefinition.SelectionSet, context).ConfigureAwait(false); + } + + /// + public override async ValueTask VisitDirectiveDefinition(GraphQLDirectiveDefinition directiveDefinition, TContext context) + { + await Visit(directiveDefinition.Comment, context).ConfigureAwait(false); + await Visit(directiveDefinition.Description, context).ConfigureAwait(false); + await context.Write("directive ").ConfigureAwait(false); + await Visit(directiveDefinition.Name, context).ConfigureAwait(false); + if (directiveDefinition.Arguments != null) + { + await context.Write("(").ConfigureAwait(false); + await Visit(directiveDefinition, context).ConfigureAwait(false); + await context.Write(")").ConfigureAwait(false); + } + } + + /// + public override async ValueTask VisitVariableDefinition(GraphQLVariableDefinition variableDefinition, TContext context) + { + await Visit(variableDefinition.Comment, context).ConfigureAwait(false); + await Visit(variableDefinition.Variable, context).ConfigureAwait(false); + await context.Write(": ").ConfigureAwait(false); + await Visit(variableDefinition.Type, context).ConfigureAwait(false); + if (variableDefinition.DefaultValue != null) + { + await context.Write(" = ").ConfigureAwait(false); + await Visit(variableDefinition.DefaultValue, context).ConfigureAwait(false); + } + await VisitDirectives(variableDefinition, context).ConfigureAwait(false); + } + + /// + public override async ValueTask VisitVariable(GraphQLVariable variable, TContext context) + { + await Visit(variable.Comment, context).ConfigureAwait(false); + await context.Write("$").ConfigureAwait(false); + await Visit(variable.Name, context).ConfigureAwait(false); + } + + /// + public override async ValueTask VisitBooleanValue(GraphQLScalarValue booleanValue, TContext context) + { + await Visit(booleanValue.Comment, context).ConfigureAwait(false); + await context.Write(booleanValue.Value).ConfigureAwait(false); + } + + /// + public override async ValueTask VisitScalarTypeDefinition(GraphQLScalarTypeDefinition scalarTypeDefinition, TContext context) + { + await Visit(scalarTypeDefinition.Comment, context).ConfigureAwait(false); + await Visit(scalarTypeDefinition.Description, context).ConfigureAwait(false); + await context.Write("scalar ").ConfigureAwait(false); + await Visit(scalarTypeDefinition.Name, context).ConfigureAwait(false); + await VisitDirectives(scalarTypeDefinition, context).ConfigureAwait(false); + await context.WriteLine().ConfigureAwait(false); + } + + /// + public override async ValueTask VisitEnumTypeDefinition(GraphQLEnumTypeDefinition enumTypeDefinition, TContext context) + { + await Visit(enumTypeDefinition.Comment, context).ConfigureAwait(false); + await Visit(enumTypeDefinition.Description, context).ConfigureAwait(false); + await context.Write("enum ").ConfigureAwait(false); + await Visit(enumTypeDefinition.Name, context).ConfigureAwait(false); + await VisitDirectives(enumTypeDefinition, context).ConfigureAwait(false); + if (enumTypeDefinition.Values?.Count > 0) + { + await context.WriteLine().ConfigureAwait(false); + await context.Write("{").ConfigureAwait(false); + await context.WriteLine().ConfigureAwait(false); + for (int i = 0; i < enumTypeDefinition.Values.Count; ++i) + { + await Visit(enumTypeDefinition.Values[i], context).ConfigureAwait(false); + await context.WriteLine().ConfigureAwait(false); + } + await context.Write("}").ConfigureAwait(false); + await context.WriteLine().ConfigureAwait(false); + } + } + + /// + public override async ValueTask VisitEnumValueDefinition(GraphQLEnumValueDefinition enumValueDefinition, TContext context) + { + await Visit(enumValueDefinition.Comment, context).ConfigureAwait(false); + await Visit(enumValueDefinition.Description, context).ConfigureAwait(false); + + int level = GetLevel(context); + await WriteIndent(context, level).ConfigureAwait(false); + + await Visit(enumValueDefinition.Name, context).ConfigureAwait(false); + await VisitDirectives(enumValueDefinition, context).ConfigureAwait(false); + } + + /// + public override async ValueTask VisitInputObjectTypeDefinition(GraphQLInputObjectTypeDefinition inputObjectTypeDefinition, TContext context) + { + await Visit(inputObjectTypeDefinition.Comment, context).ConfigureAwait(false); + await Visit(inputObjectTypeDefinition.Description, context).ConfigureAwait(false); + await context.Write("input ").ConfigureAwait(false); + await Visit(inputObjectTypeDefinition.Name, context).ConfigureAwait(false); + await VisitDirectives(inputObjectTypeDefinition, context).ConfigureAwait(false); + if (inputObjectTypeDefinition.Fields?.Count > 0) + { + await context.WriteLine().ConfigureAwait(false); + await context.Write("{").ConfigureAwait(false); + await context.WriteLine().ConfigureAwait(false); + + for (int i = 0; i < inputObjectTypeDefinition.Fields.Count; ++i) + { + await Visit(inputObjectTypeDefinition.Fields[i], context).ConfigureAwait(false); + await context.WriteLine().ConfigureAwait(false); + } + + await context.Write("}").ConfigureAwait(false); + await context.WriteLine().ConfigureAwait(false); + } + else + { + await context.WriteLine().ConfigureAwait(false); + } + } + + /// + public override async ValueTask VisitInputValueDefinition(GraphQLInputValueDefinition inputValueDefinition, TContext context) + { + await Visit(inputValueDefinition.Comment, context).ConfigureAwait(false); + await Visit(inputValueDefinition.Description, context).ConfigureAwait(false); + + int level = GetLevel(context); + await WriteIndent(context, level).ConfigureAwait(false); + + await Visit(inputValueDefinition.Name, context).ConfigureAwait(false); + await context.Write(": ").ConfigureAwait(false); + await Visit(inputValueDefinition.Type, context).ConfigureAwait(false); + if (inputValueDefinition.DefaultValue != null) + { + await context.Write(" = ").ConfigureAwait(false); + await Visit(inputValueDefinition.DefaultValue, context).ConfigureAwait(false); + } + await VisitDirectives(inputValueDefinition, context).ConfigureAwait(false); + } + + /// + public override async ValueTask VisitObjectTypeDefinition(GraphQLObjectTypeDefinition objectTypeDefinition, TContext context) + { + await Visit(objectTypeDefinition.Comment, context).ConfigureAwait(false); + await Visit(objectTypeDefinition.Description, context).ConfigureAwait(false); + await context.Write("type ").ConfigureAwait(false); + await Visit(objectTypeDefinition.Name, context).ConfigureAwait(false); + await VisitInterfaces(objectTypeDefinition, context).ConfigureAwait(false); + await VisitDirectives(objectTypeDefinition, context).ConfigureAwait(false); + + if (objectTypeDefinition.Fields?.Count > 0) + { + await context.WriteLine().ConfigureAwait(false); + await context.Write("{").ConfigureAwait(false); + await context.WriteLine().ConfigureAwait(false); + + for (int i = 0; i < objectTypeDefinition.Fields.Count; ++i) + { + await Visit(objectTypeDefinition.Fields[i], context).ConfigureAwait(false); + await context.WriteLine().ConfigureAwait(false); + } + + await context.Write("}").ConfigureAwait(false); + await context.WriteLine().ConfigureAwait(false); + } + else + { + await context.WriteLine().ConfigureAwait(false); + } + } + + /// + public override async ValueTask VisitInterfaceTypeDefinition(GraphQLInterfaceTypeDefinition interfaceTypeDefinition, TContext context) + { + await Visit(interfaceTypeDefinition.Comment, context).ConfigureAwait(false); + await Visit(interfaceTypeDefinition.Description, context).ConfigureAwait(false); + await context.Write("interface ").ConfigureAwait(false); + await Visit(interfaceTypeDefinition.Name, context).ConfigureAwait(false); + await VisitInterfaces(interfaceTypeDefinition, context).ConfigureAwait(false); + await VisitDirectives(interfaceTypeDefinition, context).ConfigureAwait(false); + await context.WriteLine().ConfigureAwait(false); + await context.Write("{").ConfigureAwait(false); + await context.WriteLine().ConfigureAwait(false); + + if (interfaceTypeDefinition.Fields?.Count > 0) + { + for (int i = 0; i < interfaceTypeDefinition.Fields.Count; ++i) + { + await Visit(interfaceTypeDefinition.Fields[i], context).ConfigureAwait(false); + await context.WriteLine().ConfigureAwait(false); + } + } + + await context.Write("}").ConfigureAwait(false); + await context.WriteLine().ConfigureAwait(false); + } + + /// + public override async ValueTask VisitFieldDefinition(GraphQLFieldDefinition fieldDefinition, TContext context) + { + await Visit(fieldDefinition.Comment, context).ConfigureAwait(false); + await Visit(fieldDefinition.Description, context).ConfigureAwait(false); + + int level = GetLevel(context); + await WriteIndent(context, level).ConfigureAwait(false); + + await Visit(fieldDefinition.Name, context).ConfigureAwait(false); + if (fieldDefinition.Arguments?.Count > 0) + { + await context.Write("(").ConfigureAwait(false); + for (int i = 0; i < fieldDefinition.Arguments.Count; ++i) + { + await Visit(fieldDefinition.Arguments[i], context).ConfigureAwait(false); + if (i < fieldDefinition.Arguments.Count - 1) + await context.Write(", ").ConfigureAwait(false); + } + await context.Write(")").ConfigureAwait(false); + } + await context.Write(": ").ConfigureAwait(false); + await Visit(fieldDefinition.Type, context).ConfigureAwait(false); + await VisitDirectives(fieldDefinition, context).ConfigureAwait(false); + } + + /// + public override async ValueTask VisitSchemaDefinition(GraphQLSchemaDefinition schemaDefinition, TContext context) + { + await Visit(schemaDefinition.Comment, context).ConfigureAwait(false); + await Visit(schemaDefinition.Description, context).ConfigureAwait(false); + await context.Write("schema").ConfigureAwait(false); + await VisitDirectives(schemaDefinition, context).ConfigureAwait(false); + + await context.WriteLine().ConfigureAwait(false); + await context.Write("{").ConfigureAwait(false); + await context.WriteLine().ConfigureAwait(false); + + if (schemaDefinition.OperationTypes?.Count > 0) + { + for (int i = 0; i < schemaDefinition.OperationTypes.Count; ++i) + { + await Visit(schemaDefinition.OperationTypes[i], context).ConfigureAwait(false); + await context.WriteLine().ConfigureAwait(false); + } + } + + await context.Write("}").ConfigureAwait(false); + await context.WriteLine().ConfigureAwait(false); + } + + /// + public override async ValueTask VisitRootOperationTypeDefinition(GraphQLRootOperationTypeDefinition rootOperationTypeDefinition, TContext context) + { + await Visit(rootOperationTypeDefinition.Comment, context).ConfigureAwait(false); + + int level = GetLevel(context); + await WriteIndent(context, level).ConfigureAwait(false); + + await context.Write(GetOperationType(rootOperationTypeDefinition.Operation)).ConfigureAwait(false); + await context.Write(": ").ConfigureAwait(false); + await Visit(rootOperationTypeDefinition.Type, context).ConfigureAwait(false); + } + + /// + public override async ValueTask VisitUnionTypeDefinition(GraphQLUnionTypeDefinition unionTypeDefinition, TContext context) + { + await Visit(unionTypeDefinition.Comment, context).ConfigureAwait(false); + await Visit(unionTypeDefinition.Description, context).ConfigureAwait(false); + await context.Write("union ").ConfigureAwait(false); + await Visit(unionTypeDefinition.Name, context).ConfigureAwait(false); + await VisitDirectives(unionTypeDefinition, context).ConfigureAwait(false); + + if (unionTypeDefinition.Types?.Count > 0) + { + await context.Write(" = ").ConfigureAwait(false); + + for (int i = 0; i < unionTypeDefinition.Types.Count; ++i) + { + await Visit(unionTypeDefinition.Types[i], context).ConfigureAwait(false); + if (i < unionTypeDefinition.Types.Count - 1) + await context.Write(" | ").ConfigureAwait(false); + } + } + } + + /// + public override async ValueTask VisitDirective(GraphQLDirective directive, TContext context) + { + await Visit(directive.Comment, context).ConfigureAwait(false); + await context.Write("@").ConfigureAwait(false); + await Visit(directive.Name, context).ConfigureAwait(false); + if (directive.Arguments != null) + { + await context.Write("(").ConfigureAwait(false); + await VisitArguments(directive, context).ConfigureAwait(false); + await context.Write(")").ConfigureAwait(false); + } + } + + /// + public override async ValueTask VisitArgument(GraphQLArgument argument, TContext context) + { + await Visit(argument.Comment, context).ConfigureAwait(false); + await Visit(argument.Name, context).ConfigureAwait(false); + await context.Write(": ").ConfigureAwait(false); + await Visit(argument.Value, context).ConfigureAwait(false); + } + + /// + public override async ValueTask VisitNonNullType(GraphQLNonNullType nonNullType, TContext context) + { + await Visit(nonNullType.Comment, context).ConfigureAwait(false); + await Visit(nonNullType.Type, context).ConfigureAwait(false); + await context.Write("!").ConfigureAwait(false); + } + + /// + public override async ValueTask VisitListType(GraphQLListType listType, TContext context) + { + await Visit(listType.Comment, context).ConfigureAwait(false); + await context.Write("[").ConfigureAwait(false); + await Visit(listType.Type, context).ConfigureAwait(false); + await context.Write("]").ConfigureAwait(false); + } + + /// + public override async ValueTask VisitListValue(GraphQLListValue listValue, TContext context) + { + await Visit(listValue.Comment, context).ConfigureAwait(false); + if (listValue.Values?.Count > 0) + { + await context.Write("[").ConfigureAwait(false); + for (int i = 0; i < listValue.Values.Count; ++i) + { + await Visit(listValue.Values[i], context).ConfigureAwait(false); + if (i < listValue.Values.Count - 1) + await context.Write(", ").ConfigureAwait(false); + } + await context.Write("]").ConfigureAwait(false); + } + } + + /// + public override async ValueTask VisitNullValue(GraphQLScalarValue nullValue, TContext context) + { + await Visit(nullValue.Comment, context).ConfigureAwait(false); + await context.Write("null").ConfigureAwait(false); + } + + /// + public override async ValueTask VisitStringValue(GraphQLScalarValue stringValue, TContext context) + { + await Visit(stringValue.Comment, context).ConfigureAwait(false); + await WriteEncodedString(context, stringValue.Value); + } + + /// + public override async ValueTask VisitIntValue(GraphQLScalarValue intValue, TContext context) + { + await Visit(intValue.Comment, context).ConfigureAwait(false); + await context.Write(intValue.Value).ConfigureAwait(false); + } + + /// + public override async ValueTask VisitFloatValue(GraphQLScalarValue floatValue, TContext context) + { + await Visit(floatValue.Comment, context).ConfigureAwait(false); + await context.Write(floatValue.Value).ConfigureAwait(false); + } + + /// + public override async ValueTask VisitEnumValue(GraphQLScalarValue enumValue, TContext context) + { + await Visit(enumValue.Comment, context).ConfigureAwait(false); + await context.Write(enumValue.Value).ConfigureAwait(false); + } + + /// + public override async ValueTask VisitObjectValue(GraphQLObjectValue objectValue, TContext context) + { + await Visit(objectValue.Comment, context).ConfigureAwait(false); + + if (objectValue.Fields?.Count > 0) + { + await context.Write("{ ").ConfigureAwait(false); + for (int i = 0; i < objectValue.Fields.Count; ++i) + { + await Visit(objectValue.Fields[i], context).ConfigureAwait(false); + if (i < objectValue.Fields.Count - 1) + await context.Write(", ").ConfigureAwait(false); + } + await context.Write(" }").ConfigureAwait(false); + } + else + { + await context.Write("{ }").ConfigureAwait(false); + } + } + + /// + public override async ValueTask VisitObjectField(GraphQLObjectField objectField, TContext context) + { + await Visit(objectField.Comment, context).ConfigureAwait(false); + await Visit(objectField.Name, context).ConfigureAwait(false); + await context.Write(": ").ConfigureAwait(false); + await Visit(objectField.Value, context).ConfigureAwait(false); + } + + /// + public override ValueTask VisitNamedType(GraphQLNamedType namedType, TContext context) + { + return base.VisitNamedType(namedType, context); + } + + /// + public override async ValueTask Visit(ASTNode? node, TContext context) + { + if (node == null) + return; + + context.Parents.Push(node); + + await base.Visit(node, context).ConfigureAwait(false); + + context.Parents.Pop(); + } + + private async ValueTask VisitArguments(IHasArgumentsNode node, TContext context) + { + if (node.Arguments?.Count > 0) + { + for (int i = 0; i < node.Arguments.Count; ++i) + { + await Visit(node.Arguments[i], context).ConfigureAwait(false); + if (i < node.Arguments.Count - 1) + await context.Write(", ").ConfigureAwait(false); + } + } + } + + private async ValueTask VisitInterfaces(IHasInterfacesNode node, TContext context) + { + if (node.Interfaces?.Count > 0) + { + await context.Write(" implements ").ConfigureAwait(false); + + for (int i = 0; i < node.Interfaces.Count; ++i) + { + await Visit(node.Interfaces[i], context).ConfigureAwait(false); + if (i < node.Interfaces.Count - 1) + await context.Write(" & ").ConfigureAwait(false); + } + } + } + + private async ValueTask VisitDirectives(IHasDirectivesNode node, TContext context) + { + if (node.Directives?.Count > 0) + { + await context.Write(" ").ConfigureAwait(false); + + for (int i = 0; i < node.Directives.Count; ++i) + { + await Visit(node.Directives[i], context).ConfigureAwait(false); + if (i < node.Directives.Count - 1) + await context.Write(" ").ConfigureAwait(false); + } + } + } + + private string GetOperationType(OperationType type) => type switch + { + OperationType.Mutation => "mutation", + OperationType.Subscription => "subscription", + _ => "query", + }; + + private async ValueTask WriteIndent(TContext context, int level) + { + for (int i = 0; i < level; ++i) + await context.Write(" ").ConfigureAwait(false); + } + + private int GetLevel(TContext context) + { + int level = 0; + + if (context.Parents.Count > 0) + { + var currentNode = context.Parents.Pop(); + + foreach (var node in context.Parents) + { + if (node is GraphQLSelectionSet || + node is GraphQLTypeDefinition || + node is GraphQLInputObjectTypeDefinition || + node is GraphQLSchemaDefinition) + ++level; + } + + if (currentNode is GraphQLDescription) + --level; + else if (currentNode is GraphQLComment && context.Parents.Peek() is GraphQLTypeDefinition) + --level; + + context.Parents.Push(currentNode); + } + + return level; + } + + // http://spec.graphql.org/October2021/#StringCharacter + private async ValueTask WriteEncodedString(TContext context, ROM value) + { + await context.Write("\"").ConfigureAwait(false); + + for (int i = 0; i < value.Span.Length; ++i) + { + char code = value.Span[i]; + if (code < ' ') + { + if (code == '\b') + await context.Write("\\b").ConfigureAwait(false); + else if (code == '\f') + await context.Write("\\f").ConfigureAwait(false); + else if (code == '\n') + await context.Write("\\n").ConfigureAwait(false); + else if (code == '\r') + await context.Write("\\r").ConfigureAwait(false); + else if (code == '\t') + await context.Write("\\t").ConfigureAwait(false); + else + await context.Write("\\u" + ((int)code).ToString("X4")).ConfigureAwait(false); + } + else if (code == '\\') + await context.Write("\\\\").ConfigureAwait(false); + else if (code == '"') + await context.Write("\\\"").ConfigureAwait(false); + else + await context.Write(value.Slice(i, 1)/*code*/).ConfigureAwait(false); // TODO: no method for char + } + + await context.Write("\"").ConfigureAwait(false); + } + } +} diff --git a/src/GraphQLParser/Visitors/StructureWriter.cs b/src/GraphQLParser/Visitors/StructureWriter.cs new file mode 100644 index 00000000..764ee33e --- /dev/null +++ b/src/GraphQLParser/Visitors/StructureWriter.cs @@ -0,0 +1,58 @@ +using System.IO; +using System.Threading.Tasks; +using GraphQLParser.AST; + +namespace GraphQLParser.Visitors +{ + /// + /// Prints AST into the provided as a hierarchy of node types. + /// + /// Type of the context object passed into all VisitXXX methods. + public class StructureWriter : DefaultNodeVisitor + where TContext : IWriteContext + { + /// + /// Creates visitor with the specified options. + /// + /// Visitor options. + public StructureWriter(StructureWriterOptions options) + { + Options = options; + } + + private StructureWriterOptions Options { get; } + + /// + public override async ValueTask Visit(ASTNode? node, TContext context) + { + if (node == null) + return; + + for (int i = 0; i < context.Parents.Count; ++i) + await context.Write(" ").ConfigureAwait(false); + + context.Parents.Push(node); + await context.Write(node.Kind.ToString()).ConfigureAwait(false); + if (Options.WriteNames && node is GraphQLName name) + { + await context.Write(" [").ConfigureAwait(false); + await context.Write(name.Value).ConfigureAwait(false); + await context.Write("]").ConfigureAwait(false); + } + await context.WriteLine().ConfigureAwait(false); + await base.Visit(node, context).ConfigureAwait(false); + context.Parents.Pop(); + } + } + + /// + /// Options for . + /// + public class StructureWriterOptions + { + /// + /// Write into the output. + /// + public bool WriteNames { get; set; } = true; + } +} diff --git a/src/GraphQLParser/Visitors/WriteContextExtensions.cs b/src/GraphQLParser/Visitors/WriteContextExtensions.cs new file mode 100644 index 00000000..cd6463c4 --- /dev/null +++ b/src/GraphQLParser/Visitors/WriteContextExtensions.cs @@ -0,0 +1,43 @@ +using System; +using System.IO; +using System.Runtime.CompilerServices; +using System.Threading; +using System.Threading.Tasks; + +namespace GraphQLParser.Visitors +{ + /// + /// Extension methods for writing into . + /// + public static class WriteContextExtensions + { + private static readonly ROM _newLine = Environment.NewLine; + + /// + /// Writes the specified to the provided context + /// using from it. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ValueTask Write(this TContext context, ROM value) + where TContext : IWriteContext + { + var task = +#if NETSTANDARD2_0 + // no cancellationToken support on netstandard2.0 + context.Writer.WriteAsync(value.ToString()); +#elif NETSTANDARD2_1_OR_GREATER + context.Writer.WriteAsync(value, context.CancellationToken); +#endif + return new ValueTask(task); + } + + /// + /// Writes to the provided context + /// using from it. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ValueTask WriteLine(this TContext context) + where TContext : IWriteContext + => Write(context, _newLine); + } +}