1use combine::{eof, many1, optional, position, StdParseResult};
2use combine::{parser, Parser};
3
4use super::ast::*;
5use super::error::ParseError;
6use crate::parser::common::Directive;
7use crate::parser::common::{arguments, default_value, directives, parse_type};
8use crate::parser::helpers::{ident, name, punct};
9use crate::parser::tokenizer::TokenStream;
10
11pub fn field<'a, S>(input: &mut TokenStream<'a>) -> StdParseResult<Field<'a, S>, TokenStream<'a>>
12where
13 S: Text<'a>,
14{
15 (
16 position(),
17 name::<'a, S>(),
18 optional(punct(":").with(name::<'a, S>())),
19 parser(arguments),
20 parser(directives),
21 optional(parser(selection_set)),
22 )
23 .map(
24 |(position, name_or_alias, opt_name, arguments, directives, sel)| {
25 let (name, alias) = match opt_name {
26 Some(name) => (name, Some(name_or_alias)),
27 None => (name_or_alias, None),
28 };
29 Field {
30 position,
31 name,
32 alias,
33 arguments,
34 directives,
35 selection_set: sel.unwrap_or_else(|| SelectionSet {
36 span: (position, position),
37 items: Vec::new(),
38 }),
39 }
40 },
41 )
42 .parse_stream(input)
43 .into_result()
44}
45
46pub fn selection<'a, S>(
47 input: &mut TokenStream<'a>,
48) -> StdParseResult<Selection<'a, S>, TokenStream<'a>>
49where
50 S: Text<'a>,
51{
52 parser(field)
53 .map(Selection::Field)
54 .or(punct("...").with(
55 (
56 position(),
57 optional(ident("on").with(name::<'a, S>()).map(TypeCondition::On)),
58 parser(directives),
59 parser(selection_set),
60 )
61 .map(
62 |(position, type_condition, directives, selection_set)| InlineFragment {
63 position,
64 type_condition,
65 selection_set,
66 directives,
67 },
68 )
69 .map(Selection::InlineFragment)
70 .or((position(), name::<'a, S>(), parser(directives))
71 .map(|(position, fragment_name, directives)| FragmentSpread {
72 position,
73 fragment_name,
74 directives,
75 })
76 .map(Selection::FragmentSpread)),
77 ))
78 .parse_stream(input)
79 .into_result()
80}
81
82pub fn selection_set<'a, S>(
83 input: &mut TokenStream<'a>,
84) -> StdParseResult<SelectionSet<'a, S>, TokenStream<'a>>
85where
86 S: Text<'a>,
87{
88 (
89 position().skip(punct("{")),
90 many1(parser(selection)),
91 position().skip(punct("}")),
92 )
93 .map(|(start, items, end)| SelectionSet {
94 span: (start, end),
95 items,
96 })
97 .parse_stream(input)
98 .into_result()
99}
100
101pub fn query<'a, T: Text<'a>>(
102 input: &mut TokenStream<'a>,
103) -> StdParseResult<Query<'a, T>, TokenStream<'a>> {
104 position()
105 .skip(ident("query"))
106 .and(parser(operation_common))
107 .map(
108 |(position, (name, variable_definitions, directives, selection_set))| Query {
109 position,
110 name,
111 selection_set,
112 variable_definitions,
113 directives,
114 },
115 )
116 .parse_stream(input)
117 .into_result()
118}
119
120#[allow(type_alias_bounds)]
122type OperationCommon<'a, T: Text<'a>> = (
123 Option<T::Value>,
124 Vec<VariableDefinition<'a, T>>,
125 Vec<Directive<'a, T>>,
126 SelectionSet<'a, T>,
127);
128
129pub fn operation_common<'a, T: Text<'a>>(
130 input: &mut TokenStream<'a>,
131) -> StdParseResult<OperationCommon<'a, T>, TokenStream<'a>> {
132 optional(name::<'a, T>())
133 .and(
134 optional(
135 punct("(")
136 .with(many1(
137 (
138 position(),
139 punct("$").with(name::<'a, T>()).skip(punct(":")),
140 parser(parse_type),
141 optional(punct("=").with(parser(default_value))),
142 )
143 .map(
144 |(position, name, var_type, default_value)| VariableDefinition {
145 position,
146 name,
147 var_type,
148 default_value,
149 },
150 ),
151 ))
152 .skip(punct(")")),
153 )
154 .map(|vars| vars.unwrap_or_default()),
155 )
156 .and(parser(directives))
157 .and(parser(selection_set))
158 .map(|(((a, b), c), d)| (a, b, c, d))
159 .parse_stream(input)
160 .into_result()
161}
162
163pub fn mutation<'a, T: Text<'a>>(
164 input: &mut TokenStream<'a>,
165) -> StdParseResult<Mutation<'a, T>, TokenStream<'a>> {
166 position()
167 .skip(ident("mutation"))
168 .and(parser(operation_common))
169 .map(
170 |(position, (name, variable_definitions, directives, selection_set))| Mutation {
171 position,
172 name,
173 selection_set,
174 variable_definitions,
175 directives,
176 },
177 )
178 .parse_stream(input)
179 .into_result()
180}
181
182pub fn subscription<'a, T: Text<'a>>(
183 input: &mut TokenStream<'a>,
184) -> StdParseResult<Subscription<'a, T>, TokenStream<'a>> {
185 position()
186 .skip(ident("subscription"))
187 .and(parser(operation_common))
188 .map(
189 |(position, (name, variable_definitions, directives, selection_set))| Subscription {
190 position,
191 name,
192 selection_set,
193 variable_definitions,
194 directives,
195 },
196 )
197 .parse_stream(input)
198 .into_result()
199}
200
201pub fn operation_definition<'a, S>(
202 input: &mut TokenStream<'a>,
203) -> StdParseResult<OperationDefinition<'a, S>, TokenStream<'a>>
204where
205 S: Text<'a>,
206{
207 parser(selection_set)
208 .map(OperationDefinition::SelectionSet)
209 .or(parser(query).map(OperationDefinition::Query))
210 .or(parser(mutation).map(OperationDefinition::Mutation))
211 .or(parser(subscription).map(OperationDefinition::Subscription))
212 .parse_stream(input)
213 .into_result()
214}
215
216pub fn fragment_definition<'a, T: Text<'a>>(
217 input: &mut TokenStream<'a>,
218) -> StdParseResult<FragmentDefinition<'a, T>, TokenStream<'a>> {
219 (
220 position().skip(ident("fragment")),
221 name::<'a, T>(),
222 ident("on").with(name::<'a, T>()).map(TypeCondition::On),
223 parser(directives),
224 parser(selection_set),
225 )
226 .map(
227 |(position, name, type_condition, directives, selection_set)| FragmentDefinition {
228 position,
229 name,
230 type_condition,
231 directives,
232 selection_set,
233 },
234 )
235 .parse_stream(input)
236 .into_result()
237}
238
239pub fn definition<'a, S>(
240 input: &mut TokenStream<'a>,
241) -> StdParseResult<Definition<'a, S>, TokenStream<'a>>
242where
243 S: Text<'a>,
244{
245 parser(operation_definition)
246 .map(Definition::Operation)
247 .or(parser(fragment_definition).map(Definition::Fragment))
248 .parse_stream(input)
249 .into_result()
250}
251
252pub fn parse_query<'a, S>(s: &'a str) -> Result<Document<'a, S>, ParseError>
254where
255 S: Text<'a>,
256{
257 let tokens = TokenStream::new(s);
258 handle_token_stream(tokens)
259}
260
261pub fn parse_query_with_token_limit<'a, S>(
262 s: &'a str,
263 token_limit: usize,
264) -> Result<Document<'a, S>, ParseError>
265where
266 S: Text<'a>,
267{
268 let tokens = TokenStream::new_with_token_limit(s, token_limit);
269 handle_token_stream(tokens)
270}
271
272fn handle_token_stream<'a, S>(mut tokens: TokenStream<'a>) -> Result<Document<'a, S>, ParseError>
273where
274 S: Text<'a>,
275{
276 let (doc, _) = many1(parser(definition))
277 .map(|d| Document { definitions: d })
278 .skip(eof())
279 .parse_stream(&mut tokens)
280 .into_result()
281 .map_err(|e| e.into_inner().error)?;
282
283 Ok(doc)
284}
285
286pub fn consume_definition<'a, S>(s: &'a str) -> Result<(Definition<'a, S>, &'a str), ParseError>
289where
290 S: Text<'a>,
291{
292 let tokens = TokenStream::new(s);
293 let (doc, tokens) = parser(definition).parse(tokens)?;
294
295 Ok((doc, &s[tokens.offset()..]))
296}
297
298#[cfg(test)]
299mod test {
300 use super::*;
301 use super::{consume_definition, parse_query};
302 use crate::parser::position::Pos;
303
304 fn ast<'a>(s: &'a str) -> Document<'a, String> {
305 parse_query::<String>(s).unwrap().to_owned()
306 }
307
308 #[test]
309 fn one_field() {
310 assert_eq!(
311 ast("{ a }"),
312 Document {
313 definitions: vec![Definition::Operation(OperationDefinition::SelectionSet(
314 SelectionSet {
315 span: (Pos { line: 1, column: 1 }, Pos { line: 1, column: 5 }),
316 items: vec![Selection::Field(Field {
317 position: Pos { line: 1, column: 3 },
318 alias: None,
319 name: "a".into(),
320 arguments: Vec::new(),
321 directives: Vec::new(),
322 selection_set: SelectionSet {
323 span: (Pos { line: 1, column: 3 }, Pos { line: 1, column: 3 }),
324 items: Vec::new()
325 },
326 }),],
327 }
328 ))],
329 }
330 );
331 }
332
333 #[test]
334 fn builtin_values() {
335 assert_eq!(
336 ast("{ a(t: true, f: false, n: null) }"),
337 Document {
338 definitions: vec![Definition::Operation(OperationDefinition::SelectionSet(
339 SelectionSet {
340 span: (
341 Pos { line: 1, column: 1 },
342 Pos {
343 line: 1,
344 column: 33
345 }
346 ),
347 items: vec![Selection::Field(Field {
348 position: Pos { line: 1, column: 3 },
349 alias: None,
350 name: "a".into(),
351 arguments: vec![
352 ("t".into(), Value::Boolean(true)),
353 ("f".into(), Value::Boolean(false)),
354 ("n".into(), Value::Null),
355 ],
356 directives: Vec::new(),
357 selection_set: SelectionSet {
358 span: (Pos { line: 1, column: 3 }, Pos { line: 1, column: 3 }),
359 items: Vec::new()
360 },
361 }),],
362 }
363 ))],
364 }
365 );
366 }
367
368 #[test]
369 fn one_field_roundtrip() {
370 assert_eq!(ast("{ a }").to_string(), "{\n a\n}\n");
371 }
372
373 #[test]
374 #[should_panic(expected = "PosOverflow")]
375 fn large_integer() {
376 ast("{ a(x: 10000000000000000000000000000 }");
377 }
378
379 #[test]
380 fn consume_single_query() {
381 let (query, remainder) = consume_definition::<String>("query { a } query { b }").unwrap();
382 assert!(matches!(query, Definition::Operation(_)));
383 assert_eq!(remainder, "query { b }");
384 }
385
386 #[test]
387 fn consume_full_text() {
388 let (query, remainder) = consume_definition::<String>("query { a }").unwrap();
389 assert!(matches!(query, Definition::Operation(_)));
390 assert_eq!(remainder, "");
391 }
392
393 #[test]
394 fn consume_single_query_preceding_non_graphql() {
395 let (query, remainder) =
396 consume_definition::<String>("query { a } where a > 1 => 10.0").unwrap();
397 assert!(matches!(query, Definition::Operation(_)));
398 assert_eq!(remainder, "where a > 1 => 10.0");
399 }
400
401 #[test]
402 fn consume_fails_without_operation() {
403 let err = consume_definition::<String>("where a > 1 => 10.0")
404 .expect_err("Expected parse to fail with an error");
405 let err = format!("{}", err);
406 assert_eq!(err, "Parse error at 1:1\nUnexpected `where[Name]`\nExpected {, query, mutation, subscription or fragment\n");
407 }
408
409 #[test]
410 fn recursion_too_deep() {
411 let query = format!(
412 "{}(b: {}{}){}",
413 "{ a".repeat(30),
414 "[".repeat(25),
415 "]".repeat(25),
416 "}".repeat(30)
417 );
418 let result = parse_query::<&str>(&query);
419 let err = format!("{}", result.unwrap_err());
420 assert_eq!(
421 &err,
422 "Parse error at 1:114\nExpected ]\nRecursion limit exceeded\n"
423 )
424 }
425}