async_graphql_parser/parse/
mod.rs

1//! Parsing module.
2//!
3//! This module's structure mirrors `types`.
4
5use 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}