graphql_parser/schema/
grammar.rs

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