1#![doc = include_str!("../RULES.md")]
27#![warn(missing_docs)]
28#![warn(rust_2018_idioms)]
29#![warn(rust_2021_compatibility)]
30#![warn(missing_debug_implementations)]
31#![warn(clippy::missing_docs_in_private_items)]
32#![warn(rustdoc::broken_intra_doc_links)]
33
34use std::collections::HashSet;
35
36use wdl_ast::Direction;
37use wdl_ast::SyntaxKind;
38use wdl_ast::SyntaxNode;
39use wdl_ast::SyntaxToken;
40
41mod analyzer;
42pub mod config;
43pub mod diagnostics;
44pub mod document;
45pub mod eval;
46mod graph;
47pub mod handlers;
48mod queue;
49mod rayon;
50mod rules;
51pub mod stdlib;
52pub mod types;
53mod validation;
54mod visitor;
55
56pub use analyzer::*;
57pub use config::Config;
58pub use config::DiagnosticsConfig;
59pub use config::FeatureFlags;
60pub use document::Document;
61pub use rules::*;
62pub use validation::*;
63pub use visitor::*;
64
65pub const EXCEPT_COMMENT_PREFIX: &str = "#@ except:";
67
68pub trait SyntaxNodeExt {
70 fn except_comments(&self) -> impl Iterator<Item = SyntaxToken> + '_;
72
73 fn rule_exceptions(&self) -> HashSet<String>;
78
79 fn is_rule_excepted(&self, id: &str) -> bool;
81}
82
83impl SyntaxNodeExt for SyntaxNode {
84 fn except_comments(&self) -> impl Iterator<Item = SyntaxToken> + '_ {
85 self.siblings_with_tokens(Direction::Prev)
86 .skip(1)
87 .map_while(|s| {
88 if s.kind() == SyntaxKind::Whitespace || s.kind() == SyntaxKind::Comment {
89 s.into_token()
90 } else {
91 None
92 }
93 })
94 .filter(|t| t.kind() == SyntaxKind::Comment)
95 }
96
97 fn rule_exceptions(&self) -> HashSet<String> {
98 let mut set = HashSet::default();
99 for comment in self.except_comments() {
100 if let Some(ids) = comment.text().strip_prefix(EXCEPT_COMMENT_PREFIX) {
101 for id in ids.split(',') {
102 let id = id.trim();
103 set.insert(id.to_string());
104 }
105 }
106 }
107
108 set
109 }
110
111 fn is_rule_excepted(&self, id: &str) -> bool {
112 for comment in self.except_comments() {
113 if let Some(ids) = comment.text().strip_prefix(EXCEPT_COMMENT_PREFIX)
114 && ids.split(',').any(|i| i.trim() == id)
115 {
116 return true;
117 }
118 }
119
120 false
121 }
122}