1use std::collections::{hash_map, HashMap};
6
7use pest::{
8 iterators::{Pair, Pairs},
9 Parser,
10};
11use utils::*;
12
13use crate::{
14 pos::{PositionCalculator, Positioned},
15 types::*,
16 Error, Result,
17};
18
19mod executable;
20#[allow(clippy::redundant_static_lifetimes)]
21#[rustfmt::skip]
22#[allow(dead_code)]
23mod generated;
24mod service;
25mod utils;
26
27use async_graphql_value::{ConstValue, Name, Number, Value};
28pub use executable::parse_query;
29use generated::Rule;
30pub use service::parse_schema;
31
32struct GraphQLParser;
33
34fn parse_operation_type(
35 pair: Pair<Rule>,
36 pc: &mut PositionCalculator,
37) -> Result<Positioned<OperationType>> {
38 debug_assert_eq!(pair.as_rule(), Rule::operation_type);
39
40 let pos = pc.step(&pair);
41
42 Ok(Positioned::new(
43 match pair.as_str() {
44 "query" => OperationType::Query,
45 "mutation" => OperationType::Mutation,
46 "subscription" => OperationType::Subscription,
47 _ => unreachable!(),
48 },
49 pos,
50 ))
51}
52
53fn parse_default_value(
54 pair: Pair<Rule>,
55 pc: &mut PositionCalculator,
56) -> Result<Positioned<ConstValue>> {
57 debug_assert_eq!(pair.as_rule(), Rule::default_value);
58
59 parse_const_value(exactly_one(pair.into_inner()), pc)
60}
61
62fn parse_type(pair: Pair<Rule>, pc: &mut PositionCalculator) -> Result<Positioned<Type>> {
63 debug_assert_eq!(pair.as_rule(), Rule::type_);
64
65 Ok(Positioned::new(
66 Type::new(pair.as_str()).unwrap(),
67 pc.step(&pair),
68 ))
69}
70
71fn parse_const_value(
72 pair: Pair<Rule>,
73 pc: &mut PositionCalculator,
74) -> Result<Positioned<ConstValue>> {
75 debug_assert_eq!(pair.as_rule(), Rule::const_value);
76
77 let pos = pc.step(&pair);
78 let pair = exactly_one(pair.into_inner());
79
80 Ok(Positioned::new(
81 match pair.as_rule() {
82 Rule::number => ConstValue::Number(parse_number(pair, pc)?.node),
83 Rule::string => ConstValue::String(parse_string(pair, pc)?.node),
84 Rule::boolean => ConstValue::Boolean(parse_boolean(pair, pc)?.node),
85 Rule::null => ConstValue::Null,
86 Rule::enum_value => ConstValue::Enum(parse_enum_value(pair, pc)?.node),
87 Rule::const_list => ConstValue::List(
88 pair.into_inner()
89 .map(|pair| Ok(parse_const_value(pair, pc)?.node))
90 .collect::<Result<_>>()?,
91 ),
92 Rule::const_object => ConstValue::Object(
93 pair.into_inner()
94 .map(|pair| {
95 debug_assert_eq!(pair.as_rule(), Rule::const_object_field);
96
97 let mut pairs = pair.into_inner();
98
99 let name = parse_name(pairs.next().unwrap(), pc)?;
100 let value = parse_const_value(pairs.next().unwrap(), pc)?;
101
102 debug_assert_eq!(pairs.next(), None);
103
104 Ok((name.node, value.node))
105 })
106 .collect::<Result<_>>()?,
107 ),
108 _ => unreachable!(),
109 },
110 pos,
111 ))
112}
113fn parse_value(pair: Pair<Rule>, pc: &mut PositionCalculator) -> Result<Positioned<Value>> {
114 debug_assert_eq!(pair.as_rule(), Rule::value);
115
116 let pos = pc.step(&pair);
117 let pair = exactly_one(pair.into_inner());
118
119 Ok(Positioned::new(
120 match pair.as_rule() {
121 Rule::variable => Value::Variable(parse_variable(pair, pc)?.node),
122 Rule::number => Value::Number(parse_number(pair, pc)?.node),
123 Rule::string => Value::String(parse_string(pair, pc)?.node),
124 Rule::boolean => Value::Boolean(parse_boolean(pair, pc)?.node),
125 Rule::null => Value::Null,
126 Rule::enum_value => Value::Enum(parse_enum_value(pair, pc)?.node),
127 Rule::list => Value::List(
128 pair.into_inner()
129 .map(|pair| Ok(parse_value(pair, pc)?.node))
130 .collect::<Result<_>>()?,
131 ),
132 Rule::object => Value::Object(
133 pair.into_inner()
134 .map(|pair| {
135 debug_assert_eq!(pair.as_rule(), Rule::object_field);
136 let mut pairs = pair.into_inner();
137
138 let name = parse_name(pairs.next().unwrap(), pc)?;
139 let value = parse_value(pairs.next().unwrap(), pc)?;
140
141 debug_assert_eq!(pairs.next(), None);
142
143 Ok((name.node, value.node))
144 })
145 .collect::<Result<_>>()?,
146 ),
147 _ => unreachable!(),
148 },
149 pos,
150 ))
151}
152
153fn parse_variable(pair: Pair<Rule>, pc: &mut PositionCalculator) -> Result<Positioned<Name>> {
154 debug_assert_eq!(pair.as_rule(), Rule::variable);
155 parse_name(exactly_one(pair.into_inner()), pc)
156}
157fn parse_number(pair: Pair<Rule>, pc: &mut PositionCalculator) -> Result<Positioned<Number>> {
158 debug_assert_eq!(pair.as_rule(), Rule::number);
159 let pos = pc.step(&pair);
160 Ok(Positioned::new(
161 pair.as_str().parse().map_err(|err| Error::Syntax {
162 message: format!("invalid number: {}", err),
163 start: pos,
164 end: None,
165 })?,
166 pos,
167 ))
168}
169fn parse_string(pair: Pair<Rule>, pc: &mut PositionCalculator) -> Result<Positioned<String>> {
170 debug_assert_eq!(pair.as_rule(), Rule::string);
171 let pos = pc.step(&pair);
172 let pair = exactly_one(pair.into_inner());
173 Ok(Positioned::new(
174 match pair.as_rule() {
175 Rule::block_string_content => block_string_value(pair.as_str()),
176 Rule::string_content => string_value(pair.as_str()),
177 _ => unreachable!(),
178 },
179 pos,
180 ))
181}
182fn parse_boolean(pair: Pair<Rule>, pc: &mut PositionCalculator) -> Result<Positioned<bool>> {
183 debug_assert_eq!(pair.as_rule(), Rule::boolean);
184 let pos = pc.step(&pair);
185 Ok(Positioned::new(
186 match pair.as_str() {
187 "true" => true,
188 "false" => false,
189 _ => unreachable!(),
190 },
191 pos,
192 ))
193}
194fn parse_enum_value(pair: Pair<Rule>, pc: &mut PositionCalculator) -> Result<Positioned<Name>> {
195 debug_assert_eq!(pair.as_rule(), Rule::enum_value);
196 parse_name(exactly_one(pair.into_inner()), pc)
197}
198
199fn parse_opt_const_directives(
200 pairs: &mut Pairs<'_, Rule>,
201 pc: &mut PositionCalculator,
202) -> Result<Vec<Positioned<ConstDirective>>> {
203 Ok(parse_if_rule(pairs, Rule::const_directives, |pair| {
204 parse_const_directives(pair, pc)
205 })?
206 .unwrap_or_default())
207}
208fn parse_opt_directives(
209 pairs: &mut Pairs<'_, Rule>,
210 pc: &mut PositionCalculator,
211) -> Result<Vec<Positioned<Directive>>> {
212 Ok(
213 parse_if_rule(pairs, Rule::directives, |pair| parse_directives(pair, pc))?
214 .unwrap_or_default(),
215 )
216}
217fn parse_const_directives(
218 pair: Pair<Rule>,
219 pc: &mut PositionCalculator,
220) -> Result<Vec<Positioned<ConstDirective>>> {
221 debug_assert_eq!(pair.as_rule(), Rule::const_directives);
222
223 pair.into_inner()
224 .map(|pair| parse_const_directive(pair, pc))
225 .collect()
226}
227fn parse_directives(
228 pair: Pair<Rule>,
229 pc: &mut PositionCalculator,
230) -> Result<Vec<Positioned<Directive>>> {
231 debug_assert_eq!(pair.as_rule(), Rule::directives);
232
233 pair.into_inner()
234 .map(|pair| parse_directive(pair, pc))
235 .collect()
236}
237
238fn parse_const_directive(
239 pair: Pair<Rule>,
240 pc: &mut PositionCalculator,
241) -> Result<Positioned<ConstDirective>> {
242 debug_assert_eq!(pair.as_rule(), Rule::const_directive);
243
244 let pos = pc.step(&pair);
245 let mut pairs = pair.into_inner();
246
247 let name = parse_name(pairs.next().unwrap(), pc)?;
248 let arguments = parse_if_rule(&mut pairs, Rule::const_arguments, |pair| {
249 parse_const_arguments(pair, pc)
250 })?;
251
252 debug_assert_eq!(pairs.next(), None);
253
254 Ok(Positioned::new(
255 ConstDirective {
256 name,
257 arguments: arguments.unwrap_or_default(),
258 },
259 pos,
260 ))
261}
262fn parse_directive(pair: Pair<Rule>, pc: &mut PositionCalculator) -> Result<Positioned<Directive>> {
263 debug_assert_eq!(pair.as_rule(), Rule::directive);
264
265 let pos = pc.step(&pair);
266 let mut pairs = pair.into_inner();
267
268 let name = parse_name(pairs.next().unwrap(), pc)?;
269 let arguments = parse_if_rule(&mut pairs, Rule::arguments, |pair| {
270 parse_arguments(pair, pc)
271 })?;
272
273 debug_assert_eq!(pairs.next(), None);
274
275 Ok(Positioned::new(
276 Directive {
277 name,
278 arguments: arguments.unwrap_or_default(),
279 },
280 pos,
281 ))
282}
283
284fn parse_const_arguments(
285 pair: Pair<Rule>,
286 pc: &mut PositionCalculator,
287) -> Result<Vec<(Positioned<Name>, Positioned<ConstValue>)>> {
288 debug_assert_eq!(pair.as_rule(), Rule::const_arguments);
289 pair.into_inner()
290 .map(|pair| {
291 debug_assert_eq!(pair.as_rule(), Rule::const_argument);
292 let mut pairs = pair.into_inner();
293
294 let name = parse_name(pairs.next().unwrap(), pc)?;
295 let value = parse_const_value(pairs.next().unwrap(), pc)?;
296
297 debug_assert_eq!(pairs.next(), None);
298
299 Ok((name, value))
300 })
301 .collect()
302}
303fn parse_arguments(
304 pair: Pair<Rule>,
305 pc: &mut PositionCalculator,
306) -> Result<Vec<(Positioned<Name>, Positioned<Value>)>> {
307 debug_assert_eq!(pair.as_rule(), Rule::arguments);
308 pair.into_inner()
309 .map(|pair| {
310 debug_assert_eq!(pair.as_rule(), Rule::argument);
311 let mut pairs = pair.into_inner();
312
313 let name = parse_name(pairs.next().unwrap(), pc)?;
314 let value = parse_value(pairs.next().unwrap(), pc)?;
315
316 debug_assert_eq!(pairs.next(), None);
317
318 Ok((name, value))
319 })
320 .collect()
321}
322
323fn parse_name(pair: Pair<Rule>, pc: &mut PositionCalculator) -> Result<Positioned<Name>> {
324 debug_assert_eq!(pair.as_rule(), Rule::name);
325 Ok(Positioned::new(Name::new(pair.as_str()), pc.step(&pair)))
326}
327
328#[cfg(test)]
329mod tests {
330 use super::*;
331
332 #[test]
333 fn test_number_lookahead_restrictions() {
334 GraphQLParser::parse(Rule::const_list, "[123 abc]").unwrap();
335 GraphQLParser::parse(Rule::const_list, "[123.0123 abc]").unwrap();
336 GraphQLParser::parse(Rule::const_list, "[123.0123e7 abc]").unwrap();
337 GraphQLParser::parse(Rule::const_list, "[123.0123e77 abc]").unwrap();
338
339 assert!(GraphQLParser::parse(Rule::const_list, "[123abc]").is_err());
340 assert!(GraphQLParser::parse(Rule::const_list, "[123.0123abc]").is_err());
341 assert!(GraphQLParser::parse(Rule::const_list, "[123.0123e7abc]").is_err());
342 assert!(GraphQLParser::parse(Rule::const_list, "[123.0123e77abc]").is_err());
343 }
344}