Selection set, fields

This commit is contained in:
thosakwe 2017-07-04 12:46:01 -04:00
parent 3be644b96f
commit d517dcf235
8 changed files with 258 additions and 66 deletions

View file

@ -9,10 +9,13 @@ class FragmentSpreadContext extends Node {
FragmentSpreadContext(this.ELLIPSIS, this.NAME);
String get name => NAME.text;
@override
SourceSpan get span {
SourceLocation end;
return new SourceSpan(ELLIPSIS.span?.start, end, toSource());
var out = ELLIPSIS.span.union(NAME.span);
if (directives.isEmpty) return out;
return directives.fold<SourceSpan>(out, (o, d) => o.union(d.span));
}
@override

View file

@ -10,8 +10,11 @@ class SelectionSetContext extends Node {
SelectionSetContext(this.LBRACE, this.RBRACE);
@override
SourceSpan get span =>
new SourceSpan(LBRACE.span?.start, RBRACE.span?.end, toSource());
SourceSpan get span {
var out =
selections.fold<SourceSpan>(LBRACE.span, (out, s) => out.union(s.span));
return out.union(RBRACE.span);
}
@override
String toSource() {

View file

@ -38,13 +38,57 @@ class Parser {
FragmentDefinitionContext parseFragmentDefinition() {}
FragmentSpreadContext parseFragmentSpread() {}
FragmentSpreadContext parseFragmentSpread() {
if (next(TokenType.ELLIPSIS)) {
var ELLIPSIS = current;
if (next(TokenType.NAME)) {
var NAME = current;
return new FragmentSpreadContext(ELLIPSIS, NAME)
..directives.addAll(parseDirectives());
} else
throw new SyntaxError.fromSourceLocation(
'Expected name in fragment spread.', ELLIPSIS.span.end);
} else
return null;
}
InlineFragmentContext parseInlineFragment() {}
SelectionSetContext parseSelectionSet() {}
SelectionSetContext parseSelectionSet() {
if (next(TokenType.LBRACE)) {
var LBRACE = current;
List<SelectionContext> selections = [];
SelectionContext selection = parseSelection();
SelectionContext parseSelection() {}
while (selection != null) {
selections.add(selection);
next(TokenType.COMMA);
selection = parseSelection();
}
if (next(TokenType.RBRACE))
return new SelectionSetContext(LBRACE, current)
..selections.addAll(selections);
else
throw new SyntaxError.fromSourceLocation(
'Expected "}" after selection set.',
selections.isEmpty ? LBRACE.span.end : selections.last.span.end);
} else
return null;
}
SelectionContext parseSelection() {
var field = parseField();
if (field != null) return new SelectionContext(field);
var fragmentSpread = parseFragmentSpread();
if (fragmentSpread != null)
return new SelectionContext(null, fragmentSpread);
var inlineFragment = parseInlineFragment();
if (inlineFragment != null)
return new SelectionContext(null, null, inlineFragment);
else
return null;
}
FieldContext parseField() {
var fieldName = parseFieldName();

View file

@ -19,6 +19,9 @@ ArgumentContext parseArgument(String text) => parse(text).parseArgument();
Matcher isArgument(String name, value) => new _IsArgument(name, value);
Matcher isArgumentList(List<Matcher> arguments) =>
new _IsArgumentList(arguments);
class _IsArgument extends Matcher {
final String name;
final value;
@ -41,3 +44,28 @@ class _IsArgument extends Matcher {
matchState);
}
}
class _IsArgumentList extends Matcher {
final List<Matcher> arguments;
_IsArgumentList(this.arguments);
@override
Description describe(Description description) {
return description.add('is list of ${arguments.length} argument(s)');
}
@override
bool matches(item, Map matchState) {
var args =
item is List<ArgumentContext> ? item : parse(item).parseArguments();
if (args.length != arguments.length) return false;
for (int i = 0; i < args.length; i++) {
if (!arguments[i].matches(args[i], matchState)) return false;
}
return true;
}
}

View file

@ -35,6 +35,9 @@ Matcher isDirective(String name, {Matcher valueOrVariable, Matcher argument}) =>
new _IsDirective(name,
valueOrVariable: valueOrVariable, argument: argument);
Matcher isDirectiveList(List<Matcher> directives) =>
new _IsDirectiveList(directives);
class _IsDirective extends Matcher {
final String name;
final Matcher valueOrVariable, argument;
@ -54,8 +57,8 @@ class _IsDirective extends Matcher {
}
@override
bool matches(String item, Map matchState) {
var directive = parseDirective(item);
bool matches(item, Map matchState) {
var directive = item is DirectiveContext ? item : parseDirective(item);
if (directive == null) return false;
if (valueOrVariable != null) {
if (directive.valueOrVariable == null)
@ -74,3 +77,28 @@ class _IsDirective extends Matcher {
return true;
}
}
class _IsDirectiveList extends Matcher {
final List<Matcher> directives;
_IsDirectiveList(this.directives);
@override
Description describe(Description description) {
return description.add('is list of ${directives.length} directive(s)');
}
@override
bool matches(item, Map matchState) {
var args =
item is List<DirectiveContext> ? item : parse(item).parseDirectives();
if (args.length != directives.length) return false;
for (int i = 0; i < args.length; i++) {
if (!directives[i].matches(args[i], matchState)) return false;
}
return true;
}
}

View file

@ -3,6 +3,8 @@ import 'package:test/test.dart';
import 'common.dart';
import 'argument_test.dart';
import 'directive_test.dart';
import 'fragment_spread_test.dart';
import 'selection_set_test.dart';
import 'value_test.dart';
main() {
@ -45,15 +47,27 @@ main() {
test('with directives', () {
expect(
'foo: bar @bar @baz: 2 @quux (one: 1)',
'foo: bar (a: 2) @bar @baz: 2 @quux (one: 1)',
isField(
fieldName: isFieldName('foo', alias: 'bar'),
arguments: isArgumentList([isArgument('a', 2)]),
directives: isDirectiveList([
isDirective('bar'),
isDirective('baz', valueOrVariable: isValue(2)),
isDirective('quux', argument: isArgument('one', 1))
])));
});
test('with selection set', () {
expect(
'foo: bar {baz, ...quux}',
isField(
fieldName: isFieldName('foo', alias: 'bar'),
selectionSet: isSelectionSet([
isField(fieldName: isFieldName('baz')),
isFragmentSpread('quux')
])));
});
});
}
@ -61,12 +75,6 @@ FieldContext parseField(String text) => parse(text).parseField();
FieldNameContext parseFieldName(String text) => parse(text).parseFieldName();
Matcher isArgumentList(List<Matcher> arguments) =>
new _IsArgumentList(arguments);
Matcher isDirectiveList(List<Matcher> directives) =>
new _IsDirectiveList(directives);
Matcher isField(
{Matcher fieldName,
Matcher arguments,
@ -121,53 +129,3 @@ class _IsFieldName extends Matcher {
return fieldName.name == name;
}
}
class _IsArgumentList extends Matcher {
final List<Matcher> arguments;
_IsArgumentList(this.arguments);
@override
Description describe(Description description) {
return description.add('is list of ${arguments.length} argument(s)');
}
@override
bool matches(item, Map matchState) {
var args =
item is List<ArgumentContext> ? item : parse(item).parseArguments();
if (args.length != arguments.length) return false;
for (int i = 0; i < args.length; i++) {
if (!arguments[i].matches(args[i], matchState)) return false;
}
return true;
}
}
class _IsDirectiveList extends Matcher {
final List<Matcher> directives;
_IsDirectiveList(this.directives);
@override
Description describe(Description description) {
return description.add('is list of ${directives.length} directive(s)');
}
@override
bool matches(item, Map matchState) {
var args =
item is List<DirectiveContext> ? item : parse(item).parseDirectives();
if (args.length != directives.length) return false;
for (int i = 0; i < args.length; i++) {
if (!directives[i].matches(args[i], matchState)) return false;
}
return true;
}
}

View file

@ -0,0 +1,59 @@
import 'package:graphql_parser/graphql_parser.dart';
import 'package:test/test.dart';
import 'common.dart';
import 'argument_test.dart';
import 'directive_test.dart';
main() {
test('name only', () {
expect(['...foo', '... foo'], everyElement(isFragmentSpread('foo')));
});
test('with directives', () {
expect(
'... foo @bar @baz: 2 @quux(one: 1)',
isFragmentSpread('foo',
directives: isDirectiveList([
isDirective('bar'),
isDirective('baz', valueOrVariable: equals(2)),
isDirective('quux', argument: isArgument('one', 1))
])));
});
test('exceptions', () {
expect(() => parseFragmentSpread('...'), throwsSyntaxError);
});
}
FragmentSpreadContext parseFragmentSpread(String text) =>
parse(text).parseFragmentSpread();
Matcher isFragmentSpread(String name, {Matcher directives}) =>
new _IsFragmentSpread(name, directives);
class _IsFragmentSpread extends Matcher {
final String name;
final Matcher directives;
_IsFragmentSpread(this.name, this.directives);
@override
Description describe(Description description) {
if (directives != null)
return directives.describe(
description.add('is a fragment spread named "$name" that also '));
return description.add('is a fragment spread named "$name"');
}
@override
bool matches(item, Map matchState) {
var spread =
item is FragmentSpreadContext ? item : parseFragmentSpread(item);
if (spread == null) return false;
if (spread.name != name) return false;
if (directives != null)
return directives.matches(spread.directives, matchState);
else
return true;
}
}

View file

@ -0,0 +1,69 @@
import 'package:graphql_parser/graphql_parser.dart';
import 'package:test/test.dart';
import 'common.dart';
import 'field_test.dart';
import 'fragment_spread_test.dart';
// TODO: Test inline fragment...
main() {
test('empty', () {
expect('{}', isSelectionSet([]));
});
test('with commas', () {
expect(
'{foo, bar: baz}',
isSelectionSet([
isField(fieldName: isFieldName('foo')),
isField(fieldName: isFieldName('bar', alias: 'baz'))
]));
});
test('no commas', () {
expect(
'{foo bar: baz ...quux}',
isSelectionSet([
isField(fieldName: isFieldName('foo')),
isField(fieldName: isFieldName('bar', alias: 'baz')),
isFragmentSpread('quux')
]));
});
test('exceptions', () {
expect(() => parseSelectionSet('{foo,bar,baz'), throwsSyntaxError);
});
}
SelectionSetContext parseSelectionSet(String text) =>
parse(text).parseSelectionSet();
Matcher isSelectionSet(List<Matcher> selections) =>
new _IsSelectionSet(selections);
class _IsSelectionSet extends Matcher {
final List<Matcher> selections;
_IsSelectionSet(this.selections);
@override
Description describe(Description description) {
return description
.add('is selection set with ${selections.length} selection(s)');
}
@override
bool matches(item, Map matchState) {
var set = item is SelectionSetContext ? item : parseSelectionSet(item);
if (set == null) return false;
if (set.selections.length != selections.length) return false;
for (int i = 0; i < set.selections.length; i++) {
var sel = set.selections[i];
if (!selections[i].matches(
sel.field ?? sel.fragmentSpread ?? sel.inlineFragment, matchState))
return false;
}
return true;
}
}