fuel_indexer_graphql_parser/query/
grammar.rs

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