graphql_parser/query/
grammar.rs

1use combine::{eof, many1, optional, position, StdParseResult};
2use combine::{parser, 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>(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>>
104where
105    T: Text<'a>,
106{
107    position()
108        .skip(ident("query"))
109        .and(parser(operation_common))
110        .map(
111            |(position, (name, variable_definitions, directives, selection_set))| Query {
112                position,
113                name,
114                selection_set,
115                variable_definitions,
116                directives,
117            },
118        )
119        .parse_stream(input)
120        .into_result()
121}
122
123/// A set of attributes common to a Query and a Mutation
124#[allow(type_alias_bounds)]
125type OperationCommon<'a, T: Text<'a>> = (
126    Option<T::Value>,
127    Vec<VariableDefinition<'a, T>>,
128    Vec<Directive<'a, T>>,
129    SelectionSet<'a, T>,
130);
131
132pub fn operation_common<'a, T: Text<'a>>(
133    input: &mut TokenStream<'a>,
134) -> StdParseResult<OperationCommon<'a, T>, TokenStream<'a>>
135where
136    T: Text<'a>,
137{
138    optional(name::<'a, T>())
139        .and(
140            optional(
141                punct("(")
142                    .with(many1(
143                        (
144                            position(),
145                            punct("$").with(name::<'a, T>()).skip(punct(":")),
146                            parser(parse_type),
147                            optional(punct("=").with(parser(default_value))),
148                        )
149                            .map(
150                                |(position, name, var_type, default_value)| VariableDefinition {
151                                    position,
152                                    name,
153                                    var_type,
154                                    default_value,
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        .into_result()
167}
168
169pub fn mutation<'a, T: Text<'a>>(
170    input: &mut TokenStream<'a>,
171) -> StdParseResult<Mutation<'a, T>, TokenStream<'a>>
172where
173    T: Text<'a>,
174{
175    position()
176        .skip(ident("mutation"))
177        .and(parser(operation_common))
178        .map(
179            |(position, (name, variable_definitions, directives, selection_set))| Mutation {
180                position,
181                name,
182                selection_set,
183                variable_definitions,
184                directives,
185            },
186        )
187        .parse_stream(input)
188        .into_result()
189}
190
191pub fn subscription<'a, T: Text<'a>>(
192    input: &mut TokenStream<'a>,
193) -> StdParseResult<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))| Subscription {
202                position,
203                name,
204                selection_set,
205                variable_definitions,
206                directives,
207            },
208        )
209        .parse_stream(input)
210        .into_result()
211}
212
213pub fn operation_definition<'a, S>(
214    input: &mut TokenStream<'a>,
215) -> StdParseResult<OperationDefinition<'a, S>, TokenStream<'a>>
216where
217    S: Text<'a>,
218{
219    parser(selection_set)
220        .map(OperationDefinition::SelectionSet)
221        .or(parser(query).map(OperationDefinition::Query))
222        .or(parser(mutation).map(OperationDefinition::Mutation))
223        .or(parser(subscription).map(OperationDefinition::Subscription))
224        .parse_stream(input)
225        .into_result()
226}
227
228pub fn fragment_definition<'a, T: Text<'a>>(
229    input: &mut TokenStream<'a>,
230) -> StdParseResult<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)| FragmentDefinition {
243                position,
244                name,
245                type_condition,
246                directives,
247                selection_set,
248            },
249        )
250        .parse_stream(input)
251        .into_result()
252}
253
254pub fn definition<'a, S>(
255    input: &mut TokenStream<'a>,
256) -> StdParseResult<Definition<'a, S>, TokenStream<'a>>
257where
258    S: Text<'a>,
259{
260    parser(operation_definition)
261        .map(Definition::Operation)
262        .or(parser(fragment_definition).map(Definition::Fragment))
263        .parse_stream(input)
264        .into_result()
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        .into_result()
278        .map_err(|e| e.into_inner().error)?;
279
280    Ok(doc)
281}
282
283/// Parses a single ExecutableDefinition and returns an AST as well as the
284/// remainder of the input which is unparsed
285pub fn consume_definition<'a, S>(s: &'a str) -> Result<(Definition<'a, S>, &'a str), ParseError>
286where
287    S: Text<'a>,
288{
289    let tokens = TokenStream::new(s);
290    let (doc, tokens) = parser(definition).parse(tokens)?;
291
292    Ok((doc, &s[tokens.offset()..]))
293}
294
295#[cfg(test)]
296mod test {
297    use super::{consume_definition, parse_query};
298    use crate::position::Pos;
299    use crate::query::grammar::*;
300
301    fn ast(s: &str) -> Document<String> {
302        parse_query::<String>(s).unwrap().to_owned()
303    }
304
305    #[test]
306    fn one_field() {
307        assert_eq!(
308            ast("{ a }"),
309            Document {
310                definitions: vec![Definition::Operation(OperationDefinition::SelectionSet(
311                    SelectionSet {
312                        span: (Pos { line: 1, column: 1 }, Pos { line: 1, column: 5 }),
313                        items: vec![Selection::Field(Field {
314                            position: Pos { line: 1, column: 3 },
315                            alias: None,
316                            name: "a".into(),
317                            arguments: Vec::new(),
318                            directives: Vec::new(),
319                            selection_set: SelectionSet {
320                                span: (Pos { line: 1, column: 3 }, Pos { line: 1, column: 3 }),
321                                items: Vec::new()
322                            },
323                        }),],
324                    }
325                ))],
326            }
327        );
328    }
329
330    #[test]
331    fn builtin_values() {
332        assert_eq!(
333            ast("{ a(t: true, f: false, n: null) }"),
334            Document {
335                definitions: vec![Definition::Operation(OperationDefinition::SelectionSet(
336                    SelectionSet {
337                        span: (
338                            Pos { line: 1, column: 1 },
339                            Pos {
340                                line: 1,
341                                column: 33
342                            }
343                        ),
344                        items: vec![Selection::Field(Field {
345                            position: Pos { line: 1, column: 3 },
346                            alias: None,
347                            name: "a".into(),
348                            arguments: vec![
349                                ("t".into(), Value::Boolean(true)),
350                                ("f".into(), Value::Boolean(false)),
351                                ("n".into(), Value::Null),
352                            ],
353                            directives: Vec::new(),
354                            selection_set: SelectionSet {
355                                span: (Pos { line: 1, column: 3 }, Pos { line: 1, column: 3 }),
356                                items: Vec::new()
357                            },
358                        }),],
359                    }
360                ))],
361            }
362        );
363    }
364
365    #[test]
366    fn one_field_roundtrip() {
367        assert_eq!(ast("{ a }").to_string(), "{\n  a\n}\n");
368    }
369
370    #[test]
371    #[should_panic(expected = "number too large")]
372    fn large_integer() {
373        ast("{ a(x: 10000000000000000000000000000 }");
374    }
375
376    #[test]
377    fn consume_single_query() {
378        let (query, remainder) = consume_definition::<String>("query { a } query { b }").unwrap();
379        assert!(matches!(query, Definition::Operation(_)));
380        assert_eq!(remainder, "query { b }");
381    }
382
383    #[test]
384    fn consume_full_text() {
385        let (query, remainder) = consume_definition::<String>("query { a }").unwrap();
386        assert!(matches!(query, Definition::Operation(_)));
387        assert_eq!(remainder, "");
388    }
389
390    #[test]
391    fn consume_single_query_preceding_non_graphql() {
392        let (query, remainder) =
393            consume_definition::<String>("query { a } where a > 1 => 10.0").unwrap();
394        assert!(matches!(query, Definition::Operation(_)));
395        assert_eq!(remainder, "where a > 1 => 10.0");
396    }
397
398    #[test]
399    fn consume_fails_without_operation() {
400        let err = consume_definition::<String>("where a > 1 => 10.0")
401            .expect_err("Expected parse to fail with an error");
402        let err = format!("{}", err);
403        assert_eq!(err, "query parse error: Parse error at 1:1\nUnexpected `where[Name]`\nExpected {, query, mutation, subscription or fragment\n");
404    }
405
406    #[test]
407    fn recursion_too_deep() {
408        let query = format!(
409            "{}(b: {}{}){}",
410            "{ a".repeat(30),
411            "[".repeat(25),
412            "]".repeat(25),
413            "}".repeat(30)
414        );
415        let result = parse_query::<&str>(&query);
416        let err = format!("{}", result.unwrap_err());
417        assert_eq!(
418            &err,
419            "query parse error: Parse error at 1:114\nExpected ]\nRecursion limit exceeded\n"
420        )
421    }
422}