gurkle_parser/schema/
grammar.rs

1use combine::combinator::sep_by1;
2use combine::combinator::{choice, eof, many, many1, optional, position};
3use combine::easy::{Error, Errors};
4use combine::error::StreamError;
5use combine::{parser, ParseResult, Parser};
6
7use crate::common::{default_value, directives, parse_type, string};
8use crate::helpers::{ident, kind, name, punct};
9use crate::schema::ast::*;
10use crate::schema::error::ParseError;
11use crate::tokenizer::{Kind as T, Token, TokenStream};
12
13pub fn schema<'a>(input: &mut TokenStream<'a>) -> ParseResult<SchemaDefinition, TokenStream<'a>> {
14    (
15        position().skip(ident("schema")),
16        parser(directives),
17        punct("{")
18            .with(many((kind(T::Name).skip(punct(":")), name())))
19            .skip(punct("}")),
20    )
21        .flat_map(
22            |(position, directives, operations): (_, _, Vec<(Token, _)>)| {
23                let mut query = None;
24                let mut mutation = None;
25                let mut subscription = None;
26                let mut err = Errors::empty(position);
27                for (oper, type_name) in operations {
28                    match oper.value {
29                        "query" if query.is_some() => {
30                            err.add_error(Error::unexpected_static_message(
31                                "duplicate `query` operation",
32                            ));
33                        }
34                        "query" => {
35                            query = Some(type_name);
36                        }
37                        "mutation" if mutation.is_some() => {
38                            err.add_error(Error::unexpected_static_message(
39                                "duplicate `mutation` operation",
40                            ));
41                        }
42                        "mutation" => {
43                            mutation = Some(type_name);
44                        }
45                        "subscription" if subscription.is_some() => {
46                            err.add_error(Error::unexpected_static_message(
47                                "duplicate `subscription` operation",
48                            ));
49                        }
50                        "subscription" => {
51                            subscription = Some(type_name);
52                        }
53                        _ => {
54                            err.add_error(Error::unexpected_token(oper));
55                            err.add_error(Error::expected_static_message("query"));
56                            err.add_error(Error::expected_static_message("mutation"));
57                            err.add_error(Error::expected_static_message("subscription"));
58                        }
59                    }
60                }
61                if !err.errors.is_empty() {
62                    return Err(err);
63                }
64                Ok(SchemaDefinition {
65                    position,
66                    directives,
67                    query,
68                    mutation,
69                    subscription,
70                })
71            },
72        )
73        .parse_stream(input)
74}
75
76pub fn scalar_type<'a>(input: &mut TokenStream<'a>) -> ParseResult<ScalarType, TokenStream<'a>> {
77    (position(), ident("scalar").with(name()), parser(directives))
78        .map(|(position, name, directives)| ScalarType {
79            position,
80            description: None,
81            name,
82            directives,
83        })
84        .parse_stream(input)
85}
86
87pub fn scalar_type_extension<'a>(
88    input: &mut TokenStream<'a>,
89) -> ParseResult<ScalarTypeExtension, TokenStream<'a>> {
90    (position(), ident("scalar").with(name()), parser(directives))
91        .flat_map(|(position, name, directives)| {
92            if directives.is_empty() {
93                let mut e = Errors::empty(position);
94                e.add_error(Error::expected_static_message(
95                    "Scalar type extension should contain at least \
96                 one directive.",
97                ));
98                return Err(e);
99            }
100            Ok(ScalarTypeExtension {
101                position,
102                name,
103                directives,
104            })
105        })
106        .parse_stream(input)
107}
108
109pub fn implements_interfaces<'a>(
110    input: &mut TokenStream<'a>,
111) -> ParseResult<Vec<NamedType>, TokenStream<'a>> {
112    optional(
113        ident("implements")
114            .skip(optional(punct("&")))
115            .with(sep_by1(name(), punct("&"))),
116    )
117    .map(|opt| opt.unwrap_or_else(Vec::new))
118    .parse_stream(input)
119}
120
121pub fn input_value<'a>(input: &mut TokenStream<'a>) -> ParseResult<InputValue, TokenStream<'a>> {
122    (
123        position(),
124        optional(parser(string)),
125        name(),
126        punct(":").with(parser(parse_type)),
127        optional(punct("=").with(parser(default_value))),
128        parser(directives),
129    )
130        .map(
131            |(position, description, name, value_type, default_value, directives)| InputValue {
132                position,
133                description,
134                name,
135                value_type,
136                default_value,
137                directives,
138            },
139        )
140        .parse_stream(input)
141}
142
143pub fn arguments_definition<'a>(
144    input: &mut TokenStream<'a>,
145) -> ParseResult<Vec<InputValue>, TokenStream<'a>> {
146    optional(punct("(").with(many1(parser(input_value))).skip(punct(")")))
147        .map(|v| v.unwrap_or_else(Vec::new))
148        .parse_stream(input)
149}
150
151pub fn field<'a>(input: &mut TokenStream<'a>) -> ParseResult<Field, TokenStream<'a>> {
152    (
153        position(),
154        optional(parser(string)),
155        name(),
156        parser(arguments_definition),
157        punct(":").with(parser(parse_type)),
158        parser(directives),
159    )
160        .map(
161            |(position, description, name, arguments, field_type, directives)| Field {
162                position,
163                description,
164                name,
165                arguments,
166                field_type,
167                directives,
168            },
169        )
170        .parse_stream(input)
171}
172
173pub fn fields<'a>(input: &mut TokenStream<'a>) -> ParseResult<Vec<Field>, TokenStream<'a>> {
174    optional(punct("{").with(many1(parser(field))).skip(punct("}")))
175        .map(|v| v.unwrap_or_else(Vec::new))
176        .parse_stream(input)
177}
178
179pub fn object_type<'a>(input: &mut TokenStream<'a>) -> ParseResult<ObjectType, TokenStream<'a>> {
180    (
181        position(),
182        ident("type").with(name()),
183        parser(implements_interfaces),
184        parser(directives),
185        parser(fields),
186    )
187        .map(|(position, name, interfaces, directives, fields)| {
188            ObjectType {
189                position,
190                name,
191                directives,
192                fields,
193                implements_interfaces: interfaces,
194                description: None, // is filled in described_definition
195            }
196        })
197        .parse_stream(input)
198}
199
200pub fn object_type_extension<'a>(
201    input: &mut TokenStream<'a>,
202) -> ParseResult<ObjectTypeExtension, TokenStream<'a>> {
203    (
204        position(),
205        ident("type").with(name()),
206        parser(implements_interfaces),
207        parser(directives),
208        parser(fields),
209    )
210        .flat_map(|(position, name, interfaces, directives, fields)| {
211            if interfaces.is_empty() && directives.is_empty() && fields.is_empty() {
212                let mut e = Errors::empty(position);
213                e.add_error(Error::expected_static_message(
214                    "Object type extension should contain at least \
215                     one interface, directive or field.",
216                ));
217                return Err(e);
218            }
219            Ok(ObjectTypeExtension {
220                position,
221                name,
222                directives,
223                fields,
224                implements_interfaces: interfaces,
225            })
226        })
227        .parse_stream(input)
228}
229
230pub fn interface_type<'a>(
231    input: &mut TokenStream<'a>,
232) -> ParseResult<InterfaceType, TokenStream<'a>> {
233    (
234        position(),
235        ident("interface").with(name()),
236        parser(directives),
237        parser(fields),
238    )
239        .map(|(position, name, directives, fields)| {
240            InterfaceType {
241                position,
242                name,
243                directives,
244                fields,
245                description: None, // is filled in described_definition
246            }
247        })
248        .parse_stream(input)
249}
250
251pub fn interface_type_extension<'a>(
252    input: &mut TokenStream<'a>,
253) -> ParseResult<InterfaceTypeExtension, TokenStream<'a>> {
254    (
255        position(),
256        ident("interface").with(name()),
257        parser(directives),
258        parser(fields),
259    )
260        .flat_map(|(position, name, directives, fields)| {
261            if directives.is_empty() && fields.is_empty() {
262                let mut e = Errors::empty(position);
263                e.add_error(Error::expected_static_message(
264                    "Interface type extension should contain at least \
265                     one directive or field.",
266                ));
267                return Err(e);
268            }
269            Ok(InterfaceTypeExtension {
270                position,
271                name,
272                directives,
273                fields,
274            })
275        })
276        .parse_stream(input)
277}
278
279pub fn union_members<'a>(
280    input: &mut TokenStream<'a>,
281) -> ParseResult<Vec<NamedType>, TokenStream<'a>> {
282    optional(punct("|"))
283        .with(sep_by1(name(), punct("|")))
284        .parse_stream(input)
285}
286
287pub fn union_type<'a>(input: &mut TokenStream<'a>) -> ParseResult<UnionType, TokenStream<'a>> {
288    (
289        position(),
290        ident("union").with(name()),
291        parser(directives),
292        optional(punct("=").with(parser(union_members))),
293    )
294        .map(|(position, name, directives, types)| {
295            UnionType {
296                position,
297                name,
298                directives,
299                types: types.unwrap_or_else(Vec::new),
300                description: None, // is filled in described_definition
301            }
302        })
303        .parse_stream(input)
304}
305
306pub fn union_type_extension<'a>(
307    input: &mut TokenStream<'a>,
308) -> ParseResult<UnionTypeExtension, TokenStream<'a>> {
309    (
310        position(),
311        ident("union").with(name()),
312        parser(directives),
313        optional(punct("=").with(parser(union_members))),
314    )
315        .flat_map(|(position, name, directives, types)| {
316            if directives.is_empty() && types.is_none() {
317                let mut e = Errors::empty(position);
318                e.add_error(Error::expected_static_message(
319                    "Union type extension should contain at least \
320                 one directive or type.",
321                ));
322                return Err(e);
323            }
324            Ok(UnionTypeExtension {
325                position,
326                name,
327                directives,
328                types: types.unwrap_or_else(Vec::new),
329            })
330        })
331        .parse_stream(input)
332}
333
334pub fn enum_values<'a>(
335    input: &mut TokenStream<'a>,
336) -> ParseResult<Vec<EnumValue>, TokenStream<'a>> {
337    punct("{")
338        .with(many1(
339            (
340                position(),
341                optional(parser(string)),
342                name(),
343                parser(directives),
344            )
345                .map(|(position, description, name, directives)| EnumValue {
346                    position,
347                    description,
348                    name,
349                    directives,
350                }),
351        ))
352        .skip(punct("}"))
353        .parse_stream(input)
354}
355
356pub fn enum_type<'a>(input: &mut TokenStream<'a>) -> ParseResult<EnumType, TokenStream<'a>> {
357    (
358        position(),
359        ident("enum").with(name()),
360        parser(directives),
361        optional(parser(enum_values)),
362    )
363        .map(|(position, name, directives, values)| {
364            EnumType {
365                position,
366                name,
367                directives,
368                values: values.unwrap_or_else(Vec::new),
369                description: None, // is filled in described_definition
370            }
371        })
372        .parse_stream(input)
373}
374
375pub fn enum_type_extension<'a>(
376    input: &mut TokenStream<'a>,
377) -> ParseResult<EnumTypeExtension, TokenStream<'a>> {
378    (
379        position(),
380        ident("enum").with(name()),
381        parser(directives),
382        optional(parser(enum_values)),
383    )
384        .flat_map(|(position, name, directives, values)| {
385            if directives.is_empty() && values.is_none() {
386                let mut e = Errors::empty(position);
387                e.add_error(Error::expected_static_message(
388                    "Enum type extension should contain at least \
389                 one directive or value.",
390                ));
391                return Err(e);
392            }
393            Ok(EnumTypeExtension {
394                position,
395                name,
396                directives,
397                values: values.unwrap_or_else(Vec::new),
398            })
399        })
400        .parse_stream(input)
401}
402
403pub fn input_fields<'a>(
404    input: &mut TokenStream<'a>,
405) -> ParseResult<Vec<InputValue>, TokenStream<'a>> {
406    optional(punct("{").with(many1(parser(input_value))).skip(punct("}")))
407        .map(|v| v.unwrap_or_else(Vec::new))
408        .parse_stream(input)
409}
410
411pub fn input_object_type<'a>(
412    input: &mut TokenStream<'a>,
413) -> ParseResult<InputObjectType, TokenStream<'a>> {
414    (
415        position(),
416        ident("input").with(name()),
417        parser(directives),
418        parser(input_fields),
419    )
420        .map(|(position, name, directives, fields)| {
421            InputObjectType {
422                position,
423                name,
424                directives,
425                fields,
426                description: None, // is filled in described_definition
427            }
428        })
429        .parse_stream(input)
430}
431
432pub fn input_object_type_extension<'a>(
433    input: &mut TokenStream<'a>,
434) -> ParseResult<InputObjectTypeExtension, TokenStream<'a>> {
435    (
436        position(),
437        ident("input").with(name()),
438        parser(directives),
439        parser(input_fields),
440    )
441        .flat_map(|(position, name, directives, fields)| {
442            if directives.is_empty() && fields.is_empty() {
443                let mut e = Errors::empty(position);
444                e.add_error(Error::expected_static_message(
445                    "Input object type extension should contain at least \
446                     one directive or field.",
447                ));
448                return Err(e);
449            }
450            Ok(InputObjectTypeExtension {
451                position,
452                name,
453                directives,
454                fields,
455            })
456        })
457        .parse_stream(input)
458}
459
460pub fn directive_locations<'a>(
461    input: &mut TokenStream<'a>,
462) -> ParseResult<Vec<DirectiveLocation>, TokenStream<'a>> {
463    optional(optional(punct("|")).with(sep_by1(
464        kind(T::Name).and_then(|tok| tok.value.parse::<DirectiveLocation>()),
465        punct("|"),
466    )))
467    .map(|opt| opt.unwrap_or_else(Vec::new))
468    .parse_stream(input)
469}
470
471pub fn directive_definition<'a>(
472    input: &mut TokenStream<'a>,
473) -> ParseResult<DirectiveDefinition, TokenStream<'a>> {
474    (
475        position(),
476        ident("directive").and(punct("@")).with(name()),
477        parser(arguments_definition),
478        optional(ident("repeatable")),
479        ident("on").with(parser(directive_locations)),
480    )
481        .map(|(position, name, arguments, repeatable, locations)| {
482            DirectiveDefinition {
483                position,
484                name,
485                arguments,
486                locations,
487                repeatable: repeatable.is_some(),
488                description: None, // is filled in described_definition
489            }
490        })
491        .parse_stream(input)
492}
493
494pub fn described_definition<'a>(
495    input: &mut TokenStream<'a>,
496) -> ParseResult<Definition, TokenStream<'a>> {
497    use self::TypeDefinition::*;
498    (
499        optional(parser(string)),
500        choice((
501            choice((
502                parser(scalar_type).map(Scalar),
503                parser(object_type).map(Object),
504                parser(interface_type).map(Interface),
505                parser(union_type).map(Union),
506                parser(enum_type).map(Enum),
507                parser(input_object_type).map(InputObject),
508            ))
509            .map(Definition::TypeDefinition),
510            parser(directive_definition).map(Definition::DirectiveDefinition),
511        )),
512    )
513        // We can't set description inside type definition parser, because
514        // that means parser will need to backtrace, and that in turn
515        // means that error reporting is bad (along with performance)
516        .map(|(descr, mut def)| {
517            use crate::schema::ast::Definition::TypeDefinition as T;
518            use crate::schema::ast::Definition::*;
519            use crate::schema::ast::TypeDefinition::*;
520            match def {
521                T(Scalar(ref mut s)) => s.description = descr,
522                T(Object(ref mut o)) => o.description = descr,
523                T(Interface(ref mut i)) => i.description = descr,
524                T(Union(ref mut u)) => u.description = descr,
525                T(Enum(ref mut e)) => e.description = descr,
526                T(InputObject(ref mut o)) => o.description = descr,
527                DirectiveDefinition(ref mut d) => d.description = descr,
528                SchemaDefinition(_) => unreachable!(),
529                TypeExtension(_) => unreachable!(),
530            }
531            def
532        })
533        .parse_stream(input)
534}
535
536pub fn type_extension<'a>(
537    input: &mut TokenStream<'a>,
538) -> ParseResult<TypeExtension, TokenStream<'a>> {
539    ident("extend")
540        .with(choice((
541            parser(scalar_type_extension).map(TypeExtension::Scalar),
542            parser(object_type_extension).map(TypeExtension::Object),
543            parser(interface_type_extension).map(TypeExtension::Interface),
544            parser(union_type_extension).map(TypeExtension::Union),
545            parser(enum_type_extension).map(TypeExtension::Enum),
546            parser(input_object_type_extension).map(TypeExtension::InputObject),
547        )))
548        .parse_stream(input)
549}
550
551pub fn definition<'a>(input: &mut TokenStream<'a>) -> ParseResult<Definition, TokenStream<'a>> {
552    choice((
553        parser(schema).map(Definition::SchemaDefinition),
554        parser(type_extension).map(Definition::TypeExtension),
555        parser(described_definition),
556    ))
557    .parse_stream(input)
558}
559
560/// Parses a piece of schema language and returns an AST
561pub fn parse_schema(s: &str) -> Result<Document, ParseError> {
562    let mut tokens = TokenStream::new(s);
563    let (doc, _) = many1(parser(definition))
564        .map(|d| Document { definitions: d })
565        .skip(eof())
566        .parse_stream(&mut tokens)
567        .map_err(|e| e.into_inner().error)?;
568
569    Ok(doc)
570}
571
572#[cfg(test)]
573mod test {
574    use super::parse_schema;
575    use crate::position::Pos;
576    use crate::schema::grammar::*;
577
578    fn ast(s: &str) -> Document {
579        parse_schema(s).unwrap()
580    }
581
582    #[test]
583    fn one_field() {
584        assert_eq!(
585            ast("schema { query: Query }"),
586            Document {
587                definitions: vec![Definition::SchemaDefinition(SchemaDefinition {
588                    position: Pos { line: 1, column: 1 },
589                    directives: vec![],
590                    query: Some("Query".into()),
591                    mutation: None,
592                    subscription: None
593                })],
594            }
595        );
596    }
597}