2020-09-08 08:21:27 +00:00
|
|
|
use super::*;
|
|
|
|
|
|
|
|
/// Parse a GraphQL query document.
|
|
|
|
///
|
|
|
|
/// # Errors
|
|
|
|
///
|
|
|
|
/// Fails if the query is not a valid GraphQL document.
|
|
|
|
pub fn parse_query<T: AsRef<str>>(input: T) -> Result<ExecutableDocument> {
|
|
|
|
let mut pc = PositionCalculator::new(input.as_ref());
|
|
|
|
Ok(parse_executable_document(
|
2020-09-08 08:30:29 +00:00
|
|
|
exactly_one(GraphQLParser::parse(
|
|
|
|
Rule::executable_document,
|
|
|
|
input.as_ref(),
|
|
|
|
)?),
|
2020-09-08 08:21:27 +00:00
|
|
|
&mut pc,
|
|
|
|
)?)
|
|
|
|
}
|
|
|
|
|
2020-09-08 08:30:29 +00:00
|
|
|
fn parse_executable_document(
|
|
|
|
pair: Pair<Rule>,
|
|
|
|
pc: &mut PositionCalculator,
|
|
|
|
) -> Result<ExecutableDocument> {
|
2020-09-08 08:21:27 +00:00
|
|
|
debug_assert_eq!(pair.as_rule(), Rule::executable_document);
|
|
|
|
|
|
|
|
Ok(ExecutableDocument {
|
|
|
|
definitions: pair
|
|
|
|
.into_inner()
|
|
|
|
.filter(|pair| pair.as_rule() != Rule::EOI)
|
|
|
|
.map(|pair| parse_executable_definition(pair, pc))
|
|
|
|
.collect::<Result<_>>()?,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2020-09-08 08:30:29 +00:00
|
|
|
fn parse_executable_definition(
|
|
|
|
pair: Pair<Rule>,
|
|
|
|
pc: &mut PositionCalculator,
|
|
|
|
) -> Result<ExecutableDefinition> {
|
2020-09-08 08:21:27 +00:00
|
|
|
debug_assert_eq!(pair.as_rule(), Rule::executable_definition);
|
|
|
|
|
|
|
|
let pair = exactly_one(pair.into_inner());
|
|
|
|
Ok(match pair.as_rule() {
|
2020-09-08 08:30:29 +00:00
|
|
|
Rule::operation_definition => {
|
|
|
|
ExecutableDefinition::Operation(parse_operation_definition(pair, pc)?)
|
|
|
|
}
|
|
|
|
Rule::fragment_definition => {
|
|
|
|
ExecutableDefinition::Fragment(parse_fragment_definition(pair, pc)?)
|
|
|
|
}
|
2020-09-08 08:21:27 +00:00
|
|
|
_ => unreachable!(),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
fn parse_operation_definition(
|
|
|
|
pair: Pair<Rule>,
|
|
|
|
pc: &mut PositionCalculator,
|
|
|
|
) -> Result<Positioned<OperationDefinition>> {
|
|
|
|
debug_assert_eq!(pair.as_rule(), Rule::operation_definition);
|
|
|
|
|
|
|
|
let pos = pc.step(&pair);
|
|
|
|
let pair = exactly_one(pair.into_inner());
|
|
|
|
Ok(Positioned::new(
|
|
|
|
match pair.as_rule() {
|
|
|
|
Rule::named_operation_definition => parse_named_operation_definition(pair, pc)?,
|
|
|
|
Rule::selection_set => OperationDefinition {
|
|
|
|
ty: OperationType::Query,
|
|
|
|
name: None,
|
|
|
|
variable_definitions: Vec::new(),
|
|
|
|
directives: Vec::new(),
|
|
|
|
selection_set: parse_selection_set(pair, pc)?,
|
|
|
|
},
|
|
|
|
_ => unreachable!(),
|
|
|
|
},
|
|
|
|
pos,
|
|
|
|
))
|
|
|
|
}
|
|
|
|
|
|
|
|
fn parse_named_operation_definition(
|
|
|
|
pair: Pair<Rule>,
|
|
|
|
pc: &mut PositionCalculator,
|
|
|
|
) -> Result<OperationDefinition> {
|
|
|
|
debug_assert_eq!(pair.as_rule(), Rule::named_operation_definition);
|
|
|
|
|
|
|
|
let mut pairs = pair.into_inner();
|
|
|
|
|
|
|
|
let ty = parse_operation_type(pairs.next().unwrap(), pc)?;
|
|
|
|
let name = parse_if_rule(&mut pairs, Rule::name, |pair| parse_name(pair, pc))?;
|
2020-09-08 08:30:29 +00:00
|
|
|
let variable_definitions = parse_if_rule(&mut pairs, Rule::variable_definitions, |pair| {
|
|
|
|
parse_variable_definitions(pair, pc)
|
|
|
|
})?;
|
2020-09-08 08:21:27 +00:00
|
|
|
let directives = parse_opt_directives(&mut pairs, pc)?;
|
|
|
|
let selection_set = parse_selection_set(pairs.next().unwrap(), pc)?;
|
|
|
|
|
|
|
|
debug_assert_eq!(pairs.next(), None);
|
|
|
|
|
|
|
|
Ok(OperationDefinition {
|
|
|
|
ty: ty.node,
|
|
|
|
name,
|
|
|
|
variable_definitions: variable_definitions.unwrap_or_default(),
|
|
|
|
directives,
|
|
|
|
selection_set,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
fn parse_variable_definitions(
|
|
|
|
pair: Pair<Rule>,
|
|
|
|
pc: &mut PositionCalculator,
|
|
|
|
) -> Result<Vec<Positioned<VariableDefinition>>> {
|
|
|
|
debug_assert_eq!(pair.as_rule(), Rule::variable_definitions);
|
|
|
|
|
|
|
|
pair.into_inner()
|
|
|
|
.map(|pair| parse_variable_definition(pair, pc))
|
|
|
|
.collect()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn parse_variable_definition(
|
|
|
|
pair: Pair<Rule>,
|
|
|
|
pc: &mut PositionCalculator,
|
|
|
|
) -> Result<Positioned<VariableDefinition>> {
|
|
|
|
debug_assert_eq!(pair.as_rule(), Rule::variable_definition);
|
|
|
|
|
|
|
|
let pos = pc.step(&pair);
|
|
|
|
let mut pairs = pair.into_inner();
|
|
|
|
|
|
|
|
let variable = parse_variable(pairs.next().unwrap(), pc)?;
|
|
|
|
let var_type = parse_type(pairs.next().unwrap(), pc)?;
|
2020-09-08 08:30:29 +00:00
|
|
|
let default_value = parse_if_rule(&mut pairs, Rule::default_value, |pair| {
|
|
|
|
parse_default_value(pair, pc)
|
|
|
|
})?;
|
2020-09-08 08:21:27 +00:00
|
|
|
|
|
|
|
debug_assert_eq!(pairs.next(), None);
|
|
|
|
|
|
|
|
Ok(Positioned::new(
|
|
|
|
VariableDefinition {
|
|
|
|
name: variable,
|
|
|
|
var_type,
|
|
|
|
default_value,
|
|
|
|
},
|
|
|
|
pos,
|
|
|
|
))
|
|
|
|
}
|
|
|
|
|
2020-09-08 08:30:29 +00:00
|
|
|
fn parse_selection_set(
|
|
|
|
pair: Pair<Rule>,
|
|
|
|
pc: &mut PositionCalculator,
|
|
|
|
) -> Result<Positioned<SelectionSet>> {
|
2020-09-08 08:21:27 +00:00
|
|
|
debug_assert_eq!(pair.as_rule(), Rule::selection_set);
|
|
|
|
|
|
|
|
let pos = pc.step(&pair);
|
|
|
|
|
|
|
|
Ok(Positioned::new(
|
|
|
|
SelectionSet {
|
|
|
|
items: pair
|
|
|
|
.into_inner()
|
|
|
|
.map(|pair| parse_selection(pair, pc))
|
|
|
|
.collect::<Result<_>>()?,
|
|
|
|
},
|
|
|
|
pos,
|
|
|
|
))
|
|
|
|
}
|
|
|
|
|
|
|
|
fn parse_selection(pair: Pair<Rule>, pc: &mut PositionCalculator) -> Result<Positioned<Selection>> {
|
|
|
|
debug_assert_eq!(pair.as_rule(), Rule::selection);
|
|
|
|
|
|
|
|
let pos = pc.step(&pair);
|
|
|
|
let pair = exactly_one(pair.into_inner());
|
|
|
|
|
|
|
|
Ok(Positioned::new(
|
|
|
|
match pair.as_rule() {
|
|
|
|
Rule::field => Selection::Field(parse_field(pair, pc)?),
|
|
|
|
Rule::fragment_spread => Selection::FragmentSpread(parse_fragment_spread(pair, pc)?),
|
|
|
|
Rule::inline_fragment => Selection::InlineFragment(parse_inline_fragment(pair, pc)?),
|
|
|
|
_ => unreachable!(),
|
|
|
|
},
|
|
|
|
pos,
|
|
|
|
))
|
|
|
|
}
|
|
|
|
|
|
|
|
fn parse_field(pair: Pair<Rule>, pc: &mut PositionCalculator) -> Result<Positioned<Field>> {
|
|
|
|
debug_assert_eq!(pair.as_rule(), Rule::field);
|
|
|
|
|
|
|
|
let pos = pc.step(&pair);
|
|
|
|
let mut pairs = pair.into_inner();
|
|
|
|
|
|
|
|
let alias = parse_if_rule(&mut pairs, Rule::alias, |pair| parse_alias(pair, pc))?;
|
|
|
|
let name = parse_name(pairs.next().unwrap(), pc)?;
|
2020-09-08 08:30:29 +00:00
|
|
|
let arguments = parse_if_rule(&mut pairs, Rule::arguments, |pair| {
|
|
|
|
parse_arguments(pair, pc)
|
|
|
|
})?;
|
2020-09-08 08:21:27 +00:00
|
|
|
let directives = parse_opt_directives(&mut pairs, pc)?;
|
2020-09-08 08:30:29 +00:00
|
|
|
let selection_set = parse_if_rule(&mut pairs, Rule::selection_set, |pair| {
|
|
|
|
parse_selection_set(pair, pc)
|
|
|
|
})?;
|
2020-09-08 08:21:27 +00:00
|
|
|
|
|
|
|
debug_assert_eq!(pairs.next(), None);
|
|
|
|
|
|
|
|
Ok(Positioned::new(
|
|
|
|
Field {
|
|
|
|
alias,
|
|
|
|
name,
|
|
|
|
arguments: arguments.unwrap_or_default(),
|
|
|
|
directives,
|
|
|
|
selection_set: selection_set.unwrap_or_default(),
|
|
|
|
},
|
|
|
|
pos,
|
|
|
|
))
|
|
|
|
}
|
|
|
|
|
|
|
|
fn parse_alias(pair: Pair<Rule>, pc: &mut PositionCalculator) -> Result<Positioned<Name>> {
|
|
|
|
debug_assert_eq!(pair.as_rule(), Rule::alias);
|
|
|
|
parse_name(exactly_one(pair.into_inner()), pc)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn parse_fragment_spread(
|
|
|
|
pair: Pair<Rule>,
|
|
|
|
pc: &mut PositionCalculator,
|
|
|
|
) -> Result<Positioned<FragmentSpread>> {
|
|
|
|
debug_assert_eq!(pair.as_rule(), Rule::fragment_spread);
|
|
|
|
|
|
|
|
let pos = pc.step(&pair);
|
|
|
|
let mut pairs = pair.into_inner();
|
|
|
|
|
|
|
|
let fragment_name = parse_name(pairs.next().unwrap(), pc)?;
|
|
|
|
let directives = parse_opt_directives(&mut pairs, pc)?;
|
|
|
|
|
|
|
|
debug_assert_eq!(pairs.next(), None);
|
|
|
|
|
|
|
|
Ok(Positioned::new(
|
|
|
|
FragmentSpread {
|
|
|
|
fragment_name,
|
|
|
|
directives,
|
|
|
|
},
|
|
|
|
pos,
|
|
|
|
))
|
|
|
|
}
|
|
|
|
|
|
|
|
fn parse_inline_fragment(
|
|
|
|
pair: Pair<Rule>,
|
|
|
|
pc: &mut PositionCalculator,
|
|
|
|
) -> Result<Positioned<InlineFragment>> {
|
|
|
|
debug_assert_eq!(pair.as_rule(), Rule::inline_fragment);
|
|
|
|
|
|
|
|
let pos = pc.step(&pair);
|
|
|
|
let mut pairs = pair.into_inner();
|
|
|
|
|
2020-09-08 08:30:29 +00:00
|
|
|
let type_condition = parse_if_rule(&mut pairs, Rule::type_condition, |pair| {
|
|
|
|
parse_type_condition(pair, pc)
|
|
|
|
})?;
|
2020-09-08 08:21:27 +00:00
|
|
|
let directives = parse_opt_directives(&mut pairs, pc)?;
|
|
|
|
let selection_set = parse_selection_set(pairs.next().unwrap(), pc)?;
|
|
|
|
|
|
|
|
debug_assert_eq!(pairs.next(), None);
|
|
|
|
|
|
|
|
Ok(Positioned::new(
|
|
|
|
InlineFragment {
|
|
|
|
type_condition,
|
|
|
|
directives,
|
|
|
|
selection_set,
|
|
|
|
},
|
|
|
|
pos,
|
|
|
|
))
|
|
|
|
}
|
|
|
|
|
|
|
|
fn parse_fragment_definition(
|
|
|
|
pair: Pair<Rule>,
|
|
|
|
pc: &mut PositionCalculator,
|
|
|
|
) -> Result<Positioned<FragmentDefinition>> {
|
|
|
|
debug_assert_eq!(pair.as_rule(), Rule::fragment_definition);
|
|
|
|
|
|
|
|
let pos = pc.step(&pair);
|
|
|
|
let mut pairs = pair.into_inner();
|
|
|
|
|
|
|
|
let name = parse_name(pairs.next().unwrap(), pc)?;
|
|
|
|
let type_condition = parse_type_condition(pairs.next().unwrap(), pc)?;
|
|
|
|
let directives = parse_opt_directives(&mut pairs, pc)?;
|
|
|
|
let selection_set = parse_selection_set(pairs.next().unwrap(), pc)?;
|
|
|
|
|
|
|
|
debug_assert_eq!(pairs.next(), None);
|
|
|
|
|
|
|
|
Ok(Positioned::new(
|
|
|
|
FragmentDefinition {
|
|
|
|
name,
|
|
|
|
type_condition,
|
|
|
|
directives,
|
|
|
|
selection_set,
|
|
|
|
},
|
|
|
|
pos,
|
|
|
|
))
|
|
|
|
}
|
|
|
|
|
|
|
|
fn parse_type_condition(
|
|
|
|
pair: Pair<Rule>,
|
|
|
|
pc: &mut PositionCalculator,
|
|
|
|
) -> Result<Positioned<TypeCondition>> {
|
|
|
|
debug_assert_eq!(pair.as_rule(), Rule::type_condition);
|
|
|
|
|
|
|
|
let pos = pc.step(&pair);
|
|
|
|
Ok(Positioned::new(
|
|
|
|
TypeCondition {
|
|
|
|
on: parse_name(exactly_one(pair.into_inner()), pc)?,
|
|
|
|
},
|
|
|
|
pos,
|
|
|
|
))
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
|
|
|
use std::fs;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_parser() {
|
|
|
|
for entry in fs::read_dir("tests/executables").unwrap() {
|
|
|
|
if let Ok(entry) = entry {
|
2020-09-08 08:30:29 +00:00
|
|
|
GraphQLParser::parse(
|
|
|
|
Rule::executable_document,
|
|
|
|
&fs::read_to_string(entry.path()).unwrap(),
|
|
|
|
)
|
|
|
|
.unwrap();
|
2020-09-08 08:21:27 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_parser_ast() {
|
|
|
|
for entry in fs::read_dir("tests/executables").unwrap() {
|
|
|
|
if let Ok(entry) = entry {
|
|
|
|
parse_query(fs::read_to_string(entry.path()).unwrap()).unwrap();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_parse_overflowing_int() {
|
|
|
|
let query_ok = format!("mutation {{ add(big: {}) }} ", std::i32::MAX);
|
|
|
|
let query_overflow = format!("mutation {{ add(big: {}0000) }} ", std::i32::MAX);
|
|
|
|
assert!(parse_query(query_ok).is_ok());
|
|
|
|
assert!(parse_query(query_overflow).is_ok());
|
|
|
|
}
|
|
|
|
}
|