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
33pub 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}