async_graphql_parser/parse/
executable.rs

1use super::*;
2
3const MAX_RECURSION_DEPTH: usize = 64;
4
5macro_rules! recursion_depth {
6    ($remaining_depth:ident) => {{
7        if $remaining_depth == 0 {
8            return Err(Error::RecursionLimitExceeded);
9        }
10        $remaining_depth - 1
11    }};
12}
13
14/// Parse a GraphQL query document.
15///
16/// # Errors
17///
18/// Fails if the query is not a valid GraphQL document.
19pub fn parse_query<T: AsRef<str>>(input: T) -> Result<ExecutableDocument> {
20    let mut pc = PositionCalculator::new(input.as_ref());
21
22    let pairs = GraphQLParser::parse(Rule::executable_document, input.as_ref())?;
23    let items = parse_definition_items(exactly_one(pairs), &mut pc)?;
24
25    let mut operations = None;
26    let mut fragments: HashMap<_, Positioned<FragmentDefinition>> = HashMap::new();
27
28    for item in items {
29        match item {
30            DefinitionItem::Operation(item) => {
31                if let Some(name) = item.node.name {
32                    let operations = operations
33                        .get_or_insert_with(|| DocumentOperations::Multiple(HashMap::new()));
34                    let operations = match operations {
35                        DocumentOperations::Single(anonymous) => {
36                            return Err(Error::MultipleOperations {
37                                anonymous: anonymous.pos,
38                                operation: item.pos,
39                            })
40                        }
41                        DocumentOperations::Multiple(operations) => operations,
42                    };
43
44                    match operations.entry(name.node) {
45                        hash_map::Entry::Occupied(entry) => {
46                            let (name, first) = entry.remove_entry();
47                            return Err(Error::OperationDuplicated {
48                                operation: name,
49                                first: first.pos,
50                                second: item.pos,
51                            });
52                        }
53                        hash_map::Entry::Vacant(entry) => {
54                            entry.insert(Positioned::new(item.node.definition, item.pos));
55                        }
56                    }
57                } else {
58                    match operations {
59                        Some(operations) => {
60                            return Err(Error::MultipleOperations {
61                                anonymous: item.pos,
62                                operation: match operations {
63                                    DocumentOperations::Single(single) => single.pos,
64                                    DocumentOperations::Multiple(map) => {
65                                        map.values().next().unwrap().pos
66                                    }
67                                },
68                            });
69                        }
70                        None => {
71                            operations = Some(DocumentOperations::Single(Positioned::new(
72                                item.node.definition,
73                                item.pos,
74                            )));
75                        }
76                    }
77                }
78            }
79            DefinitionItem::Fragment(item) => match fragments.entry(item.node.name.node) {
80                hash_map::Entry::Occupied(entry) => {
81                    let (name, first) = entry.remove_entry();
82                    return Err(Error::FragmentDuplicated {
83                        fragment: name,
84                        first: first.pos,
85                        second: item.pos,
86                    });
87                }
88                hash_map::Entry::Vacant(entry) => {
89                    entry.insert(Positioned::new(item.node.definition, item.pos));
90                }
91            },
92        }
93    }
94
95    Ok(ExecutableDocument {
96        operations: operations.ok_or(Error::MissingOperation)?,
97        fragments,
98    })
99}
100
101fn parse_definition_items(
102    pair: Pair<Rule>,
103    pc: &mut PositionCalculator,
104) -> Result<Vec<DefinitionItem>> {
105    debug_assert_eq!(pair.as_rule(), Rule::executable_document);
106
107    Ok(pair
108        .into_inner()
109        .filter(|pair| pair.as_rule() != Rule::EOI)
110        .map(|pair| parse_definition_item(pair, pc))
111        .collect::<Result<_>>()?)
112}
113
114enum DefinitionItem {
115    Operation(Positioned<OperationDefinitionItem>),
116    Fragment(Positioned<FragmentDefinitionItem>),
117}
118
119fn parse_definition_item(pair: Pair<Rule>, pc: &mut PositionCalculator) -> Result<DefinitionItem> {
120    debug_assert_eq!(pair.as_rule(), Rule::executable_definition);
121
122    let pair = exactly_one(pair.into_inner());
123    Ok(match pair.as_rule() {
124        Rule::operation_definition => {
125            DefinitionItem::Operation(parse_operation_definition_item(pair, pc)?)
126        }
127        Rule::fragment_definition => {
128            DefinitionItem::Fragment(parse_fragment_definition_item(pair, pc)?)
129        }
130        _ => unreachable!(),
131    })
132}
133
134struct OperationDefinitionItem {
135    name: Option<Positioned<Name>>,
136    definition: OperationDefinition,
137}
138
139fn parse_operation_definition_item(
140    pair: Pair<Rule>,
141    pc: &mut PositionCalculator,
142) -> Result<Positioned<OperationDefinitionItem>> {
143    debug_assert_eq!(pair.as_rule(), Rule::operation_definition);
144
145    let pos = pc.step(&pair);
146    let pair = exactly_one(pair.into_inner());
147    Ok(Positioned::new(
148        match pair.as_rule() {
149            Rule::named_operation_definition => parse_named_operation_definition(pair, pc)?,
150            Rule::selection_set => OperationDefinitionItem {
151                name: None,
152                definition: OperationDefinition {
153                    ty: OperationType::Query,
154                    variable_definitions: Vec::new(),
155                    directives: Vec::new(),
156                    selection_set: parse_selection_set(pair, pc, MAX_RECURSION_DEPTH)?,
157                },
158            },
159            _ => unreachable!(),
160        },
161        pos,
162    ))
163}
164
165fn parse_named_operation_definition(
166    pair: Pair<Rule>,
167    pc: &mut PositionCalculator,
168) -> Result<OperationDefinitionItem> {
169    debug_assert_eq!(pair.as_rule(), Rule::named_operation_definition);
170
171    let mut pairs = pair.into_inner();
172
173    let ty = parse_operation_type(pairs.next().unwrap(), pc)?;
174    let name = parse_if_rule(&mut pairs, Rule::name, |pair| parse_name(pair, pc))?;
175    let variable_definitions = parse_if_rule(&mut pairs, Rule::variable_definitions, |pair| {
176        parse_variable_definitions(pair, pc)
177    })?;
178    let directives = parse_opt_directives(&mut pairs, pc)?;
179    let selection_set = parse_selection_set(pairs.next().unwrap(), pc, MAX_RECURSION_DEPTH)?;
180
181    debug_assert_eq!(pairs.next(), None);
182
183    Ok(OperationDefinitionItem {
184        name,
185        definition: OperationDefinition {
186            ty: ty.node,
187            variable_definitions: variable_definitions.unwrap_or_default(),
188            directives,
189            selection_set,
190        },
191    })
192}
193
194fn parse_variable_definitions(
195    pair: Pair<Rule>,
196    pc: &mut PositionCalculator,
197) -> Result<Vec<Positioned<VariableDefinition>>> {
198    debug_assert_eq!(pair.as_rule(), Rule::variable_definitions);
199
200    pair.into_inner()
201        .map(|pair| parse_variable_definition(pair, pc))
202        .collect()
203}
204
205fn parse_variable_definition(
206    pair: Pair<Rule>,
207    pc: &mut PositionCalculator,
208) -> Result<Positioned<VariableDefinition>> {
209    debug_assert_eq!(pair.as_rule(), Rule::variable_definition);
210
211    let pos = pc.step(&pair);
212    let mut pairs = pair.into_inner();
213
214    let variable = parse_variable(pairs.next().unwrap(), pc)?;
215    let var_type = parse_type(pairs.next().unwrap(), pc)?;
216
217    let directives = parse_opt_directives(&mut pairs, pc)?;
218    let default_value = parse_if_rule(&mut pairs, Rule::default_value, |pair| {
219        parse_default_value(pair, pc)
220    })?;
221
222    debug_assert_eq!(pairs.next(), None);
223
224    Ok(Positioned::new(
225        VariableDefinition {
226            name: variable,
227            var_type,
228            directives,
229            default_value,
230        },
231        pos,
232    ))
233}
234
235fn parse_selection_set(
236    pair: Pair<Rule>,
237    pc: &mut PositionCalculator,
238    remaining_depth: usize,
239) -> Result<Positioned<SelectionSet>> {
240    debug_assert_eq!(pair.as_rule(), Rule::selection_set);
241
242    let pos = pc.step(&pair);
243
244    Ok(Positioned::new(
245        SelectionSet {
246            items: pair
247                .into_inner()
248                .map(|pair| parse_selection(pair, pc, remaining_depth))
249                .collect::<Result<_>>()?,
250        },
251        pos,
252    ))
253}
254
255fn parse_selection(
256    pair: Pair<Rule>,
257    pc: &mut PositionCalculator,
258    remaining_depth: usize,
259) -> Result<Positioned<Selection>> {
260    debug_assert_eq!(pair.as_rule(), Rule::selection);
261
262    let pos = pc.step(&pair);
263    let pair = exactly_one(pair.into_inner());
264
265    Ok(Positioned::new(
266        match pair.as_rule() {
267            Rule::field => Selection::Field(parse_field(pair, pc, remaining_depth)?),
268            Rule::fragment_spread => Selection::FragmentSpread(parse_fragment_spread(pair, pc)?),
269            Rule::inline_fragment => {
270                Selection::InlineFragment(parse_inline_fragment(pair, pc, remaining_depth)?)
271            }
272            _ => unreachable!(),
273        },
274        pos,
275    ))
276}
277
278fn parse_field(
279    pair: Pair<Rule>,
280    pc: &mut PositionCalculator,
281    remaining_depth: usize,
282) -> Result<Positioned<Field>> {
283    debug_assert_eq!(pair.as_rule(), Rule::field);
284
285    let pos = pc.step(&pair);
286    let mut pairs = pair.into_inner();
287
288    let alias = parse_if_rule(&mut pairs, Rule::alias, |pair| parse_alias(pair, pc))?;
289    let name = parse_name(pairs.next().unwrap(), pc)?;
290    let arguments = parse_if_rule(&mut pairs, Rule::arguments, |pair| {
291        parse_arguments(pair, pc)
292    })?;
293    let directives = parse_opt_directives(&mut pairs, pc)?;
294    let selection_set = parse_if_rule(&mut pairs, Rule::selection_set, |pair| {
295        parse_selection_set(pair, pc, recursion_depth!(remaining_depth))
296    })?;
297
298    debug_assert_eq!(pairs.next(), None);
299
300    Ok(Positioned::new(
301        Field {
302            alias,
303            name,
304            arguments: arguments.unwrap_or_default(),
305            directives,
306            selection_set: selection_set.unwrap_or_default(),
307        },
308        pos,
309    ))
310}
311
312fn parse_alias(pair: Pair<Rule>, pc: &mut PositionCalculator) -> Result<Positioned<Name>> {
313    debug_assert_eq!(pair.as_rule(), Rule::alias);
314    parse_name(exactly_one(pair.into_inner()), pc)
315}
316
317fn parse_fragment_spread(
318    pair: Pair<Rule>,
319    pc: &mut PositionCalculator,
320) -> Result<Positioned<FragmentSpread>> {
321    debug_assert_eq!(pair.as_rule(), Rule::fragment_spread);
322
323    let pos = pc.step(&pair);
324    let mut pairs = pair.into_inner();
325
326    let fragment_name = parse_name(pairs.next().unwrap(), pc)?;
327    let directives = parse_opt_directives(&mut pairs, pc)?;
328
329    debug_assert_eq!(pairs.next(), None);
330
331    Ok(Positioned::new(
332        FragmentSpread {
333            fragment_name,
334            directives,
335        },
336        pos,
337    ))
338}
339
340fn parse_inline_fragment(
341    pair: Pair<Rule>,
342    pc: &mut PositionCalculator,
343    remaining_depth: usize,
344) -> Result<Positioned<InlineFragment>> {
345    debug_assert_eq!(pair.as_rule(), Rule::inline_fragment);
346
347    let pos = pc.step(&pair);
348    let mut pairs = pair.into_inner();
349
350    let type_condition = parse_if_rule(&mut pairs, Rule::type_condition, |pair| {
351        parse_type_condition(pair, pc)
352    })?;
353    let directives = parse_opt_directives(&mut pairs, pc)?;
354    let selection_set =
355        parse_selection_set(pairs.next().unwrap(), pc, recursion_depth!(remaining_depth))?;
356
357    debug_assert_eq!(pairs.next(), None);
358
359    Ok(Positioned::new(
360        InlineFragment {
361            type_condition,
362            directives,
363            selection_set,
364        },
365        pos,
366    ))
367}
368
369struct FragmentDefinitionItem {
370    name: Positioned<Name>,
371    definition: FragmentDefinition,
372}
373
374fn parse_fragment_definition_item(
375    pair: Pair<Rule>,
376    pc: &mut PositionCalculator,
377) -> Result<Positioned<FragmentDefinitionItem>> {
378    debug_assert_eq!(pair.as_rule(), Rule::fragment_definition);
379
380    let pos = pc.step(&pair);
381    let mut pairs = pair.into_inner();
382
383    let name = parse_name(pairs.next().unwrap(), pc)?;
384    let type_condition = parse_type_condition(pairs.next().unwrap(), pc)?;
385    let directives = parse_opt_directives(&mut pairs, pc)?;
386    let selection_set = parse_selection_set(pairs.next().unwrap(), pc, MAX_RECURSION_DEPTH)?;
387
388    debug_assert_eq!(pairs.next(), None);
389
390    Ok(Positioned::new(
391        FragmentDefinitionItem {
392            name,
393            definition: FragmentDefinition {
394                type_condition,
395                directives,
396                selection_set,
397            },
398        },
399        pos,
400    ))
401}
402
403fn parse_type_condition(
404    pair: Pair<Rule>,
405    pc: &mut PositionCalculator,
406) -> Result<Positioned<TypeCondition>> {
407    debug_assert_eq!(pair.as_rule(), Rule::type_condition);
408
409    let pos = pc.step(&pair);
410    Ok(Positioned::new(
411        TypeCondition {
412            on: parse_name(exactly_one(pair.into_inner()), pc)?,
413        },
414        pos,
415    ))
416}
417
418#[cfg(test)]
419mod tests {
420    use std::fs;
421
422    use super::*;
423
424    #[test]
425    fn test_parser() {
426        for entry in fs::read_dir("tests/executables").unwrap() {
427            let entry = entry.unwrap();
428            eprintln!("Parsing file {}", entry.path().display());
429
430            GraphQLParser::parse(
431                Rule::executable_document,
432                &fs::read_to_string(entry.path()).unwrap(),
433            )
434            .unwrap();
435        }
436    }
437
438    #[test]
439    fn test_parser_ast() {
440        for entry in fs::read_dir("tests/executables").unwrap() {
441            let entry = entry.unwrap();
442            eprintln!("Parsing and transforming file {}", entry.path().display());
443            parse_query(fs::read_to_string(entry.path()).unwrap()).unwrap();
444        }
445    }
446
447    #[test]
448    fn test_parse_overflowing_int() {
449        let query_ok = format!("mutation {{ add(big: {}) }} ", i32::MAX);
450        let query_overflow = format!("mutation {{ add(big: {}0000) }} ", i32::MAX);
451        assert!(parse_query(query_ok).is_ok());
452        assert!(parse_query(query_overflow).is_ok());
453    }
454}