graphql_toolkit_parser/parse/
mod.rs

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