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 mut tokens = TokenStream::new(s);
258    let (doc, _) = many1(parser(definition))
259        .map(|d| Document { definitions: d })
260        .skip(eof())
261        .parse_stream(&mut tokens)
262        .into_result()
263        .map_err(|e| e.into_inner().error)?;
264
265    Ok(doc)
266}
267
268/// Parses a single ExecutableDefinition and returns an AST as well as the
269/// remainder of the input which is unparsed
270pub fn consume_definition<'a, S>(s: &'a str) -> Result<(Definition<'a, S>, &'a str), ParseError>
271where
272    S: Text<'a>,
273{
274    let tokens = TokenStream::new(s);
275    let (doc, tokens) = parser(definition).parse(tokens)?;
276
277    Ok((doc, &s[tokens.offset()..]))
278}
279
280#[cfg(test)]
281mod test {
282    use super::*;
283    use super::{consume_definition, parse_query};
284    use crate::parser::position::Pos;
285
286    fn ast<'a>(s: &'a str) -> Document<'a, String> {
287        parse_query::<String>(s).unwrap().to_owned()
288    }
289
290    #[test]
291    fn one_field() {
292        assert_eq!(
293            ast("{ a }"),
294            Document {
295                definitions: vec![Definition::Operation(OperationDefinition::SelectionSet(
296                    SelectionSet {
297                        span: (Pos { line: 1, column: 1 }, Pos { line: 1, column: 5 }),
298                        items: vec![Selection::Field(Field {
299                            position: Pos { line: 1, column: 3 },
300                            alias: None,
301                            name: "a".into(),
302                            arguments: Vec::new(),
303                            directives: Vec::new(),
304                            selection_set: SelectionSet {
305                                span: (Pos { line: 1, column: 3 }, Pos { line: 1, column: 3 }),
306                                items: Vec::new()
307                            },
308                        }),],
309                    }
310                ))],
311            }
312        );
313    }
314
315    #[test]
316    fn builtin_values() {
317        assert_eq!(
318            ast("{ a(t: true, f: false, n: null) }"),
319            Document {
320                definitions: vec![Definition::Operation(OperationDefinition::SelectionSet(
321                    SelectionSet {
322                        span: (
323                            Pos { line: 1, column: 1 },
324                            Pos {
325                                line: 1,
326                                column: 33
327                            }
328                        ),
329                        items: vec![Selection::Field(Field {
330                            position: Pos { line: 1, column: 3 },
331                            alias: None,
332                            name: "a".into(),
333                            arguments: vec![
334                                ("t".into(), Value::Boolean(true)),
335                                ("f".into(), Value::Boolean(false)),
336                                ("n".into(), Value::Null),
337                            ],
338                            directives: Vec::new(),
339                            selection_set: SelectionSet {
340                                span: (Pos { line: 1, column: 3 }, Pos { line: 1, column: 3 }),
341                                items: Vec::new()
342                            },
343                        }),],
344                    }
345                ))],
346            }
347        );
348    }
349
350    #[test]
351    fn one_field_roundtrip() {
352        assert_eq!(ast("{ a }").to_string(), "{\n  a\n}\n");
353    }
354
355    #[test]
356    #[should_panic(expected = "number too large")]
357    fn large_integer() {
358        ast("{ a(x: 10000000000000000000000000000 }");
359    }
360
361    #[test]
362    fn consume_single_query() {
363        let (query, remainder) = consume_definition::<String>("query { a } query { b }").unwrap();
364        assert!(matches!(query, Definition::Operation(_)));
365        assert_eq!(remainder, "query { b }");
366    }
367
368    #[test]
369    fn consume_full_text() {
370        let (query, remainder) = consume_definition::<String>("query { a }").unwrap();
371        assert!(matches!(query, Definition::Operation(_)));
372        assert_eq!(remainder, "");
373    }
374
375    #[test]
376    fn consume_single_query_preceding_non_graphql() {
377        let (query, remainder) =
378            consume_definition::<String>("query { a } where a > 1 => 10.0").unwrap();
379        assert!(matches!(query, Definition::Operation(_)));
380        assert_eq!(remainder, "where a > 1 => 10.0");
381    }
382
383    #[test]
384    fn consume_fails_without_operation() {
385        let err = consume_definition::<String>("where a > 1 => 10.0")
386            .expect_err("Expected parse to fail with an error");
387        let err = format!("{}", err);
388        assert_eq!(err, "query parse error: Parse error at 1:1\nUnexpected `where[Name]`\nExpected {, query, mutation, subscription or fragment\n");
389    }
390
391    #[test]
392    fn recursion_too_deep() {
393        let query = format!(
394            "{}(b: {}{}){}",
395            "{ a".repeat(30),
396            "[".repeat(25),
397            "]".repeat(25),
398            "}".repeat(30)
399        );
400        let result = parse_query::<&str>(&query);
401        let err = format!("{}", result.unwrap_err());
402        assert_eq!(
403            &err,
404            "query parse error: Parse error at 1:114\nExpected ]\nRecursion limit exceeded\n"
405        )
406    }
407}