mirror of
https://github.com/stashapp/stash.git
synced 2025-12-17 05:47:37 +01:00
* Bump golang.org/x/text from 0.3.7 to 0.3.8 Bumps [golang.org/x/text](https://github.com/golang/text) from 0.3.7 to 0.3.8. - [Release notes](https://github.com/golang/text/releases) - [Commits](https://github.com/golang/text/compare/v0.3.7...v0.3.8) --- updated-dependencies: - dependency-name: golang.org/x/text dependency-type: direct:production ... Signed-off-by: dependabot[bot] <support@github.com> * Update go dependencies * Update x/net --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
534 lines
13 KiB
Go
534 lines
13 KiB
Go
package parser
|
|
|
|
import (
|
|
. "github.com/vektah/gqlparser/v2/ast"
|
|
"github.com/vektah/gqlparser/v2/lexer"
|
|
)
|
|
|
|
func ParseSchema(source *Source) (*SchemaDocument, error) {
|
|
p := parser{
|
|
lexer: lexer.New(source),
|
|
}
|
|
ast, err := p.parseSchemaDocument(), p.err
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
for _, def := range ast.Definitions {
|
|
def.BuiltIn = source.BuiltIn
|
|
}
|
|
for _, def := range ast.Extensions {
|
|
def.BuiltIn = source.BuiltIn
|
|
}
|
|
|
|
return ast, nil
|
|
}
|
|
|
|
func ParseSchemas(inputs ...*Source) (*SchemaDocument, error) {
|
|
ast := &SchemaDocument{}
|
|
for _, input := range inputs {
|
|
inputAst, err := ParseSchema(input)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
ast.Merge(inputAst)
|
|
}
|
|
return ast, nil
|
|
}
|
|
|
|
func (p *parser) parseSchemaDocument() *SchemaDocument {
|
|
var doc SchemaDocument
|
|
doc.Position = p.peekPos()
|
|
for p.peek().Kind != lexer.EOF {
|
|
if p.err != nil {
|
|
return nil
|
|
}
|
|
|
|
var description string
|
|
if p.peek().Kind == lexer.BlockString || p.peek().Kind == lexer.String {
|
|
description = p.parseDescription()
|
|
}
|
|
|
|
if p.peek().Kind != lexer.Name {
|
|
p.unexpectedError()
|
|
break
|
|
}
|
|
|
|
switch p.peek().Value {
|
|
case "scalar", "type", "interface", "union", "enum", "input":
|
|
doc.Definitions = append(doc.Definitions, p.parseTypeSystemDefinition(description))
|
|
case "schema":
|
|
doc.Schema = append(doc.Schema, p.parseSchemaDefinition(description))
|
|
case "directive":
|
|
doc.Directives = append(doc.Directives, p.parseDirectiveDefinition(description))
|
|
case "extend":
|
|
if description != "" {
|
|
p.unexpectedToken(p.prev)
|
|
}
|
|
p.parseTypeSystemExtension(&doc)
|
|
default:
|
|
p.unexpectedError()
|
|
return nil
|
|
}
|
|
}
|
|
|
|
return &doc
|
|
}
|
|
|
|
func (p *parser) parseDescription() string {
|
|
token := p.peek()
|
|
|
|
if token.Kind != lexer.BlockString && token.Kind != lexer.String {
|
|
return ""
|
|
}
|
|
|
|
return p.next().Value
|
|
}
|
|
|
|
func (p *parser) parseTypeSystemDefinition(description string) *Definition {
|
|
tok := p.peek()
|
|
if tok.Kind != lexer.Name {
|
|
p.unexpectedError()
|
|
return nil
|
|
}
|
|
|
|
switch tok.Value {
|
|
case "scalar":
|
|
return p.parseScalarTypeDefinition(description)
|
|
case "type":
|
|
return p.parseObjectTypeDefinition(description)
|
|
case "interface":
|
|
return p.parseInterfaceTypeDefinition(description)
|
|
case "union":
|
|
return p.parseUnionTypeDefinition(description)
|
|
case "enum":
|
|
return p.parseEnumTypeDefinition(description)
|
|
case "input":
|
|
return p.parseInputObjectTypeDefinition(description)
|
|
default:
|
|
p.unexpectedError()
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func (p *parser) parseSchemaDefinition(description string) *SchemaDefinition {
|
|
p.expectKeyword("schema")
|
|
|
|
def := SchemaDefinition{Description: description}
|
|
def.Position = p.peekPos()
|
|
def.Description = description
|
|
def.Directives = p.parseDirectives(true)
|
|
|
|
p.some(lexer.BraceL, lexer.BraceR, func() {
|
|
def.OperationTypes = append(def.OperationTypes, p.parseOperationTypeDefinition())
|
|
})
|
|
return &def
|
|
}
|
|
|
|
func (p *parser) parseOperationTypeDefinition() *OperationTypeDefinition {
|
|
var op OperationTypeDefinition
|
|
op.Position = p.peekPos()
|
|
op.Operation = p.parseOperationType()
|
|
p.expect(lexer.Colon)
|
|
op.Type = p.parseName()
|
|
return &op
|
|
}
|
|
|
|
func (p *parser) parseScalarTypeDefinition(description string) *Definition {
|
|
p.expectKeyword("scalar")
|
|
|
|
var def Definition
|
|
def.Position = p.peekPos()
|
|
def.Kind = Scalar
|
|
def.Description = description
|
|
def.Name = p.parseName()
|
|
def.Directives = p.parseDirectives(true)
|
|
return &def
|
|
}
|
|
|
|
func (p *parser) parseObjectTypeDefinition(description string) *Definition {
|
|
p.expectKeyword("type")
|
|
|
|
var def Definition
|
|
def.Position = p.peekPos()
|
|
def.Kind = Object
|
|
def.Description = description
|
|
def.Name = p.parseName()
|
|
def.Interfaces = p.parseImplementsInterfaces()
|
|
def.Directives = p.parseDirectives(true)
|
|
def.Fields = p.parseFieldsDefinition()
|
|
return &def
|
|
}
|
|
|
|
func (p *parser) parseImplementsInterfaces() []string {
|
|
var types []string
|
|
if p.peek().Value == "implements" {
|
|
p.next()
|
|
// optional leading ampersand
|
|
p.skip(lexer.Amp)
|
|
|
|
types = append(types, p.parseName())
|
|
for p.skip(lexer.Amp) && p.err == nil {
|
|
types = append(types, p.parseName())
|
|
}
|
|
}
|
|
return types
|
|
}
|
|
|
|
func (p *parser) parseFieldsDefinition() FieldList {
|
|
var defs FieldList
|
|
p.some(lexer.BraceL, lexer.BraceR, func() {
|
|
defs = append(defs, p.parseFieldDefinition())
|
|
})
|
|
return defs
|
|
}
|
|
|
|
func (p *parser) parseFieldDefinition() *FieldDefinition {
|
|
var def FieldDefinition
|
|
def.Position = p.peekPos()
|
|
def.Description = p.parseDescription()
|
|
def.Name = p.parseName()
|
|
def.Arguments = p.parseArgumentDefs()
|
|
p.expect(lexer.Colon)
|
|
def.Type = p.parseTypeReference()
|
|
def.Directives = p.parseDirectives(true)
|
|
|
|
return &def
|
|
}
|
|
|
|
func (p *parser) parseArgumentDefs() ArgumentDefinitionList {
|
|
var args ArgumentDefinitionList
|
|
p.some(lexer.ParenL, lexer.ParenR, func() {
|
|
args = append(args, p.parseArgumentDef())
|
|
})
|
|
return args
|
|
}
|
|
|
|
func (p *parser) parseArgumentDef() *ArgumentDefinition {
|
|
var def ArgumentDefinition
|
|
def.Position = p.peekPos()
|
|
def.Description = p.parseDescription()
|
|
def.Name = p.parseName()
|
|
p.expect(lexer.Colon)
|
|
def.Type = p.parseTypeReference()
|
|
if p.skip(lexer.Equals) {
|
|
def.DefaultValue = p.parseValueLiteral(true)
|
|
}
|
|
def.Directives = p.parseDirectives(true)
|
|
return &def
|
|
}
|
|
|
|
func (p *parser) parseInputValueDef() *FieldDefinition {
|
|
var def FieldDefinition
|
|
def.Position = p.peekPos()
|
|
def.Description = p.parseDescription()
|
|
def.Name = p.parseName()
|
|
p.expect(lexer.Colon)
|
|
def.Type = p.parseTypeReference()
|
|
if p.skip(lexer.Equals) {
|
|
def.DefaultValue = p.parseValueLiteral(true)
|
|
}
|
|
def.Directives = p.parseDirectives(true)
|
|
return &def
|
|
}
|
|
|
|
func (p *parser) parseInterfaceTypeDefinition(description string) *Definition {
|
|
p.expectKeyword("interface")
|
|
|
|
var def Definition
|
|
def.Position = p.peekPos()
|
|
def.Kind = Interface
|
|
def.Description = description
|
|
def.Name = p.parseName()
|
|
def.Interfaces = p.parseImplementsInterfaces()
|
|
def.Directives = p.parseDirectives(true)
|
|
def.Fields = p.parseFieldsDefinition()
|
|
return &def
|
|
}
|
|
|
|
func (p *parser) parseUnionTypeDefinition(description string) *Definition {
|
|
p.expectKeyword("union")
|
|
|
|
var def Definition
|
|
def.Position = p.peekPos()
|
|
def.Kind = Union
|
|
def.Description = description
|
|
def.Name = p.parseName()
|
|
def.Directives = p.parseDirectives(true)
|
|
def.Types = p.parseUnionMemberTypes()
|
|
return &def
|
|
}
|
|
|
|
func (p *parser) parseUnionMemberTypes() []string {
|
|
var types []string
|
|
if p.skip(lexer.Equals) {
|
|
// optional leading pipe
|
|
p.skip(lexer.Pipe)
|
|
|
|
types = append(types, p.parseName())
|
|
for p.skip(lexer.Pipe) && p.err == nil {
|
|
types = append(types, p.parseName())
|
|
}
|
|
}
|
|
return types
|
|
}
|
|
|
|
func (p *parser) parseEnumTypeDefinition(description string) *Definition {
|
|
p.expectKeyword("enum")
|
|
|
|
var def Definition
|
|
def.Position = p.peekPos()
|
|
def.Kind = Enum
|
|
def.Description = description
|
|
def.Name = p.parseName()
|
|
def.Directives = p.parseDirectives(true)
|
|
def.EnumValues = p.parseEnumValuesDefinition()
|
|
return &def
|
|
}
|
|
|
|
func (p *parser) parseEnumValuesDefinition() EnumValueList {
|
|
var values EnumValueList
|
|
p.some(lexer.BraceL, lexer.BraceR, func() {
|
|
values = append(values, p.parseEnumValueDefinition())
|
|
})
|
|
return values
|
|
}
|
|
|
|
func (p *parser) parseEnumValueDefinition() *EnumValueDefinition {
|
|
return &EnumValueDefinition{
|
|
Position: p.peekPos(),
|
|
Description: p.parseDescription(),
|
|
Name: p.parseName(),
|
|
Directives: p.parseDirectives(true),
|
|
}
|
|
}
|
|
|
|
func (p *parser) parseInputObjectTypeDefinition(description string) *Definition {
|
|
p.expectKeyword("input")
|
|
|
|
var def Definition
|
|
def.Position = p.peekPos()
|
|
def.Kind = InputObject
|
|
def.Description = description
|
|
def.Name = p.parseName()
|
|
def.Directives = p.parseDirectives(true)
|
|
def.Fields = p.parseInputFieldsDefinition()
|
|
return &def
|
|
}
|
|
|
|
func (p *parser) parseInputFieldsDefinition() FieldList {
|
|
var values FieldList
|
|
p.some(lexer.BraceL, lexer.BraceR, func() {
|
|
values = append(values, p.parseInputValueDef())
|
|
})
|
|
return values
|
|
}
|
|
|
|
func (p *parser) parseTypeSystemExtension(doc *SchemaDocument) {
|
|
p.expectKeyword("extend")
|
|
|
|
switch p.peek().Value {
|
|
case "schema":
|
|
doc.SchemaExtension = append(doc.SchemaExtension, p.parseSchemaExtension())
|
|
case "scalar":
|
|
doc.Extensions = append(doc.Extensions, p.parseScalarTypeExtension())
|
|
case "type":
|
|
doc.Extensions = append(doc.Extensions, p.parseObjectTypeExtension())
|
|
case "interface":
|
|
doc.Extensions = append(doc.Extensions, p.parseInterfaceTypeExtension())
|
|
case "union":
|
|
doc.Extensions = append(doc.Extensions, p.parseUnionTypeExtension())
|
|
case "enum":
|
|
doc.Extensions = append(doc.Extensions, p.parseEnumTypeExtension())
|
|
case "input":
|
|
doc.Extensions = append(doc.Extensions, p.parseInputObjectTypeExtension())
|
|
default:
|
|
p.unexpectedError()
|
|
}
|
|
}
|
|
|
|
func (p *parser) parseSchemaExtension() *SchemaDefinition {
|
|
p.expectKeyword("schema")
|
|
|
|
var def SchemaDefinition
|
|
def.Position = p.peekPos()
|
|
def.Directives = p.parseDirectives(true)
|
|
p.some(lexer.BraceL, lexer.BraceR, func() {
|
|
def.OperationTypes = append(def.OperationTypes, p.parseOperationTypeDefinition())
|
|
})
|
|
if len(def.Directives) == 0 && len(def.OperationTypes) == 0 {
|
|
p.unexpectedError()
|
|
}
|
|
return &def
|
|
}
|
|
|
|
func (p *parser) parseScalarTypeExtension() *Definition {
|
|
p.expectKeyword("scalar")
|
|
|
|
var def Definition
|
|
def.Position = p.peekPos()
|
|
def.Kind = Scalar
|
|
def.Name = p.parseName()
|
|
def.Directives = p.parseDirectives(true)
|
|
if len(def.Directives) == 0 {
|
|
p.unexpectedError()
|
|
}
|
|
return &def
|
|
}
|
|
|
|
func (p *parser) parseObjectTypeExtension() *Definition {
|
|
p.expectKeyword("type")
|
|
|
|
var def Definition
|
|
def.Position = p.peekPos()
|
|
def.Kind = Object
|
|
def.Name = p.parseName()
|
|
def.Interfaces = p.parseImplementsInterfaces()
|
|
def.Directives = p.parseDirectives(true)
|
|
def.Fields = p.parseFieldsDefinition()
|
|
if len(def.Interfaces) == 0 && len(def.Directives) == 0 && len(def.Fields) == 0 {
|
|
p.unexpectedError()
|
|
}
|
|
return &def
|
|
}
|
|
|
|
func (p *parser) parseInterfaceTypeExtension() *Definition {
|
|
p.expectKeyword("interface")
|
|
|
|
var def Definition
|
|
def.Position = p.peekPos()
|
|
def.Kind = Interface
|
|
def.Name = p.parseName()
|
|
def.Directives = p.parseDirectives(true)
|
|
def.Fields = p.parseFieldsDefinition()
|
|
if len(def.Directives) == 0 && len(def.Fields) == 0 {
|
|
p.unexpectedError()
|
|
}
|
|
return &def
|
|
}
|
|
|
|
func (p *parser) parseUnionTypeExtension() *Definition {
|
|
p.expectKeyword("union")
|
|
|
|
var def Definition
|
|
def.Position = p.peekPos()
|
|
def.Kind = Union
|
|
def.Name = p.parseName()
|
|
def.Directives = p.parseDirectives(true)
|
|
def.Types = p.parseUnionMemberTypes()
|
|
|
|
if len(def.Directives) == 0 && len(def.Types) == 0 {
|
|
p.unexpectedError()
|
|
}
|
|
return &def
|
|
}
|
|
|
|
func (p *parser) parseEnumTypeExtension() *Definition {
|
|
p.expectKeyword("enum")
|
|
|
|
var def Definition
|
|
def.Position = p.peekPos()
|
|
def.Kind = Enum
|
|
def.Name = p.parseName()
|
|
def.Directives = p.parseDirectives(true)
|
|
def.EnumValues = p.parseEnumValuesDefinition()
|
|
if len(def.Directives) == 0 && len(def.EnumValues) == 0 {
|
|
p.unexpectedError()
|
|
}
|
|
return &def
|
|
}
|
|
|
|
func (p *parser) parseInputObjectTypeExtension() *Definition {
|
|
p.expectKeyword("input")
|
|
|
|
var def Definition
|
|
def.Position = p.peekPos()
|
|
def.Kind = InputObject
|
|
def.Name = p.parseName()
|
|
def.Directives = p.parseDirectives(false)
|
|
def.Fields = p.parseInputFieldsDefinition()
|
|
if len(def.Directives) == 0 && len(def.Fields) == 0 {
|
|
p.unexpectedError()
|
|
}
|
|
return &def
|
|
}
|
|
|
|
func (p *parser) parseDirectiveDefinition(description string) *DirectiveDefinition {
|
|
p.expectKeyword("directive")
|
|
p.expect(lexer.At)
|
|
|
|
var def DirectiveDefinition
|
|
def.Position = p.peekPos()
|
|
def.Description = description
|
|
def.Name = p.parseName()
|
|
def.Arguments = p.parseArgumentDefs()
|
|
|
|
if peek := p.peek(); peek.Kind == lexer.Name && peek.Value == "repeatable" {
|
|
def.IsRepeatable = true
|
|
p.skip(lexer.Name)
|
|
}
|
|
|
|
p.expectKeyword("on")
|
|
def.Locations = p.parseDirectiveLocations()
|
|
return &def
|
|
}
|
|
|
|
func (p *parser) parseDirectiveLocations() []DirectiveLocation {
|
|
p.skip(lexer.Pipe)
|
|
|
|
locations := []DirectiveLocation{p.parseDirectiveLocation()}
|
|
|
|
for p.skip(lexer.Pipe) && p.err == nil {
|
|
locations = append(locations, p.parseDirectiveLocation())
|
|
}
|
|
|
|
return locations
|
|
}
|
|
|
|
func (p *parser) parseDirectiveLocation() DirectiveLocation {
|
|
name := p.expect(lexer.Name)
|
|
|
|
switch name.Value {
|
|
case `QUERY`:
|
|
return LocationQuery
|
|
case `MUTATION`:
|
|
return LocationMutation
|
|
case `SUBSCRIPTION`:
|
|
return LocationSubscription
|
|
case `FIELD`:
|
|
return LocationField
|
|
case `FRAGMENT_DEFINITION`:
|
|
return LocationFragmentDefinition
|
|
case `FRAGMENT_SPREAD`:
|
|
return LocationFragmentSpread
|
|
case `INLINE_FRAGMENT`:
|
|
return LocationInlineFragment
|
|
case `VARIABLE_DEFINITION`:
|
|
return LocationVariableDefinition
|
|
case `SCHEMA`:
|
|
return LocationSchema
|
|
case `SCALAR`:
|
|
return LocationScalar
|
|
case `OBJECT`:
|
|
return LocationObject
|
|
case `FIELD_DEFINITION`:
|
|
return LocationFieldDefinition
|
|
case `ARGUMENT_DEFINITION`:
|
|
return LocationArgumentDefinition
|
|
case `INTERFACE`:
|
|
return LocationInterface
|
|
case `UNION`:
|
|
return LocationUnion
|
|
case `ENUM`:
|
|
return LocationEnum
|
|
case `ENUM_VALUE`:
|
|
return LocationEnumValue
|
|
case `INPUT_OBJECT`:
|
|
return LocationInputObject
|
|
case `INPUT_FIELD_DEFINITION`:
|
|
return LocationInputFieldDefinition
|
|
}
|
|
|
|
p.unexpectedToken(name)
|
|
return ""
|
|
}
|