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