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