graphql_toolkit_parser/parse/
service.rs

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