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