1use wdl_ast::Comment;
4use wdl_ast::Diagnostic;
5use wdl_ast::SupportedVersion;
6use wdl_ast::SyntaxElement;
7use wdl_ast::SyntaxKind;
8use wdl_ast::VersionStatement;
9use wdl_ast::Whitespace;
10use wdl_ast::v1;
11
12use crate::Config;
13use crate::SyntaxNodeExt;
14use crate::VisitReason;
15use crate::Visitor;
16use crate::document::Document;
17
18mod counts;
19mod env;
20mod exprs;
21mod imports;
22mod keys;
23mod numbers;
24mod requirements;
25mod strings;
26mod version;
27
28#[derive(Debug, Default)]
33pub struct Diagnostics(pub(crate) Vec<Diagnostic>);
34
35impl Diagnostics {
36 pub fn add(&mut self, diagnostic: Diagnostic) {
38 self.0.push(diagnostic);
39 }
40
41 pub fn exceptable_add(
46 &mut self,
47 diagnostic: Diagnostic,
48 element: SyntaxElement,
49 exceptable_nodes: &Option<&'static [SyntaxKind]>,
50 ) {
51 if let Some(rule) = diagnostic.rule() {
52 for node in element.ancestors().filter(|node| {
53 exceptable_nodes
54 .as_ref()
55 .is_none_or(|nodes| nodes.contains(&node.kind()))
56 }) {
57 if node.is_rule_excepted(rule) {
58 return;
60 }
61 }
62 }
63
64 self.add(diagnostic);
65 }
66
67 pub fn extend(&mut self, diagnostics: Diagnostics) {
69 self.0.extend(diagnostics.0);
70 }
71
72 pub fn is_empty(&self) -> bool {
74 self.0.is_empty()
75 }
76
77 pub fn sort(&mut self) {
79 self.0.sort();
80 }
81}
82
83#[allow(missing_debug_implementations)]
89pub struct Validator {
90 visitors: Vec<Box<dyn Visitor>>,
92}
93
94impl Validator {
95 pub const fn empty() -> Self {
97 Self {
98 visitors: Vec::new(),
99 }
100 }
101
102 pub fn add_visitor<V: Visitor + 'static>(&mut self, visitor: V) {
104 self.visitors.push(Box::new(visitor));
105 }
106
107 pub fn add_visitors(&mut self, visitors: impl IntoIterator<Item = Box<dyn Visitor>>) {
109 self.visitors.extend(visitors)
110 }
111
112 pub fn validate(
115 &mut self,
116 document: &Document,
117 config: &Config,
118 ) -> Result<(), Vec<Diagnostic>> {
119 let mut diagnostics = Diagnostics::default();
120 self.register(config);
121 document.visit(&mut diagnostics, self);
122
123 self.reset();
124
125 if diagnostics.is_empty() {
126 Ok(())
127 } else {
128 diagnostics.sort();
129 Err(diagnostics.0)
130 }
131 }
132}
133
134impl Default for Validator {
135 fn default() -> Self {
137 Self {
138 visitors: vec![
139 Box::new(strings::LiteralTextVisitor),
140 Box::<counts::CountingVisitor>::default(),
141 Box::<keys::UniqueKeysVisitor>::default(),
142 Box::<numbers::NumberVisitor>::default(),
143 Box::<version::VersionVisitor>::default(),
144 Box::<requirements::RequirementsVisitor>::default(),
145 Box::<exprs::ScopedExprVisitor>::default(),
146 Box::<imports::ImportsVisitor>::default(),
147 Box::<env::EnvVisitor>::default(),
148 ],
149 }
150 }
151}
152
153impl Visitor for Validator {
154 fn register(&mut self, config: &crate::Config) {
155 for visitor in self.visitors.iter_mut() {
156 visitor.register(config);
157 }
158 }
159
160 fn reset(&mut self) {
161 for visitor in self.visitors.iter_mut() {
162 visitor.reset();
163 }
164 }
165
166 fn document(
167 &mut self,
168 diagnostics: &mut Diagnostics,
169 reason: VisitReason,
170 doc: &Document,
171 version: SupportedVersion,
172 ) {
173 for visitor in self.visitors.iter_mut() {
174 visitor.document(diagnostics, reason, doc, version);
175 }
176 }
177
178 fn whitespace(&mut self, diagnostics: &mut Diagnostics, whitespace: &Whitespace) {
179 for visitor in self.visitors.iter_mut() {
180 visitor.whitespace(diagnostics, whitespace);
181 }
182 }
183
184 fn comment(&mut self, diagnostics: &mut Diagnostics, comment: &Comment) {
185 for visitor in self.visitors.iter_mut() {
186 visitor.comment(diagnostics, comment);
187 }
188 }
189
190 fn version_statement(
191 &mut self,
192 diagnostics: &mut Diagnostics,
193 reason: VisitReason,
194 stmt: &VersionStatement,
195 ) {
196 for visitor in self.visitors.iter_mut() {
197 visitor.version_statement(diagnostics, reason, stmt);
198 }
199 }
200
201 fn import_statement(
202 &mut self,
203 diagnostics: &mut Diagnostics,
204 reason: VisitReason,
205 stmt: &v1::ImportStatement,
206 ) {
207 for visitor in self.visitors.iter_mut() {
208 visitor.import_statement(diagnostics, reason, stmt);
209 }
210 }
211
212 fn struct_definition(
213 &mut self,
214 diagnostics: &mut Diagnostics,
215 reason: VisitReason,
216 def: &v1::StructDefinition,
217 ) {
218 for visitor in self.visitors.iter_mut() {
219 visitor.struct_definition(diagnostics, reason, def);
220 }
221 }
222
223 fn enum_definition(
224 &mut self,
225 diagnostics: &mut Diagnostics,
226 reason: VisitReason,
227 def: &v1::EnumDefinition,
228 ) {
229 for visitor in self.visitors.iter_mut() {
230 visitor.enum_definition(diagnostics, reason, def);
231 }
232 }
233
234 fn task_definition(
235 &mut self,
236 diagnostics: &mut Diagnostics,
237 reason: VisitReason,
238 task: &v1::TaskDefinition,
239 ) {
240 for visitor in self.visitors.iter_mut() {
241 visitor.task_definition(diagnostics, reason, task);
242 }
243 }
244
245 fn workflow_definition(
246 &mut self,
247 diagnostics: &mut Diagnostics,
248 reason: VisitReason,
249 workflow: &v1::WorkflowDefinition,
250 ) {
251 for visitor in self.visitors.iter_mut() {
252 visitor.workflow_definition(diagnostics, reason, workflow);
253 }
254 }
255
256 fn input_section(
257 &mut self,
258 diagnostics: &mut Diagnostics,
259 reason: VisitReason,
260 section: &v1::InputSection,
261 ) {
262 for visitor in self.visitors.iter_mut() {
263 visitor.input_section(diagnostics, reason, section);
264 }
265 }
266
267 fn output_section(
268 &mut self,
269 diagnostics: &mut Diagnostics,
270 reason: VisitReason,
271 section: &v1::OutputSection,
272 ) {
273 for visitor in self.visitors.iter_mut() {
274 visitor.output_section(diagnostics, reason, section);
275 }
276 }
277
278 fn command_section(
279 &mut self,
280 diagnostics: &mut Diagnostics,
281 reason: VisitReason,
282 section: &v1::CommandSection,
283 ) {
284 for visitor in self.visitors.iter_mut() {
285 visitor.command_section(diagnostics, reason, section);
286 }
287 }
288
289 fn command_text(&mut self, diagnostics: &mut Diagnostics, text: &v1::CommandText) {
290 for visitor in self.visitors.iter_mut() {
291 visitor.command_text(diagnostics, text);
292 }
293 }
294
295 fn requirements_section(
296 &mut self,
297 diagnostics: &mut Diagnostics,
298 reason: VisitReason,
299 section: &v1::RequirementsSection,
300 ) {
301 for visitor in self.visitors.iter_mut() {
302 visitor.requirements_section(diagnostics, reason, section);
303 }
304 }
305
306 fn task_hints_section(
307 &mut self,
308 diagnostics: &mut Diagnostics,
309 reason: VisitReason,
310 section: &v1::TaskHintsSection,
311 ) {
312 for visitor in self.visitors.iter_mut() {
313 visitor.task_hints_section(diagnostics, reason, section);
314 }
315 }
316
317 fn workflow_hints_section(
318 &mut self,
319 diagnostics: &mut Diagnostics,
320 reason: VisitReason,
321 section: &v1::WorkflowHintsSection,
322 ) {
323 for visitor in self.visitors.iter_mut() {
324 visitor.workflow_hints_section(diagnostics, reason, section);
325 }
326 }
327
328 fn runtime_section(
329 &mut self,
330 diagnostics: &mut Diagnostics,
331 reason: VisitReason,
332 section: &v1::RuntimeSection,
333 ) {
334 for visitor in self.visitors.iter_mut() {
335 visitor.runtime_section(diagnostics, reason, section);
336 }
337 }
338
339 fn runtime_item(
340 &mut self,
341 diagnostics: &mut Diagnostics,
342 reason: VisitReason,
343 item: &v1::RuntimeItem,
344 ) {
345 for visitor in self.visitors.iter_mut() {
346 visitor.runtime_item(diagnostics, reason, item);
347 }
348 }
349
350 fn metadata_section(
351 &mut self,
352 diagnostics: &mut Diagnostics,
353 reason: VisitReason,
354 section: &v1::MetadataSection,
355 ) {
356 for visitor in self.visitors.iter_mut() {
357 visitor.metadata_section(diagnostics, reason, section);
358 }
359 }
360
361 fn parameter_metadata_section(
362 &mut self,
363 diagnostics: &mut Diagnostics,
364 reason: VisitReason,
365 section: &v1::ParameterMetadataSection,
366 ) {
367 for visitor in self.visitors.iter_mut() {
368 visitor.parameter_metadata_section(diagnostics, reason, section);
369 }
370 }
371
372 fn metadata_object(
373 &mut self,
374 diagnostics: &mut Diagnostics,
375 reason: VisitReason,
376 object: &v1::MetadataObject,
377 ) {
378 for visitor in self.visitors.iter_mut() {
379 visitor.metadata_object(diagnostics, reason, object);
380 }
381 }
382
383 fn metadata_object_item(
384 &mut self,
385 diagnostics: &mut Diagnostics,
386 reason: VisitReason,
387 item: &v1::MetadataObjectItem,
388 ) {
389 for visitor in self.visitors.iter_mut() {
390 visitor.metadata_object_item(diagnostics, reason, item);
391 }
392 }
393
394 fn metadata_array(
395 &mut self,
396 diagnostics: &mut Diagnostics,
397 reason: VisitReason,
398 item: &v1::MetadataArray,
399 ) {
400 for visitor in self.visitors.iter_mut() {
401 visitor.metadata_array(diagnostics, reason, item);
402 }
403 }
404
405 fn unbound_decl(
406 &mut self,
407 diagnostics: &mut Diagnostics,
408 reason: VisitReason,
409 decl: &v1::UnboundDecl,
410 ) {
411 for visitor in self.visitors.iter_mut() {
412 visitor.unbound_decl(diagnostics, reason, decl);
413 }
414 }
415
416 fn bound_decl(
417 &mut self,
418 diagnostics: &mut Diagnostics,
419 reason: VisitReason,
420 decl: &v1::BoundDecl,
421 ) {
422 for visitor in self.visitors.iter_mut() {
423 visitor.bound_decl(diagnostics, reason, decl);
424 }
425 }
426
427 fn expr(&mut self, diagnostics: &mut Diagnostics, reason: VisitReason, expr: &v1::Expr) {
428 for visitor in self.visitors.iter_mut() {
429 visitor.expr(diagnostics, reason, expr);
430 }
431 }
432
433 fn string_text(&mut self, diagnostics: &mut Diagnostics, text: &v1::StringText) {
434 for visitor in self.visitors.iter_mut() {
435 visitor.string_text(diagnostics, text);
436 }
437 }
438
439 fn placeholder(
440 &mut self,
441 diagnostics: &mut Diagnostics,
442 reason: VisitReason,
443 placeholder: &v1::Placeholder,
444 ) {
445 for visitor in self.visitors.iter_mut() {
446 visitor.placeholder(diagnostics, reason, placeholder);
447 }
448 }
449
450 fn conditional_statement(
451 &mut self,
452 diagnostics: &mut Diagnostics,
453 reason: VisitReason,
454 stmt: &v1::ConditionalStatement,
455 ) {
456 for visitor in self.visitors.iter_mut() {
457 visitor.conditional_statement(diagnostics, reason, stmt);
458 }
459 }
460
461 fn scatter_statement(
462 &mut self,
463 diagnostics: &mut Diagnostics,
464 reason: VisitReason,
465 stmt: &v1::ScatterStatement,
466 ) {
467 for visitor in self.visitors.iter_mut() {
468 visitor.scatter_statement(diagnostics, reason, stmt);
469 }
470 }
471
472 fn call_statement(
473 &mut self,
474 diagnostics: &mut Diagnostics,
475 reason: VisitReason,
476 stmt: &v1::CallStatement,
477 ) {
478 for visitor in self.visitors.iter_mut() {
479 visitor.call_statement(diagnostics, reason, stmt);
480 }
481 }
482}