use crate::parser::grammar::name;
use crate::Parser;
use crate::SyntaxKind;
use crate::Token;
use crate::TokenKind;
use crate::S;
use crate::T;
pub(crate) fn ty(p: &mut Parser) {
match parse(p) {
Ok(_) => (),
Err(Some(token)) => p.err_at_token(&token, "expected a type"),
Err(None) => p.err("expected a type"),
}
}
fn parse<'a>(p: &mut Parser<'a>) -> Result<(), Option<Token<'a>>> {
let checkpoint = p.checkpoint_node();
match p.peek() {
Some(T!['[']) => {
let _guard = p.start_node(SyntaxKind::LIST_TYPE);
p.bump(S!['[']);
if p.recursion_limit.check_and_increment() {
p.limit_err("parser recursion limit reached");
return Ok(()); }
let result = parse(p);
p.recursion_limit.decrement();
if let Err(Some(token)) = result {
p.err_at_token(&token, "expected item type");
}
p.expect(T![']'], S![']']);
}
Some(TokenKind::Name) => {
let _guard = p.start_node(SyntaxKind::NAMED_TYPE);
let _name_node_guard = p.start_node(SyntaxKind::NAME);
let token = p.pop();
name::validate_name(token.data(), p);
p.push_token(SyntaxKind::IDENT, token);
}
Some(_) => return Err(Some(p.pop())),
None => return Err(None),
};
p.skip_ignored();
if let Some(T![!]) = p.peek() {
let _guard = checkpoint.wrap_node(SyntaxKind::NON_NULL_TYPE);
p.eat(S![!]);
}
p.skip_ignored();
Ok(())
}
pub(crate) fn named_type(p: &mut Parser) {
if let Some(TokenKind::Name) = p.peek() {
let _g = p.start_node(SyntaxKind::NAMED_TYPE);
name::name(p);
}
}
#[cfg(test)]
mod test {
use crate::cst;
use crate::cst::CstNode;
use crate::Parser;
#[test]
fn it_parses_nested_wrapped_types_in_op_def_and_returns_matching_stringified_doc() {
let mutation = r#"
mutation MyMutation($custId: [Int!]!) {
myMutation(custId: $custId)
}"#;
let parser = Parser::new(mutation);
let cst = parser.parse();
assert!(cst.errors.is_empty());
let doc = cst.document();
assert_eq!(&mutation, &doc.source_string());
for definition in doc.definitions() {
if let cst::Definition::OperationDefinition(op_type) = definition {
for var in op_type
.variable_definitions()
.unwrap()
.variable_definitions()
{
if let cst::Type::NamedType(name) = var.ty().unwrap() {
assert_eq!(name.source_string(), "[Int!]!")
}
}
}
}
}
#[test]
fn stringified_cst_matches_input_with_deeply_nested_wrapped_types() {
let mutation = r#"
mutation MyMutation($a: Int $b: [Int] $c: String! $d: [Int!]!
$e: String
$f: [String]
$g: String!
$h: [String!]!
) {
myMutation(custId: $a)
}"#;
let parser = Parser::new(mutation);
let cst = parser.parse();
let doc = cst.document();
assert_eq!(&mutation, &doc.source_string());
}
#[test]
fn stringified_cst_matches_input_with_deeply_nested_wrapped_types_with_commas() {
let mutation = r#"
mutation MyMutation($a: Int, $b: [Int], $c: String!, $d: [Int!]!,
$e: String,
$f: [String],
$g: String!,
$h: [String!]!,
) {
myMutation(custId: $a)
}"#;
let parser = Parser::new(mutation);
let cst = parser.parse();
let doc = cst.document();
assert_eq!(&mutation, &doc.source_string());
}
}