async_graphql_parser/parse/
service.rs

1use super::*;
2
3/// Parse a GraphQL schema document.
4///
5/// # Errors
6///
7/// Fails if the schema is not a valid GraphQL document.
8pub fn parse_schema<T: AsRef<str>>(input: T) -> Result<ServiceDocument> {
9    let mut pc = PositionCalculator::new(input.as_ref());
10    Ok(parse_service_document(
11        exactly_one(GraphQLParser::parse(
12            Rule::service_document,
13            input.as_ref(),
14        )?),
15        &mut pc,
16    )?)
17}
18
19fn parse_service_document(
20    pair: Pair<Rule>,
21    pc: &mut PositionCalculator,
22) -> Result<ServiceDocument> {
23    debug_assert_eq!(pair.as_rule(), Rule::service_document);
24
25    Ok(ServiceDocument {
26        definitions: pair
27            .into_inner()
28            .filter(|pair| pair.as_rule() != Rule::EOI)
29            .map(|pair| parse_type_system_definition(pair, pc))
30            .collect::<Result<_>>()?,
31    })
32}
33
34fn parse_type_system_definition(
35    pair: Pair<Rule>,
36    pc: &mut PositionCalculator,
37) -> Result<TypeSystemDefinition> {
38    debug_assert_eq!(pair.as_rule(), Rule::type_system_definition);
39
40    let pair = exactly_one(pair.into_inner());
41    Ok(match pair.as_rule() {
42        Rule::schema_definition => TypeSystemDefinition::Schema(parse_schema_definition(pair, pc)?),
43        Rule::type_definition => TypeSystemDefinition::Type(parse_type_definition(pair, pc)?),
44        Rule::directive_definition => {
45            TypeSystemDefinition::Directive(parse_directive_definition(pair, pc)?)
46        }
47        _ => unreachable!(),
48    })
49}
50
51fn parse_schema_definition(
52    pair: Pair<Rule>,
53    pc: &mut PositionCalculator,
54) -> Result<Positioned<SchemaDefinition>> {
55    debug_assert_eq!(pair.as_rule(), Rule::schema_definition);
56
57    let pos = pc.step(&pair);
58    let mut pairs = pair.into_inner();
59
60    let extend = next_if_rule(&mut pairs, Rule::extend).is_some();
61    let directives = parse_opt_const_directives(&mut pairs, pc)?;
62
63    let mut query = None;
64    let mut mutation = None;
65    let mut subscription = None;
66
67    for pair in pairs {
68        debug_assert_eq!(pair.as_rule(), Rule::operation_type_definition);
69
70        let mut pairs = pair.into_inner();
71
72        let operation_type = parse_operation_type(pairs.next().unwrap(), pc)?;
73        let name = parse_name(pairs.next().unwrap(), pc)?;
74
75        match operation_type.node {
76            OperationType::Query if query.is_none() => query = Some(name),
77            OperationType::Mutation if mutation.is_none() => mutation = Some(name),
78            OperationType::Subscription if subscription.is_none() => subscription = Some(name),
79            _ => {
80                return Err(Error::MultipleRoots {
81                    root: operation_type.node,
82                    schema: pos,
83                    pos: operation_type.pos,
84                })
85            }
86        }
87
88        debug_assert_eq!(pairs.next(), None);
89    }
90
91    if !extend && query.is_none() {
92        return Err(Error::MissingQueryRoot { pos });
93    }
94
95    Ok(Positioned::new(
96        SchemaDefinition {
97            extend,
98            directives,
99            query,
100            mutation,
101            subscription,
102        },
103        pos,
104    ))
105}
106
107fn parse_type_definition(
108    pair: Pair<Rule>,
109    pc: &mut PositionCalculator,
110) -> Result<Positioned<TypeDefinition>> {
111    debug_assert_eq!(pair.as_rule(), Rule::type_definition);
112
113    let pos = pc.step(&pair);
114    let pair = exactly_one(pair.into_inner());
115    let rule = pair.as_rule();
116    let mut pairs = pair.into_inner();
117
118    let description = parse_if_rule(&mut pairs, Rule::string, |pair| parse_string(pair, pc))?;
119    let extend = next_if_rule(&mut pairs, Rule::extend).is_some();
120    let name = parse_name(pairs.next().unwrap(), pc)?;
121
122    let (directives, kind) = match rule {
123        Rule::scalar_type => {
124            let directives = parse_opt_const_directives(&mut pairs, pc)?;
125            (directives, TypeKind::Scalar)
126        }
127        Rule::object_type => {
128            let implements = parse_if_rule(&mut pairs, Rule::implements_interfaces, |pair| {
129                debug_assert_eq!(pair.as_rule(), Rule::implements_interfaces);
130
131                pair.into_inner()
132                    .map(|pair| parse_name(pair, pc))
133                    .collect::<Result<_>>()
134            })?;
135
136            let directives = parse_opt_const_directives(&mut pairs, pc)?;
137
138            let fields = parse_if_rule(&mut pairs, Rule::fields_definition, |pair| {
139                parse_fields_definition(pair, pc)
140            })?
141            .unwrap_or_default();
142
143            (
144                directives,
145                TypeKind::Object(ObjectType {
146                    implements: implements.unwrap_or_default(),
147                    fields,
148                }),
149            )
150        }
151        Rule::interface_type => {
152            let implements = parse_if_rule(&mut pairs, Rule::implements_interfaces, |pair| {
153                debug_assert_eq!(pair.as_rule(), Rule::implements_interfaces);
154
155                pair.into_inner()
156                    .map(|pair| parse_name(pair, pc))
157                    .collect::<Result<_>>()
158            })?;
159
160            let directives = parse_opt_const_directives(&mut pairs, pc)?;
161            let fields = parse_if_rule(&mut pairs, Rule::fields_definition, |pair| {
162                parse_fields_definition(pair, pc)
163            })?
164            .unwrap_or_default();
165            (
166                directives,
167                TypeKind::Interface(InterfaceType {
168                    implements: implements.unwrap_or_default(),
169                    fields,
170                }),
171            )
172        }
173        Rule::union_type => {
174            let directives = parse_opt_const_directives(&mut pairs, pc)?;
175            let members = parse_if_rule(&mut pairs, Rule::union_member_types, |pair| {
176                debug_assert_eq!(pair.as_rule(), Rule::union_member_types);
177
178                pair.into_inner().map(|pair| parse_name(pair, pc)).collect()
179            })?
180            .unwrap_or_default();
181            (directives, TypeKind::Union(UnionType { members }))
182        }
183        Rule::enum_type => {
184            let directives = parse_opt_const_directives(&mut pairs, pc)?;
185            let values = parse_if_rule(&mut pairs, Rule::enum_values, |pair| {
186                debug_assert_eq!(pair.as_rule(), Rule::enum_values);
187
188                pair.into_inner()
189                    .map(|pair| {
190                        debug_assert_eq!(pair.as_rule(), Rule::enum_value_definition);
191
192                        let pos = pc.step(&pair);
193                        let mut pairs = pair.into_inner();
194
195                        let description =
196                            parse_if_rule(&mut pairs, Rule::string, |pair| parse_string(pair, pc))?;
197                        let value = parse_enum_value(pairs.next().unwrap(), pc)?;
198                        let directives = parse_opt_const_directives(&mut pairs, pc)?;
199
200                        debug_assert_eq!(pairs.next(), None);
201
202                        Ok(Positioned::new(
203                            EnumValueDefinition {
204                                description,
205                                value,
206                                directives,
207                            },
208                            pos,
209                        ))
210                    })
211                    .collect()
212            })?
213            .unwrap_or_default();
214            (directives, TypeKind::Enum(EnumType { values }))
215        }
216        Rule::input_object_type => {
217            let directives = parse_opt_const_directives(&mut pairs, pc)?;
218            let fields = parse_if_rule(&mut pairs, Rule::input_fields_definition, |pair| {
219                debug_assert_eq!(pair.as_rule(), Rule::input_fields_definition);
220
221                pair.into_inner()
222                    .map(|pair| parse_input_value_definition(pair, pc))
223                    .collect()
224            })?
225            .unwrap_or_default();
226
227            (
228                directives,
229                TypeKind::InputObject(InputObjectType { fields }),
230            )
231        }
232        _ => unreachable!(),
233    };
234
235    debug_assert_eq!(pairs.next(), None);
236
237    Ok(Positioned::new(
238        TypeDefinition {
239            extend,
240            description,
241            name,
242            directives,
243            kind,
244        },
245        pos,
246    ))
247}
248
249fn parse_fields_definition(
250    pair: Pair<Rule>,
251    pc: &mut PositionCalculator,
252) -> Result<Vec<Positioned<FieldDefinition>>> {
253    debug_assert_eq!(pair.as_rule(), Rule::fields_definition);
254
255    pair.into_inner()
256        .map(|pair| parse_field_definition(pair, pc))
257        .collect()
258}
259
260fn parse_field_definition(
261    pair: Pair<Rule>,
262    pc: &mut PositionCalculator,
263) -> Result<Positioned<FieldDefinition>> {
264    debug_assert_eq!(pair.as_rule(), Rule::field_definition);
265
266    let pos = pc.step(&pair);
267    let mut pairs = pair.into_inner();
268
269    let description = parse_if_rule(&mut pairs, Rule::string, |pair| parse_string(pair, pc))?;
270    let name = parse_name(pairs.next().unwrap(), pc)?;
271    let arguments = parse_if_rule(&mut pairs, Rule::arguments_definition, |pair| {
272        parse_arguments_definition(pair, pc)
273    })?
274    .unwrap_or_default();
275    let ty = parse_type(pairs.next().unwrap(), pc)?;
276    let directives = parse_opt_const_directives(&mut pairs, pc)?;
277
278    debug_assert_eq!(pairs.next(), None);
279
280    Ok(Positioned::new(
281        FieldDefinition {
282            description,
283            name,
284            arguments,
285            ty,
286            directives,
287        },
288        pos,
289    ))
290}
291
292fn parse_directive_definition(
293    pair: Pair<Rule>,
294    pc: &mut PositionCalculator,
295) -> Result<Positioned<DirectiveDefinition>> {
296    debug_assert_eq!(pair.as_rule(), Rule::directive_definition);
297
298    let pos = pc.step(&pair);
299    let mut pairs = pair.into_inner();
300
301    let description = parse_if_rule(&mut pairs, Rule::string, |pair| parse_string(pair, pc))?;
302    let name = parse_name(pairs.next().unwrap(), pc)?;
303    let arguments = parse_if_rule(&mut pairs, Rule::arguments_definition, |pair| {
304        debug_assert_eq!(pair.as_rule(), Rule::arguments_definition);
305        pair.into_inner()
306            .map(|pair| parse_input_value_definition(pair, pc))
307            .collect()
308    })?
309    .unwrap_or_default();
310    let is_repeatable = parse_if_rule(&mut pairs, Rule::repeatable, |pair| {
311        debug_assert_eq!(pair.as_rule(), Rule::repeatable);
312        Ok(())
313    })
314    .unwrap_or_default()
315    .is_some();
316    let locations = {
317        let pair = pairs.next().unwrap();
318        debug_assert_eq!(pair.as_rule(), Rule::directive_locations);
319        pair.into_inner()
320            .map(|pair| {
321                let pos = pc.step(&pair);
322                debug_assert_eq!(pair.as_rule(), Rule::directive_location);
323                Positioned::new(
324                    match pair.as_str() {
325                        "QUERY" => DirectiveLocation::Query,
326                        "MUTATION" => DirectiveLocation::Mutation,
327                        "SUBSCRIPTION" => DirectiveLocation::Subscription,
328                        "FIELD" => DirectiveLocation::Field,
329                        "FRAGMENT_DEFINITION" => DirectiveLocation::FragmentDefinition,
330                        "FRAGMENT_SPREAD" => DirectiveLocation::FragmentSpread,
331                        "INLINE_FRAGMENT" => DirectiveLocation::InlineFragment,
332                        "VARIABLE_DEFINITION" => DirectiveLocation::VariableDefinition,
333                        "SCHEMA" => DirectiveLocation::Schema,
334                        "SCALAR" => DirectiveLocation::Scalar,
335                        "OBJECT" => DirectiveLocation::Object,
336                        "FIELD_DEFINITION" => DirectiveLocation::FieldDefinition,
337                        "ARGUMENT_DEFINITION" => DirectiveLocation::ArgumentDefinition,
338                        "INTERFACE" => DirectiveLocation::Interface,
339                        "UNION" => DirectiveLocation::Union,
340                        "ENUM" => DirectiveLocation::Enum,
341                        "ENUM_VALUE" => DirectiveLocation::EnumValue,
342                        "INPUT_OBJECT" => DirectiveLocation::InputObject,
343                        "INPUT_FIELD_DEFINITION" => DirectiveLocation::InputFieldDefinition,
344                        _ => unreachable!(),
345                    },
346                    pos,
347                )
348            })
349            .collect()
350    };
351
352    debug_assert_eq!(pairs.next(), None);
353
354    Ok(Positioned::new(
355        DirectiveDefinition {
356            description,
357            name,
358            arguments,
359            is_repeatable,
360            locations,
361        },
362        pos,
363    ))
364}
365
366fn parse_arguments_definition(
367    pair: Pair<Rule>,
368    pc: &mut PositionCalculator,
369) -> Result<Vec<Positioned<InputValueDefinition>>> {
370    debug_assert_eq!(pair.as_rule(), Rule::arguments_definition);
371
372    pair.into_inner()
373        .map(|pair| parse_input_value_definition(pair, pc))
374        .collect()
375}
376
377fn parse_input_value_definition(
378    pair: Pair<Rule>,
379    pc: &mut PositionCalculator,
380) -> Result<Positioned<InputValueDefinition>> {
381    debug_assert_eq!(pair.as_rule(), Rule::input_value_definition);
382
383    let pos = pc.step(&pair);
384    let mut pairs = pair.into_inner();
385
386    let description = parse_if_rule(&mut pairs, Rule::string, |pair| parse_string(pair, pc))?;
387    let name = parse_name(pairs.next().unwrap(), pc)?;
388    let ty = parse_type(pairs.next().unwrap(), pc)?;
389    let default_value = parse_if_rule(&mut pairs, Rule::default_value, |pair| {
390        parse_default_value(pair, pc)
391    })?;
392    let directives = parse_opt_const_directives(&mut pairs, pc)?;
393
394    Ok(Positioned::new(
395        InputValueDefinition {
396            description,
397            name,
398            ty,
399            default_value,
400            directives,
401        },
402        pos,
403    ))
404}
405
406#[cfg(test)]
407mod tests {
408    use std::fs;
409
410    use super::*;
411
412    #[test]
413    fn test_parser() {
414        for entry in fs::read_dir("tests/services").unwrap() {
415            let entry = entry.unwrap();
416            eprintln!("Parsing file {}", entry.path().display());
417            GraphQLParser::parse(
418                Rule::service_document,
419                &fs::read_to_string(entry.path()).unwrap(),
420            )
421            .unwrap();
422        }
423    }
424
425    #[test]
426    fn test_parser_ast() {
427        for entry in fs::read_dir("tests/services").unwrap() {
428            let entry = entry.unwrap();
429            parse_schema(fs::read_to_string(entry.path()).unwrap()).unwrap();
430        }
431    }
432}