Skip to main content

graphql_tools/parser/query/
grammar.rs

1use combine::{eof, many1, optional, position, StdParseResult};
2use combine::{parser, Parser};
3
4use super::ast::*;
5use super::error::ParseError;
6use crate::parser::common::Directive;
7use crate::parser::common::{arguments, default_value, directives, parse_type, string};
8use crate::parser::helpers::{ident, name, punct};
9use crate::parser::tokenizer::TokenStream;
10
11pub fn field<'a, S>(input: &mut TokenStream<'a>) -> StdParseResult<Field<'a, S>, TokenStream<'a>>
12where
13    S: Text<'a>,
14{
15    (
16        position(),
17        name::<'a, S>(),
18        optional(punct(":").with(name::<'a, S>())),
19        parser(arguments),
20        parser(directives),
21        optional(parser(selection_set)),
22    )
23        .map(
24            |(position, name_or_alias, opt_name, arguments, directives, sel)| {
25                let (name, alias) = match opt_name {
26                    Some(name) => (name, Some(name_or_alias)),
27                    None => (name_or_alias, None),
28                };
29                Field {
30                    position,
31                    name,
32                    alias,
33                    arguments,
34                    directives,
35                    selection_set: sel.unwrap_or_else(|| SelectionSet {
36                        span: (position, position),
37                        items: Vec::new(),
38                    }),
39                }
40            },
41        )
42        .parse_stream(input)
43        .into_result()
44}
45
46pub fn selection<'a, S>(
47    input: &mut TokenStream<'a>,
48) -> StdParseResult<Selection<'a, S>, TokenStream<'a>>
49where
50    S: Text<'a>,
51{
52    parser(field)
53        .map(Selection::Field)
54        .or(punct("...").with(
55            (
56                position(),
57                optional(ident("on").with(name::<'a, S>()).map(TypeCondition::On)),
58                parser(directives),
59                parser(selection_set),
60            )
61                .map(
62                    |(position, type_condition, directives, selection_set)| InlineFragment {
63                        position,
64                        type_condition,
65                        selection_set,
66                        directives,
67                    },
68                )
69                .map(Selection::InlineFragment)
70                .or((position(), name::<'a, S>(), parser(directives))
71                    .map(|(position, fragment_name, directives)| FragmentSpread {
72                        position,
73                        fragment_name,
74                        directives,
75                    })
76                    .map(Selection::FragmentSpread)),
77        ))
78        .parse_stream(input)
79        .into_result()
80}
81
82pub fn selection_set<'a, S>(
83    input: &mut TokenStream<'a>,
84) -> StdParseResult<SelectionSet<'a, S>, TokenStream<'a>>
85where
86    S: Text<'a>,
87{
88    (
89        position().skip(punct("{")),
90        many1(parser(selection)),
91        position().skip(punct("}")),
92    )
93        .map(|(start, items, end)| SelectionSet {
94            span: (start, end),
95            items,
96        })
97        .parse_stream(input)
98        .into_result()
99}
100
101pub fn query<'a, T: Text<'a>>(
102    input: &mut TokenStream<'a>,
103) -> StdParseResult<Query<'a, T>, TokenStream<'a>> {
104    position()
105        .skip(ident("query"))
106        .and(parser(operation_common))
107        .map(
108            |(position, (name, variable_definitions, directives, selection_set))| Query {
109                position,
110                name,
111                selection_set,
112                variable_definitions,
113                directives,
114            },
115        )
116        .parse_stream(input)
117        .into_result()
118}
119
120/// A set of attributes common to a Query and a Mutation
121#[allow(type_alias_bounds)]
122type OperationCommon<'a, T: Text<'a>> = (
123    Option<T::Value>,
124    Vec<VariableDefinition<'a, T>>,
125    Vec<Directive<'a, T>>,
126    SelectionSet<'a, T>,
127);
128
129pub fn operation_common<'a, T: Text<'a>>(
130    input: &mut TokenStream<'a>,
131) -> StdParseResult<OperationCommon<'a, T>, TokenStream<'a>> {
132    optional(name::<'a, T>())
133        .and(
134            optional(
135                punct("(")
136                    .with(many1(
137                        (
138                            position(),
139                            // The description has no meaning for execution, so it is not kept.
140                            optional(parser(string))
141                                .with(punct("$"))
142                                .with(name::<'a, T>())
143                                .skip(punct(":")),
144                            parser(parse_type),
145                            optional(punct("=").with(parser(default_value))),
146                        )
147                            .map(
148                                |(position, name, var_type, default_value)| VariableDefinition {
149                                    position,
150                                    name,
151                                    var_type,
152                                    default_value,
153                                },
154                            ),
155                    ))
156                    .skip(punct(")")),
157            )
158            .map(|vars| vars.unwrap_or_default()),
159        )
160        .and(parser(directives))
161        .and(parser(selection_set))
162        .map(|(((a, b), c), d)| (a, b, c, d))
163        .parse_stream(input)
164        .into_result()
165}
166
167pub fn mutation<'a, T: Text<'a>>(
168    input: &mut TokenStream<'a>,
169) -> StdParseResult<Mutation<'a, T>, TokenStream<'a>> {
170    position()
171        .skip(ident("mutation"))
172        .and(parser(operation_common))
173        .map(
174            |(position, (name, variable_definitions, directives, selection_set))| Mutation {
175                position,
176                name,
177                selection_set,
178                variable_definitions,
179                directives,
180            },
181        )
182        .parse_stream(input)
183        .into_result()
184}
185
186pub fn subscription<'a, T: Text<'a>>(
187    input: &mut TokenStream<'a>,
188) -> StdParseResult<Subscription<'a, T>, TokenStream<'a>> {
189    position()
190        .skip(ident("subscription"))
191        .and(parser(operation_common))
192        .map(
193            |(position, (name, variable_definitions, directives, selection_set))| Subscription {
194                position,
195                name,
196                selection_set,
197                variable_definitions,
198                directives,
199            },
200        )
201        .parse_stream(input)
202        .into_result()
203}
204
205pub fn operation_definition<'a, S>(
206    input: &mut TokenStream<'a>,
207) -> StdParseResult<OperationDefinition<'a, S>, TokenStream<'a>>
208where
209    S: Text<'a>,
210{
211    parser(selection_set)
212        .map(OperationDefinition::SelectionSet)
213        .or(parser(query).map(OperationDefinition::Query))
214        .or(parser(mutation).map(OperationDefinition::Mutation))
215        .or(parser(subscription).map(OperationDefinition::Subscription))
216        .parse_stream(input)
217        .into_result()
218}
219
220pub fn fragment_definition<'a, T: Text<'a>>(
221    input: &mut TokenStream<'a>,
222) -> StdParseResult<FragmentDefinition<'a, T>, TokenStream<'a>> {
223    (
224        position().skip(ident("fragment")),
225        name::<'a, T>(),
226        ident("on").with(name::<'a, T>()).map(TypeCondition::On),
227        parser(directives),
228        parser(selection_set),
229    )
230        .map(
231            |(position, name, type_condition, directives, selection_set)| FragmentDefinition {
232                position,
233                name,
234                type_condition,
235                directives,
236                selection_set,
237            },
238        )
239        .parse_stream(input)
240        .into_result()
241}
242
243pub fn definition<'a, S>(
244    input: &mut TokenStream<'a>,
245) -> StdParseResult<Definition<'a, S>, TokenStream<'a>>
246where
247    S: Text<'a>,
248{
249    parser(operation_definition)
250        .map(Definition::Operation)
251        .or(parser(fragment_definition).map(Definition::Fragment))
252        .parse_stream(input)
253        .into_result()
254}
255
256/// Parses a piece of query language and returns an AST
257pub fn parse_query<'a, S>(s: &'a str) -> Result<Document<'a, S>, ParseError>
258where
259    S: Text<'a>,
260{
261    let tokens = TokenStream::new(s);
262    handle_token_stream(tokens)
263}
264
265pub fn parse_query_with_token_limit<'a, S>(
266    s: &'a str,
267    token_limit: usize,
268) -> Result<Document<'a, S>, ParseError>
269where
270    S: Text<'a>,
271{
272    let tokens = TokenStream::new_with_token_limit(s, token_limit);
273    handle_token_stream(tokens)
274}
275
276fn handle_token_stream<'a, S>(mut tokens: TokenStream<'a>) -> Result<Document<'a, S>, ParseError>
277where
278    S: Text<'a>,
279{
280    let (doc, _) = many1(parser(definition))
281        .map(|d| Document { definitions: d })
282        .skip(eof())
283        .parse_stream(&mut tokens)
284        .into_result()
285        .map_err(|e| e.into_inner().error)?;
286
287    Ok(doc)
288}
289
290/// Parses a single ExecutableDefinition and returns an AST as well as the
291/// remainder of the input which is unparsed
292pub fn consume_definition<'a, S>(s: &'a str) -> Result<(Definition<'a, S>, &'a str), ParseError>
293where
294    S: Text<'a>,
295{
296    let tokens = TokenStream::new(s);
297    let (doc, tokens) = parser(definition).parse(tokens)?;
298
299    Ok((doc, &s[tokens.offset()..]))
300}
301
302#[cfg(test)]
303mod test {
304    use super::*;
305    use super::{consume_definition, parse_query};
306    use crate::parser::position::Pos;
307
308    fn ast<'a>(s: &'a str) -> Document<'a, String> {
309        parse_query::<String>(s).unwrap().to_owned()
310    }
311
312    #[test]
313    fn one_field() {
314        assert_eq!(
315            ast("{ a }"),
316            Document {
317                definitions: vec![Definition::Operation(OperationDefinition::SelectionSet(
318                    SelectionSet {
319                        span: (Pos { line: 1, column: 1 }, Pos { line: 1, column: 5 }),
320                        items: vec![Selection::Field(Field {
321                            position: Pos { line: 1, column: 3 },
322                            alias: None,
323                            name: "a".into(),
324                            arguments: Vec::new(),
325                            directives: Vec::new(),
326                            selection_set: SelectionSet {
327                                span: (Pos { line: 1, column: 3 }, Pos { line: 1, column: 3 }),
328                                items: Vec::new()
329                            },
330                        }),],
331                    }
332                ))],
333            }
334        );
335    }
336
337    #[test]
338    fn builtin_values() {
339        assert_eq!(
340            ast("{ a(t: true, f: false, n: null) }"),
341            Document {
342                definitions: vec![Definition::Operation(OperationDefinition::SelectionSet(
343                    SelectionSet {
344                        span: (
345                            Pos { line: 1, column: 1 },
346                            Pos {
347                                line: 1,
348                                column: 33
349                            }
350                        ),
351                        items: vec![Selection::Field(Field {
352                            position: Pos { line: 1, column: 3 },
353                            alias: None,
354                            name: "a".into(),
355                            arguments: vec![
356                                ("t".into(), Value::Boolean(true)),
357                                ("f".into(), Value::Boolean(false)),
358                                ("n".into(), Value::Null),
359                            ],
360                            directives: Vec::new(),
361                            selection_set: SelectionSet {
362                                span: (Pos { line: 1, column: 3 }, Pos { line: 1, column: 3 }),
363                                items: Vec::new()
364                            },
365                        }),],
366                    }
367                ))],
368            }
369        );
370    }
371
372    #[test]
373    fn one_field_roundtrip() {
374        assert_eq!(ast("{ a }").to_string(), "{\n  a\n}\n");
375    }
376
377    #[test]
378    #[should_panic(expected = "PosOverflow")]
379    fn large_integer() {
380        ast("{ a(x: 10000000000000000000000000000 }");
381    }
382
383    #[test]
384    fn variable_definition_descriptions() {
385        assert_eq!(
386            ast(r#"query ("a" $a: Int, """b""" $b: String = "x") { f(a: $a, b: $b) }"#).to_string(),
387            ast(r#"query ($a: Int, $b: String = "x") { f(a: $a, b: $b) }"#).to_string(),
388        );
389    }
390
391    #[test]
392    fn consume_single_query() {
393        let (query, remainder) = consume_definition::<String>("query { a } query { b }").unwrap();
394        assert!(matches!(query, Definition::Operation(_)));
395        assert_eq!(remainder, "query { b }");
396    }
397
398    #[test]
399    fn consume_full_text() {
400        let (query, remainder) = consume_definition::<String>("query { a }").unwrap();
401        assert!(matches!(query, Definition::Operation(_)));
402        assert_eq!(remainder, "");
403    }
404
405    #[test]
406    fn consume_single_query_preceding_non_graphql() {
407        let (query, remainder) =
408            consume_definition::<String>("query { a } where a > 1 => 10.0").unwrap();
409        assert!(matches!(query, Definition::Operation(_)));
410        assert_eq!(remainder, "where a > 1 => 10.0");
411    }
412
413    #[test]
414    fn consume_fails_without_operation() {
415        let err = consume_definition::<String>("where a > 1 => 10.0")
416            .expect_err("Expected parse to fail with an error");
417        let err = format!("{}", err);
418        assert_eq!(err, "Parse error at 1:1\nUnexpected `where[Name]`\nExpected {, query, mutation, subscription or fragment\n");
419    }
420
421    #[test]
422    fn recursion_too_deep() {
423        let query = format!(
424            "{}(b: {}{}){}",
425            "{ a".repeat(30),
426            "[".repeat(25),
427            "]".repeat(25),
428            "}".repeat(30)
429        );
430        let result = parse_query::<&str>(&query);
431        let err = format!("{}", result.unwrap_err());
432        assert_eq!(
433            &err,
434            "Parse error at 1:114\nExpected ]\nRecursion limit exceeded\n"
435        )
436    }
437}