graphql_toolkit_parser/parse/
executable.rs

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