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