use crate::parser::grammar::description;
use crate::parser::grammar::directive;
use crate::parser::grammar::name;
use crate::parser::grammar::selection;
use crate::parser::grammar::value::Constness;
use crate::parser::grammar::variable;
use crate::Parser;
use crate::SyntaxKind;
use crate::TokenKind;
use crate::T;
pub(crate) fn operation_definition(p: &mut Parser) {
let has_description =
p.executable_descriptions_allowed() && matches!(p.peek(), Some(TokenKind::StringValue));
match p.peek() {
Some(TokenKind::Name) => {
let _g = p.start_node(SyntaxKind::OPERATION_DEFINITION);
named_operation_definition(p);
}
Some(T!['{']) => {
let _g = p.start_node(SyntaxKind::OPERATION_DEFINITION);
selection::selection_set(p)
}
Some(TokenKind::StringValue) if has_description => {
let _g = p.start_node(SyntaxKind::OPERATION_DEFINITION);
description::description(p);
match p.peek() {
Some(TokenKind::Name) => named_operation_definition(p),
Some(T!['{']) => {
p.err("a Description is not allowed on a shorthand operation");
selection::selection_set(p)
}
_ => p.err_and_pop("expected an Operation Type or a Selection Set"),
}
}
_ => p.err_and_pop("expected an Operation Type or a Selection Set"),
}
}
fn named_operation_definition(p: &mut Parser) {
operation_type(p);
if let Some(TokenKind::Name) = p.peek() {
name::name(p);
}
if let Some(T!['(']) = p.peek() {
variable::variable_definitions(p)
}
if let Some(T![@]) = p.peek() {
directive::directives(p, Constness::NotConst);
}
match p.peek() {
Some(T!['{']) => selection::selection_set(p),
_ => p.err_and_pop("expected a Selection Set"),
}
}
pub(crate) fn operation_type(p: &mut Parser) {
if let Some(node) = p.peek_data() {
let _g = p.start_node(SyntaxKind::OPERATION_TYPE);
match node {
"query" => p.bump(SyntaxKind::query_KW),
"subscription" => p.bump(SyntaxKind::subscription_KW),
"mutation" => p.bump(SyntaxKind::mutation_KW),
_ => p.err_and_pop("expected either a 'mutation', a 'query', or a 'subscription'"),
}
}
}
#[cfg(test)]
mod test {
use crate::cst;
use crate::Parser;
#[test]
fn it_continues_parsing_when_operation_definition_starts_with_description() {
let input = "\"description\"{}";
let parser = Parser::new(input);
let cst = parser.parse();
assert_eq!(cst.errors().len(), 2);
assert_eq!(cst.document().definitions().count(), 1);
}
#[test]
fn it_parses_operation_description_when_enabled() {
let input = "\"Query description\"\nquery MyQuery { field }";
let cst = Parser::new(input)
.allow_executable_descriptions(true)
.parse();
assert_eq!(cst.errors().len(), 0);
let def = cst.document().definitions().next().unwrap();
let cst::Definition::OperationDefinition(op) = def else {
panic!("expected an OperationDefinition");
};
assert!(op.description().is_some());
}
}