query.go

  1package query
  2
  3import (
  4	"fmt"
  5	"strings"
  6	"text/scanner"
  7
  8	"github.com/vektah/gqlgen/neelance/common"
  9	"github.com/vektah/gqlgen/neelance/errors"
 10)
 11
 12type Document struct {
 13	Operations OperationList
 14	Fragments  FragmentList
 15}
 16
 17type OperationList []*Operation
 18
 19func (l OperationList) Get(name string) *Operation {
 20	for _, f := range l {
 21		if f.Name.Name == name {
 22			return f
 23		}
 24	}
 25	return nil
 26}
 27
 28type FragmentList []*FragmentDecl
 29
 30func (l FragmentList) Get(name string) *FragmentDecl {
 31	for _, f := range l {
 32		if f.Name.Name == name {
 33			return f
 34		}
 35	}
 36	return nil
 37}
 38
 39type Operation struct {
 40	Type       OperationType
 41	Name       common.Ident
 42	Vars       common.InputValueList
 43	Selections []Selection
 44	Directives common.DirectiveList
 45	Loc        errors.Location
 46}
 47
 48type OperationType string
 49
 50const (
 51	Query        OperationType = "QUERY"
 52	Mutation                   = "MUTATION"
 53	Subscription               = "SUBSCRIPTION"
 54)
 55
 56type Fragment struct {
 57	On         common.TypeName
 58	Selections []Selection
 59}
 60
 61type FragmentDecl struct {
 62	Fragment
 63	Name       common.Ident
 64	Directives common.DirectiveList
 65	Loc        errors.Location
 66}
 67
 68type Selection interface {
 69	isSelection()
 70}
 71
 72type Field struct {
 73	Alias           common.Ident
 74	Name            common.Ident
 75	Arguments       common.ArgumentList
 76	Directives      common.DirectiveList
 77	Selections      []Selection
 78	SelectionSetLoc errors.Location
 79}
 80
 81type InlineFragment struct {
 82	Fragment
 83	Directives common.DirectiveList
 84	Loc        errors.Location
 85}
 86
 87type FragmentSpread struct {
 88	Name       common.Ident
 89	Directives common.DirectiveList
 90	Loc        errors.Location
 91}
 92
 93func (Field) isSelection()          {}
 94func (InlineFragment) isSelection() {}
 95func (FragmentSpread) isSelection() {}
 96
 97func Parse(queryString string) (*Document, *errors.QueryError) {
 98	sc := &scanner.Scanner{
 99		Mode: scanner.ScanIdents | scanner.ScanInts | scanner.ScanFloats | scanner.ScanStrings,
100	}
101	sc.Init(strings.NewReader(queryString))
102
103	l := common.New(sc)
104	var doc *Document
105	err := l.CatchSyntaxError(func() {
106		doc = parseDocument(l)
107	})
108	if err != nil {
109		return nil, err
110	}
111
112	return doc, nil
113}
114
115func parseDocument(l *common.Lexer) *Document {
116	d := &Document{}
117	for l.Peek() != scanner.EOF {
118		if l.Peek() == '{' {
119			op := &Operation{Type: Query, Loc: l.Location()}
120			op.Selections = parseSelectionSet(l)
121			d.Operations = append(d.Operations, op)
122			continue
123		}
124
125		loc := l.Location()
126		switch x := l.ConsumeIdent(); x {
127		case "query":
128			op := parseOperation(l, Query)
129			op.Loc = loc
130			d.Operations = append(d.Operations, op)
131
132		case "mutation":
133			d.Operations = append(d.Operations, parseOperation(l, Mutation))
134
135		case "subscription":
136			d.Operations = append(d.Operations, parseOperation(l, Subscription))
137
138		case "fragment":
139			frag := parseFragment(l)
140			frag.Loc = loc
141			d.Fragments = append(d.Fragments, frag)
142
143		default:
144			l.SyntaxError(fmt.Sprintf(`unexpected %q, expecting "fragment"`, x))
145		}
146	}
147	return d
148}
149
150func parseOperation(l *common.Lexer, opType OperationType) *Operation {
151	op := &Operation{Type: opType}
152	op.Name.Loc = l.Location()
153	if l.Peek() == scanner.Ident {
154		op.Name = l.ConsumeIdentWithLoc()
155	}
156	op.Directives = common.ParseDirectives(l)
157	if l.Peek() == '(' {
158		l.ConsumeToken('(')
159		for l.Peek() != ')' {
160			loc := l.Location()
161			l.ConsumeToken('$')
162			iv := common.ParseInputValue(l)
163			iv.Loc = loc
164			op.Vars = append(op.Vars, iv)
165		}
166		l.ConsumeToken(')')
167	}
168	op.Selections = parseSelectionSet(l)
169	return op
170}
171
172func parseFragment(l *common.Lexer) *FragmentDecl {
173	f := &FragmentDecl{}
174	f.Name = l.ConsumeIdentWithLoc()
175	l.ConsumeKeyword("on")
176	f.On = common.TypeName{Ident: l.ConsumeIdentWithLoc()}
177	f.Directives = common.ParseDirectives(l)
178	f.Selections = parseSelectionSet(l)
179	return f
180}
181
182func parseSelectionSet(l *common.Lexer) []Selection {
183	var sels []Selection
184	l.ConsumeToken('{')
185	for l.Peek() != '}' {
186		sels = append(sels, parseSelection(l))
187	}
188	l.ConsumeToken('}')
189	return sels
190}
191
192func parseSelection(l *common.Lexer) Selection {
193	if l.Peek() == '.' {
194		return parseSpread(l)
195	}
196	return parseField(l)
197}
198
199func parseField(l *common.Lexer) *Field {
200	f := &Field{}
201	f.Alias = l.ConsumeIdentWithLoc()
202	f.Name = f.Alias
203	if l.Peek() == ':' {
204		l.ConsumeToken(':')
205		f.Name = l.ConsumeIdentWithLoc()
206	}
207	if l.Peek() == '(' {
208		f.Arguments = common.ParseArguments(l)
209	}
210	f.Directives = common.ParseDirectives(l)
211	if l.Peek() == '{' {
212		f.SelectionSetLoc = l.Location()
213		f.Selections = parseSelectionSet(l)
214	}
215	return f
216}
217
218func parseSpread(l *common.Lexer) Selection {
219	loc := l.Location()
220	l.ConsumeToken('.')
221	l.ConsumeToken('.')
222	l.ConsumeToken('.')
223
224	f := &InlineFragment{Loc: loc}
225	if l.Peek() == scanner.Ident {
226		ident := l.ConsumeIdentWithLoc()
227		if ident.Name != "on" {
228			fs := &FragmentSpread{
229				Name: ident,
230				Loc:  loc,
231			}
232			fs.Directives = common.ParseDirectives(l)
233			return fs
234		}
235		f.On = common.TypeName{Ident: l.ConsumeIdentWithLoc()}
236	}
237	f.Directives = common.ParseDirectives(l)
238	f.Selections = parseSelectionSet(l)
239	return f
240}
241
242func (d *Document) GetOperation(operationName string) (*Operation, error) {
243	if len(d.Operations) == 0 {
244		return nil, fmt.Errorf("no operations in query document")
245	}
246
247	if operationName == "" {
248		if len(d.Operations) > 1 {
249			return nil, fmt.Errorf("more than one operation in query document and no operation name given")
250		}
251		for _, op := range d.Operations {
252			return op, nil // return the one and only operation
253		}
254	}
255
256	op := d.Operations.Get(operationName)
257	if op == nil {
258		return nil, fmt.Errorf("no operation with name %q", operationName)
259	}
260	return op, nil
261}