apollo_parser/parser/grammar/
directive.rs1use crate::parser::grammar::argument;
2use crate::parser::grammar::description;
3use crate::parser::grammar::input;
4use crate::parser::grammar::name;
5use crate::parser::grammar::value::Constness;
6use crate::Parser;
7use crate::SyntaxKind;
8use crate::TokenKind;
9use crate::S;
10use crate::T;
11use std::ops::ControlFlow;
12
13pub(crate) fn directive_definition(p: &mut Parser) {
18 let _g = p.start_node(SyntaxKind::DIRECTIVE_DEFINITION);
19
20 if let Some(TokenKind::StringValue) = p.peek() {
21 description::description(p);
22 }
23
24 if let Some("directive") = p.peek_data() {
25 p.bump(SyntaxKind::directive_KW);
26 }
27
28 match p.peek() {
29 Some(T![@]) => p.bump(S![@]),
30 _ => p.err("expected @ symbol"),
31 }
32 name::name(p);
33
34 if let Some(T!['(']) = p.peek() {
35 let _g = p.start_node(SyntaxKind::ARGUMENTS_DEFINITION);
36 p.bump(S!['(']);
37 if let Some(TokenKind::Name | TokenKind::StringValue) = p.peek() {
38 input::input_value_definition(p);
39 } else {
40 p.err("expected an Argument Definition");
41 }
42 p.peek_while(|p, kind| match kind {
43 TokenKind::Name | TokenKind::StringValue => {
44 input::input_value_definition(p);
45 ControlFlow::Continue(())
46 }
47 _ => ControlFlow::Break(()),
48 });
49 p.expect(T![')'], S![')']);
50 }
51
52 if let Some(node) = p.peek_data() {
53 if node == "repeatable" {
54 p.bump(SyntaxKind::repeatable_KW);
55 }
56 }
57
58 if let Some(node) = p.peek_data() {
59 match node {
60 "on" => p.bump(SyntaxKind::on_KW),
61 _ => p.err("expected Directive Locations"),
62 }
63 }
64
65 if let Some(TokenKind::Name | T![|]) = p.peek() {
66 let _g = p.start_node(SyntaxKind::DIRECTIVE_LOCATIONS);
67 directive_locations(p);
68 } else {
69 p.err("expected valid Directive Location");
70 }
71}
72
73fn directive_location(p: &mut Parser) {
82 let Some(token) = p.peek_token() else {
83 return;
84 };
85
86 if token.kind == TokenKind::Name {
87 match token.data {
88 "QUERY" => {
89 let _g = p.start_node(SyntaxKind::DIRECTIVE_LOCATION);
90 p.bump(SyntaxKind::QUERY_KW);
91 }
92 "MUTATION" => {
93 let _g = p.start_node(SyntaxKind::DIRECTIVE_LOCATION);
94 p.bump(SyntaxKind::MUTATION_KW);
95 }
96 "SUBSCRIPTION" => {
97 let _g = p.start_node(SyntaxKind::DIRECTIVE_LOCATION);
98 p.bump(SyntaxKind::SUBSCRIPTION_KW);
99 }
100 "FIELD" => {
101 let _g = p.start_node(SyntaxKind::DIRECTIVE_LOCATION);
102 p.bump(SyntaxKind::FIELD_KW);
103 }
104 "FRAGMENT_DEFINITION" => {
105 let _g = p.start_node(SyntaxKind::DIRECTIVE_LOCATION);
106 p.bump(SyntaxKind::FRAGMENT_DEFINITION_KW);
107 }
108 "FRAGMENT_SPREAD" => {
109 let _g = p.start_node(SyntaxKind::DIRECTIVE_LOCATION);
110 p.bump(SyntaxKind::FRAGMENT_SPREAD_KW);
111 }
112 "INLINE_FRAGMENT" => {
113 let _g = p.start_node(SyntaxKind::DIRECTIVE_LOCATION);
114 p.bump(SyntaxKind::INLINE_FRAGMENT_KW);
115 }
116 "VARIABLE_DEFINITION" => {
117 let _g = p.start_node(SyntaxKind::DIRECTIVE_LOCATION);
118 p.bump(SyntaxKind::VARIABLE_DEFINITION_KW);
119 }
120 "SCHEMA" => {
121 let _g = p.start_node(SyntaxKind::DIRECTIVE_LOCATION);
122 p.bump(SyntaxKind::SCHEMA_KW);
123 }
124 "SCALAR" => {
125 let _g = p.start_node(SyntaxKind::DIRECTIVE_LOCATION);
126 p.bump(SyntaxKind::SCALAR_KW);
127 }
128 "OBJECT" => {
129 let _g = p.start_node(SyntaxKind::DIRECTIVE_LOCATION);
130 p.bump(SyntaxKind::OBJECT_KW);
131 }
132 "FIELD_DEFINITION" => {
133 let _g = p.start_node(SyntaxKind::DIRECTIVE_LOCATION);
134 p.bump(SyntaxKind::FIELD_DEFINITION_KW);
135 }
136 "ARGUMENT_DEFINITION" => {
137 let _g = p.start_node(SyntaxKind::DIRECTIVE_LOCATION);
138 p.bump(SyntaxKind::ARGUMENT_DEFINITION_KW);
139 }
140 "INTERFACE" => {
141 let _g = p.start_node(SyntaxKind::DIRECTIVE_LOCATION);
142 p.bump(SyntaxKind::INTERFACE_KW);
143 }
144 "UNION" => {
145 let _g = p.start_node(SyntaxKind::DIRECTIVE_LOCATION);
146 p.bump(SyntaxKind::UNION_KW);
147 }
148 "ENUM" => {
149 let _g = p.start_node(SyntaxKind::DIRECTIVE_LOCATION);
150 p.bump(SyntaxKind::ENUM_KW);
151 }
152 "ENUM_VALUE" => {
153 let _g = p.start_node(SyntaxKind::DIRECTIVE_LOCATION);
154 p.bump(SyntaxKind::ENUM_VALUE_KW);
155 }
156 "INPUT_OBJECT" => {
157 let _g = p.start_node(SyntaxKind::DIRECTIVE_LOCATION);
158 p.bump(SyntaxKind::INPUT_OBJECT_KW);
159 }
160 "INPUT_FIELD_DEFINITION" => {
161 let _g = p.start_node(SyntaxKind::DIRECTIVE_LOCATION);
162 p.bump(SyntaxKind::INPUT_FIELD_DEFINITION_KW);
163 }
164 _ => {
165 p.err("expected valid Directive Location");
166 }
167 }
168 } else {
169 p.err("expected Directive Location");
170 }
171}
172
173pub(crate) fn directive_locations(p: &mut Parser) {
179 p.parse_separated_list(T![|], S![|], directive_location);
180}
181
182pub(crate) fn directive(p: &mut Parser, constness: Constness) {
187 let _g = p.start_node(SyntaxKind::DIRECTIVE);
188
189 p.expect(T![@], S![@]);
190 name::name(p);
191
192 if let Some(T!['(']) = p.peek() {
193 argument::arguments(p, constness);
194 }
195}
196
197pub(crate) fn directives(p: &mut Parser, constness: Constness) {
202 let _g = p.start_node(SyntaxKind::DIRECTIVES);
203 p.peek_while_kind(T![@], |p| {
204 directive(p, constness);
205 });
206}
207
208#[cfg(test)]
209mod tests {
210 use super::*;
211 use crate::cst;
212
213 #[test]
214 fn it_can_access_repeatable_kw_on_directive_definition() {
215 let schema = r#"
216directive @example(isTreat: Boolean, treatKind: String) repeatable on FIELD | MUTATION
217 "#;
218 let parser = Parser::new(schema);
219 let cst = parser.parse();
220
221 assert!(cst.errors.is_empty());
222
223 let document = cst.document();
224 for definition in document.definitions() {
225 if let cst::Definition::DirectiveDefinition(dir_def) = definition {
226 assert_eq!(
227 dir_def.repeatable_token().unwrap().kind(),
228 SyntaxKind::repeatable_KW
229 );
230 return;
231 }
232 }
233 panic!("Expected CST to have a Directive Definition");
234 }
235
236 #[test]
237 fn it_can_access_directive_location_on_directive_definition() {
238 let schema = r#"
239directive @example on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT
240 "#;
241 let parser = Parser::new(schema);
242 let cst = parser.parse();
243 assert!(cst.errors.is_empty());
244
245 let document = cst.document();
246 for definition in document.definitions() {
247 if let cst::Definition::DirectiveDefinition(dir_def) = definition {
248 let dir_locations: Vec<String> = dir_def
249 .directive_locations()
250 .unwrap()
251 .directive_locations()
252 .map(|loc| loc.text().unwrap().to_string())
253 .collect();
254 assert_eq!(
255 dir_locations,
256 ["FIELD", "FRAGMENT_SPREAD", "INLINE_FRAGMENT"]
257 );
258 return;
259 }
260 }
261 panic!("Expected CST to have a Directive Definition");
262 }
263
264 #[test]
265 fn it_can_access_multiline_directive_location_on_directive_definition() {
266 let schema = r#"
267directive @example on
268| FIELD
269| FRAGMENT_SPREAD
270| INLINE_FRAGMENT
271 "#;
272 let parser = Parser::new(schema);
273 let cst = parser.parse();
274 assert!(cst.errors.is_empty());
275
276 let document = cst.document();
277 for definition in document.definitions() {
278 if let cst::Definition::DirectiveDefinition(dir_def) = definition {
279 let dir_locations: Vec<String> = dir_def
280 .directive_locations()
281 .unwrap()
282 .directive_locations()
283 .map(|loc| loc.text().unwrap().to_string())
284 .collect();
285 assert_eq!(
286 dir_locations,
287 ["FIELD", "FRAGMENT_SPREAD", "INLINE_FRAGMENT"]
288 );
289 return;
290 }
291 }
292 panic!("Expected CST to have a Directive Definition");
293 }
294
295 #[test]
296 fn it_can_access_multiline_directive_location_on_directive_definition_with_error() {
297 let schema = r#"
298directive @example on
299| FIELD
300| FRAGMENT_SPREAD
301| INLINE_FRAGMENT |
302 "#;
303 let parser = Parser::new(schema);
304 let cst = parser.parse();
305 assert!(!cst.errors.is_empty());
306
307 let schema = r#"
308directive @example on
309|| FIELD
310| FRAGMENT_SPREAD
311| INLINE_FRAGMENT
312 "#;
313 let parser = Parser::new(schema);
314 let cst = parser.parse();
315 assert!(!cst.errors.is_empty());
316
317 let schema = r#"
318directive @example on
319| FIELD
320|| FRAGMENT_SPREAD
321| INLINE_FRAGMENT
322 "#;
323 let parser = Parser::new(schema);
324 let cst = parser.parse();
325 assert!(!cst.errors.is_empty());
326 }
327}