apollo_parser/parser/grammar/
ty.rs1use crate::parser::grammar::name;
2use crate::Parser;
3use crate::SyntaxKind;
4use crate::Token;
5use crate::TokenKind;
6use crate::S;
7use crate::T;
8
9pub(crate) fn ty(p: &mut Parser) {
28 match parse(p) {
29 Ok(_) => (),
30 Err(Some(token)) => p.err_at_token(&token, "expected a type"),
31 Err(None) => p.err("expected a type"),
32 }
33}
34
35fn parse<'a>(p: &mut Parser<'a>) -> Result<(), Option<Token<'a>>> {
41 let checkpoint = p.checkpoint_node();
42 match p.peek() {
43 Some(T!['[']) => {
44 let _guard = p.start_node(SyntaxKind::LIST_TYPE);
45 p.bump(S!['[']);
46
47 if p.recursion_limit.check_and_increment() {
48 p.limit_err("parser recursion limit reached");
49 return Ok(()); }
51 let result = parse(p);
52 p.recursion_limit.decrement();
53
54 if let Err(Some(token)) = result {
55 p.err_at_token(&token, "expected item type");
58 }
59 p.expect(T![']'], S![']']);
60 }
61 Some(TokenKind::Name) => {
62 let _guard = p.start_node(SyntaxKind::NAMED_TYPE);
63 let _name_node_guard = p.start_node(SyntaxKind::NAME);
64
65 let token = p.pop();
66 name::validate_name(token.data(), p);
67 p.push_token(SyntaxKind::IDENT, token);
68 }
69 Some(_) => return Err(Some(p.pop())),
70 None => return Err(None),
71 };
72
73 p.skip_ignored();
75
76 if let Some(T![!]) = p.peek() {
78 let _guard = checkpoint.wrap_node(SyntaxKind::NON_NULL_TYPE);
79
80 p.eat(S![!]);
81 }
82
83 p.skip_ignored();
87
88 Ok(())
89}
90
91pub(crate) fn named_type(p: &mut Parser) {
96 if let Some(TokenKind::Name) = p.peek() {
98 let _g = p.start_node(SyntaxKind::NAMED_TYPE);
99 name::name(p);
100 }
101}
102
103#[cfg(test)]
104mod test {
105 use crate::cst;
106 use crate::cst::CstNode;
107 use crate::Parser;
108
109 #[test]
110 fn it_parses_nested_wrapped_types_in_op_def_and_returns_matching_stringified_doc() {
111 let mutation = r#"
112mutation MyMutation($custId: [Int!]!) {
113 myMutation(custId: $custId)
114}"#;
115 let parser = Parser::new(mutation);
116 let cst = parser.parse();
117 assert!(cst.errors.is_empty());
118
119 let doc = cst.document();
120 assert_eq!(&mutation, &doc.source_string());
121
122 for definition in doc.definitions() {
123 if let cst::Definition::OperationDefinition(op_type) = definition {
124 for var in op_type
125 .variable_definitions()
126 .unwrap()
127 .variable_definitions()
128 {
129 if let cst::Type::NamedType(name) = var.ty().unwrap() {
130 assert_eq!(name.source_string(), "[Int!]!")
131 }
132 }
133 }
134 }
135 }
136
137 #[test]
138 fn stringified_cst_matches_input_with_deeply_nested_wrapped_types() {
139 let mutation = r#"
140mutation MyMutation($a: Int $b: [Int] $c: String! $d: [Int!]!
141
142 $e: String
143 $f: [String]
144 $g: String!
145 $h: [String!]!
146) {
147 myMutation(custId: $a)
148}"#;
149 let parser = Parser::new(mutation);
150 let cst = parser.parse();
151
152 let doc = cst.document();
153 assert_eq!(&mutation, &doc.source_string());
154 }
155
156 #[test]
157 fn stringified_cst_matches_input_with_deeply_nested_wrapped_types_with_commas() {
158 let mutation = r#"
159mutation MyMutation($a: Int, $b: [Int], $c: String!, $d: [Int!]!,
160
161 $e: String,
162 $f: [String],
163 $g: String!,
164 $h: [String!]!,
165) {
166 myMutation(custId: $a)
167}"#;
168 let parser = Parser::new(mutation);
169 let cst = parser.parse();
170
171 let doc = cst.document();
172 assert_eq!(&mutation, &doc.source_string());
173 }
174}