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};
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                            punct("$").with(name::<'a, T>()).skip(punct(":")),
140                            parser(parse_type),
141                            optional(punct("=").with(parser(default_value))),
142                        )
143                            .map(
144                                |(position, name, var_type, default_value)| VariableDefinition {
145                                    position,
146                                    name,
147                                    var_type,
148                                    default_value,
149                                },
150                            ),
151                    ))
152                    .skip(punct(")")),
153            )
154            .map(|vars| vars.unwrap_or_default()),
155        )
156        .and(parser(directives))
157        .and(parser(selection_set))
158        .map(|(((a, b), c), d)| (a, b, c, d))
159        .parse_stream(input)
160        .into_result()
161}
162
163pub fn mutation<'a, T: Text<'a>>(
164    input: &mut TokenStream<'a>,
165) -> StdParseResult<Mutation<'a, T>, TokenStream<'a>> {
166    position()
167        .skip(ident("mutation"))
168        .and(parser(operation_common))
169        .map(
170            |(position, (name, variable_definitions, directives, selection_set))| Mutation {
171                position,
172                name,
173                selection_set,
174                variable_definitions,
175                directives,
176            },
177        )
178        .parse_stream(input)
179        .into_result()
180}
181
182pub fn subscription<'a, T: Text<'a>>(
183    input: &mut TokenStream<'a>,
184) -> StdParseResult<Subscription<'a, T>, TokenStream<'a>> {
185    position()
186        .skip(ident("subscription"))
187        .and(parser(operation_common))
188        .map(
189            |(position, (name, variable_definitions, directives, selection_set))| Subscription {
190                position,
191                name,
192                selection_set,
193                variable_definitions,
194                directives,
195            },
196        )
197        .parse_stream(input)
198        .into_result()
199}
200
201pub fn operation_definition<'a, S>(
202    input: &mut TokenStream<'a>,
203) -> StdParseResult<OperationDefinition<'a, S>, TokenStream<'a>>
204where
205    S: Text<'a>,
206{
207    parser(selection_set)
208        .map(OperationDefinition::SelectionSet)
209        .or(parser(query).map(OperationDefinition::Query))
210        .or(parser(mutation).map(OperationDefinition::Mutation))
211        .or(parser(subscription).map(OperationDefinition::Subscription))
212        .parse_stream(input)
213        .into_result()
214}
215
216pub fn fragment_definition<'a, T: Text<'a>>(
217    input: &mut TokenStream<'a>,
218) -> StdParseResult<FragmentDefinition<'a, T>, TokenStream<'a>> {
219    (
220        position().skip(ident("fragment")),
221        name::<'a, T>(),
222        ident("on").with(name::<'a, T>()).map(TypeCondition::On),
223        parser(directives),
224        parser(selection_set),
225    )
226        .map(
227            |(position, name, type_condition, directives, selection_set)| FragmentDefinition {
228                position,
229                name,
230                type_condition,
231                directives,
232                selection_set,
233            },
234        )
235        .parse_stream(input)
236        .into_result()
237}
238
239pub fn definition<'a, S>(
240    input: &mut TokenStream<'a>,
241) -> StdParseResult<Definition<'a, S>, TokenStream<'a>>
242where
243    S: Text<'a>,
244{
245    parser(operation_definition)
246        .map(Definition::Operation)
247        .or(parser(fragment_definition).map(Definition::Fragment))
248        .parse_stream(input)
249        .into_result()
250}
251
252/// Parses a piece of query language and returns an AST
253pub fn parse_query<'a, S>(s: &'a str) -> Result<Document<'a, S>, ParseError>
254where
255    S: Text<'a>,
256{
257    let tokens = TokenStream::new(s);
258    handle_token_stream(tokens)
259}
260
261pub fn parse_query_with_token_limit<'a, S>(
262    s: &'a str,
263    token_limit: usize,
264) -> Result<Document<'a, S>, ParseError>
265where
266    S: Text<'a>,
267{
268    let tokens = TokenStream::new_with_token_limit(s, token_limit);
269    handle_token_stream(tokens)
270}
271
272fn handle_token_stream<'a, S>(mut tokens: TokenStream<'a>) -> Result<Document<'a, S>, ParseError>
273where
274    S: Text<'a>,
275{
276    let (doc, _) = many1(parser(definition))
277        .map(|d| Document { definitions: d })
278        .skip(eof())
279        .parse_stream(&mut tokens)
280        .into_result()
281        .map_err(|e| e.into_inner().error)?;
282
283    Ok(doc)
284}
285
286/// Parses a single ExecutableDefinition and returns an AST as well as the
287/// remainder of the input which is unparsed
288pub fn consume_definition<'a, S>(s: &'a str) -> Result<(Definition<'a, S>, &'a str), ParseError>
289where
290    S: Text<'a>,
291{
292    let tokens = TokenStream::new(s);
293    let (doc, tokens) = parser(definition).parse(tokens)?;
294
295    Ok((doc, &s[tokens.offset()..]))
296}
297
298#[cfg(test)]
299mod test {
300    use super::*;
301    use super::{consume_definition, parse_query};
302    use crate::parser::position::Pos;
303
304    fn ast<'a>(s: &'a str) -> Document<'a, String> {
305        parse_query::<String>(s).unwrap().to_owned()
306    }
307
308    #[test]
309    fn one_field() {
310        assert_eq!(
311            ast("{ a }"),
312            Document {
313                definitions: vec![Definition::Operation(OperationDefinition::SelectionSet(
314                    SelectionSet {
315                        span: (Pos { line: 1, column: 1 }, Pos { line: 1, column: 5 }),
316                        items: vec![Selection::Field(Field {
317                            position: Pos { line: 1, column: 3 },
318                            alias: None,
319                            name: "a".into(),
320                            arguments: Vec::new(),
321                            directives: Vec::new(),
322                            selection_set: SelectionSet {
323                                span: (Pos { line: 1, column: 3 }, Pos { line: 1, column: 3 }),
324                                items: Vec::new()
325                            },
326                        }),],
327                    }
328                ))],
329            }
330        );
331    }
332
333    #[test]
334    fn builtin_values() {
335        assert_eq!(
336            ast("{ a(t: true, f: false, n: null) }"),
337            Document {
338                definitions: vec![Definition::Operation(OperationDefinition::SelectionSet(
339                    SelectionSet {
340                        span: (
341                            Pos { line: 1, column: 1 },
342                            Pos {
343                                line: 1,
344                                column: 33
345                            }
346                        ),
347                        items: vec![Selection::Field(Field {
348                            position: Pos { line: 1, column: 3 },
349                            alias: None,
350                            name: "a".into(),
351                            arguments: vec![
352                                ("t".into(), Value::Boolean(true)),
353                                ("f".into(), Value::Boolean(false)),
354                                ("n".into(), Value::Null),
355                            ],
356                            directives: Vec::new(),
357                            selection_set: SelectionSet {
358                                span: (Pos { line: 1, column: 3 }, Pos { line: 1, column: 3 }),
359                                items: Vec::new()
360                            },
361                        }),],
362                    }
363                ))],
364            }
365        );
366    }
367
368    #[test]
369    fn one_field_roundtrip() {
370        assert_eq!(ast("{ a }").to_string(), "{\n  a\n}\n");
371    }
372
373    #[test]
374    #[should_panic(expected = "PosOverflow")]
375    fn large_integer() {
376        ast("{ a(x: 10000000000000000000000000000 }");
377    }
378
379    #[test]
380    fn consume_single_query() {
381        let (query, remainder) = consume_definition::<String>("query { a } query { b }").unwrap();
382        assert!(matches!(query, Definition::Operation(_)));
383        assert_eq!(remainder, "query { b }");
384    }
385
386    #[test]
387    fn consume_full_text() {
388        let (query, remainder) = consume_definition::<String>("query { a }").unwrap();
389        assert!(matches!(query, Definition::Operation(_)));
390        assert_eq!(remainder, "");
391    }
392
393    #[test]
394    fn consume_single_query_preceding_non_graphql() {
395        let (query, remainder) =
396            consume_definition::<String>("query { a } where a > 1 => 10.0").unwrap();
397        assert!(matches!(query, Definition::Operation(_)));
398        assert_eq!(remainder, "where a > 1 => 10.0");
399    }
400
401    #[test]
402    fn consume_fails_without_operation() {
403        let err = consume_definition::<String>("where a > 1 => 10.0")
404            .expect_err("Expected parse to fail with an error");
405        let err = format!("{}", err);
406        assert_eq!(err, "Parse error at 1:1\nUnexpected `where[Name]`\nExpected {, query, mutation, subscription or fragment\n");
407    }
408
409    #[test]
410    fn recursion_too_deep() {
411        let query = format!(
412            "{}(b: {}{}){}",
413            "{ a".repeat(30),
414            "[".repeat(25),
415            "]".repeat(25),
416            "}".repeat(30)
417        );
418        let result = parse_query::<&str>(&query);
419        let err = format!("{}", result.unwrap_err());
420        assert_eq!(
421            &err,
422            "Parse error at 1:114\nExpected ]\nRecursion limit exceeded\n"
423        )
424    }
425}