//! Parsing module. //! //! This module's structure mirrors `types`. use std::collections::{hash_map, HashMap}; use pest::{ iterators::{Pair, Pairs}, Parser, }; use utils::*; use crate::{ pos::{PositionCalculator, Positioned}, types::*, Error, Result, }; mod executable; #[allow(clippy::redundant_static_lifetimes)] mod generated; mod service; mod utils; use async_graphql_value::{ConstValue, Name, Number, Value}; pub use executable::parse_query; use generated::Rule; pub use service::parse_schema; struct GraphQLParser; fn parse_operation_type( pair: Pair, pc: &mut PositionCalculator, ) -> Result> { debug_assert_eq!(pair.as_rule(), Rule::operation_type); let pos = pc.step(&pair); Ok(Positioned::new( match pair.as_str() { "query" => OperationType::Query, "mutation" => OperationType::Mutation, "subscription" => OperationType::Subscription, _ => unreachable!(), }, pos, )) } fn parse_default_value( pair: Pair, pc: &mut PositionCalculator, ) -> Result> { debug_assert_eq!(pair.as_rule(), Rule::default_value); parse_const_value(exactly_one(pair.into_inner()), pc) } fn parse_type(pair: Pair, pc: &mut PositionCalculator) -> Result> { debug_assert_eq!(pair.as_rule(), Rule::type_); Ok(Positioned::new( Type::new(pair.as_str()).unwrap(), pc.step(&pair), )) } fn parse_const_value( pair: Pair, pc: &mut PositionCalculator, ) -> Result> { debug_assert_eq!(pair.as_rule(), Rule::const_value); let pos = pc.step(&pair); let pair = exactly_one(pair.into_inner()); Ok(Positioned::new( match pair.as_rule() { Rule::number => ConstValue::Number(parse_number(pair, pc)?.node), Rule::string => ConstValue::String(parse_string(pair, pc)?.node), Rule::boolean => ConstValue::Boolean(parse_boolean(pair, pc)?.node), Rule::null => ConstValue::Null, Rule::enum_value => ConstValue::Enum(parse_enum_value(pair, pc)?.node), Rule::const_list => ConstValue::List( pair.into_inner() .map(|pair| Ok(parse_const_value(pair, pc)?.node)) .collect::>()?, ), Rule::const_object => ConstValue::Object( pair.into_inner() .map(|pair| { debug_assert_eq!(pair.as_rule(), Rule::const_object_field); let mut pairs = pair.into_inner(); let name = parse_name(pairs.next().unwrap(), pc)?; let value = parse_const_value(pairs.next().unwrap(), pc)?; debug_assert_eq!(pairs.next(), None); Ok((name.node, value.node)) }) .collect::>()?, ), _ => unreachable!(), }, pos, )) } fn parse_value(pair: Pair, pc: &mut PositionCalculator) -> Result> { debug_assert_eq!(pair.as_rule(), Rule::value); let pos = pc.step(&pair); let pair = exactly_one(pair.into_inner()); Ok(Positioned::new( match pair.as_rule() { Rule::variable => Value::Variable(parse_variable(pair, pc)?.node), Rule::number => Value::Number(parse_number(pair, pc)?.node), Rule::string => Value::String(parse_string(pair, pc)?.node), Rule::boolean => Value::Boolean(parse_boolean(pair, pc)?.node), Rule::null => Value::Null, Rule::enum_value => Value::Enum(parse_enum_value(pair, pc)?.node), Rule::list => Value::List( pair.into_inner() .map(|pair| Ok(parse_value(pair, pc)?.node)) .collect::>()?, ), Rule::object => Value::Object( pair.into_inner() .map(|pair| { debug_assert_eq!(pair.as_rule(), Rule::object_field); let mut pairs = pair.into_inner(); let name = parse_name(pairs.next().unwrap(), pc)?; let value = parse_value(pairs.next().unwrap(), pc)?; debug_assert_eq!(pairs.next(), None); Ok((name.node, value.node)) }) .collect::>()?, ), _ => unreachable!(), }, pos, )) } fn parse_variable(pair: Pair, pc: &mut PositionCalculator) -> Result> { debug_assert_eq!(pair.as_rule(), Rule::variable); parse_name(exactly_one(pair.into_inner()), pc) } fn parse_number(pair: Pair, pc: &mut PositionCalculator) -> Result> { debug_assert_eq!(pair.as_rule(), Rule::number); let pos = pc.step(&pair); Ok(Positioned::new( pair.as_str().parse().map_err(|err| Error::Syntax { message: format!("invalid number: {}", err), start: pos, end: None, })?, pos, )) } fn parse_string(pair: Pair, pc: &mut PositionCalculator) -> Result> { debug_assert_eq!(pair.as_rule(), Rule::string); let pos = pc.step(&pair); let pair = exactly_one(pair.into_inner()); Ok(Positioned::new( match pair.as_rule() { Rule::block_string_content => block_string_value(pair.as_str()), Rule::string_content => string_value(pair.as_str()), _ => unreachable!(), }, pos, )) } fn parse_boolean(pair: Pair, pc: &mut PositionCalculator) -> Result> { debug_assert_eq!(pair.as_rule(), Rule::boolean); let pos = pc.step(&pair); Ok(Positioned::new( match pair.as_str() { "true" => true, "false" => false, _ => unreachable!(), }, pos, )) } fn parse_enum_value(pair: Pair, pc: &mut PositionCalculator) -> Result> { debug_assert_eq!(pair.as_rule(), Rule::enum_value); parse_name(exactly_one(pair.into_inner()), pc) } fn parse_opt_const_directives<'a>( pairs: &mut Pairs<'a, Rule>, pc: &mut PositionCalculator, ) -> Result>> { Ok(parse_if_rule(pairs, Rule::const_directives, |pair| { parse_const_directives(pair, pc) })? .unwrap_or_default()) } fn parse_opt_directives<'a>( pairs: &mut Pairs<'a, Rule>, pc: &mut PositionCalculator, ) -> Result>> { Ok( parse_if_rule(pairs, Rule::directives, |pair| parse_directives(pair, pc))? .unwrap_or_default(), ) } fn parse_const_directives( pair: Pair, pc: &mut PositionCalculator, ) -> Result>> { debug_assert_eq!(pair.as_rule(), Rule::const_directives); pair.into_inner() .map(|pair| parse_const_directive(pair, pc)) .collect() } fn parse_directives( pair: Pair, pc: &mut PositionCalculator, ) -> Result>> { debug_assert_eq!(pair.as_rule(), Rule::directives); pair.into_inner() .map(|pair| parse_directive(pair, pc)) .collect() } fn parse_const_directive( pair: Pair, pc: &mut PositionCalculator, ) -> Result> { debug_assert_eq!(pair.as_rule(), Rule::const_directive); let pos = pc.step(&pair); let mut pairs = pair.into_inner(); let name = parse_name(pairs.next().unwrap(), pc)?; let arguments = parse_if_rule(&mut pairs, Rule::const_arguments, |pair| { parse_const_arguments(pair, pc) })?; debug_assert_eq!(pairs.next(), None); Ok(Positioned::new( ConstDirective { name, arguments: arguments.unwrap_or_default(), }, pos, )) } fn parse_directive(pair: Pair, pc: &mut PositionCalculator) -> Result> { debug_assert_eq!(pair.as_rule(), Rule::directive); let pos = pc.step(&pair); let mut pairs = pair.into_inner(); let name = parse_name(pairs.next().unwrap(), pc)?; let arguments = parse_if_rule(&mut pairs, Rule::arguments, |pair| { parse_arguments(pair, pc) })?; debug_assert_eq!(pairs.next(), None); Ok(Positioned::new( Directive { name, arguments: arguments.unwrap_or_default(), }, pos, )) } fn parse_const_arguments( pair: Pair, pc: &mut PositionCalculator, ) -> Result, Positioned)>> { debug_assert_eq!(pair.as_rule(), Rule::const_arguments); pair.into_inner() .map(|pair| { debug_assert_eq!(pair.as_rule(), Rule::const_argument); let mut pairs = pair.into_inner(); let name = parse_name(pairs.next().unwrap(), pc)?; let value = parse_const_value(pairs.next().unwrap(), pc)?; debug_assert_eq!(pairs.next(), None); Ok((name, value)) }) .collect() } fn parse_arguments( pair: Pair, pc: &mut PositionCalculator, ) -> Result, Positioned)>> { debug_assert_eq!(pair.as_rule(), Rule::arguments); pair.into_inner() .map(|pair| { debug_assert_eq!(pair.as_rule(), Rule::argument); let mut pairs = pair.into_inner(); let name = parse_name(pairs.next().unwrap(), pc)?; let value = parse_value(pairs.next().unwrap(), pc)?; debug_assert_eq!(pairs.next(), None); Ok((name, value)) }) .collect() } fn parse_name(pair: Pair, pc: &mut PositionCalculator) -> Result> { debug_assert_eq!(pair.as_rule(), Rule::name); Ok(Positioned::new(Name::new(pair.as_str()), pc.step(&pair))) } #[cfg(test)] mod tests { use super::*; #[test] fn test_number_lookahead_restrictions() { GraphQLParser::parse(Rule::const_list, "[123 abc]").unwrap(); GraphQLParser::parse(Rule::const_list, "[123.0123 abc]").unwrap(); GraphQLParser::parse(Rule::const_list, "[123.0123e7 abc]").unwrap(); GraphQLParser::parse(Rule::const_list, "[123.0123e77 abc]").unwrap(); assert!(GraphQLParser::parse(Rule::const_list, "[123abc]").is_err()); assert!(GraphQLParser::parse(Rule::const_list, "[123.0123abc]").is_err()); assert!(GraphQLParser::parse(Rule::const_list, "[123.0123e7abc]").is_err()); assert!(GraphQLParser::parse(Rule::const_list, "[123.0123e77abc]").is_err()); } }