#![doc = include_str!("../RULES.md")]
#![doc = include_str!("../DEFINITIONS.md")]
#![warn(missing_docs)]
#![warn(rust_2018_idioms)]
#![warn(rust_2021_compatibility)]
#![warn(missing_debug_implementations)]
#![warn(clippy::missing_docs_in_private_items)]
#![warn(rustdoc::broken_intra_doc_links)]
use std::sync::LazyLock;
use dyn_clone::DynClone;
use strum::VariantArray;
use wdl_analysis::Example;
use wdl_analysis::Visitor;
use wdl_ast::SyntaxKind;
pub mod baseline;
mod config;
pub(crate) mod fix;
mod linter;
pub mod rules;
mod tags;
pub(crate) mod util;
pub use baseline::Baseline;
pub use baseline::BaselineEntry;
pub use baseline::BaselineMatcher;
pub use config::Config;
#[doc(hidden)]
pub use config::ConfigField;
pub use linter::*;
pub use tags::*;
pub use wdl_analysis as analysis;
pub use wdl_ast as ast;
pub const DEFINITIONS_TEXT: &str = include_str!("../DEFINITIONS.md");
pub static ALL_RULE_IDS: LazyLock<Vec<String>> = LazyLock::new(|| {
let mut ids: Vec<String> = rules(&Config::default())
.iter()
.map(|r| r.id().to_string())
.collect();
ids.sort();
ids
});
pub static ALL_TAG_NAMES: LazyLock<Vec<String>> =
LazyLock::new(|| ALL_TAGS.iter().map(|t| t.to_string()).collect());
pub static ALL_TAGS: LazyLock<Vec<Tag>> = LazyLock::new(|| {
let mut tags: Vec<Tag> = Tag::VARIANTS.to_vec();
tags.sort_by_cached_key(Tag::to_string);
tags
});
pub trait Rule: Visitor + DynClone {
fn id(&self) -> &'static str;
fn description(&self) -> &'static str;
fn explanation(&self) -> &'static str;
fn examples(&self) -> &'static [Example];
fn tags(&self) -> TagSet;
fn url(&self) -> Option<&'static str> {
None
}
fn exceptable_nodes(&self) -> Option<&'static [SyntaxKind]>;
fn related_rules(&self) -> &'static [&'static str];
}
dyn_clone::clone_trait_object!(Rule);
pub fn rules(config: &Config) -> Vec<Box<dyn Rule + Send + Sync>> {
let rules: Vec<Box<dyn Rule + Send + Sync>> = vec![
Box::<rules::DoubleQuotesRule>::default(),
Box::<rules::HereDocCommandsRule>::default(),
Box::new(rules::SnakeCaseRule::new(config)),
Box::<rules::RuntimeSectionRule>::default(),
Box::<rules::ParameterMetaMatchedRule>::default(),
Box::<rules::CommandSectionIndentationRule>::default(),
Box::<rules::ImportPlacementRule>::default(),
Box::<rules::PascalCaseRule>::default(),
Box::<rules::MetaSectionsRule>::default(),
Box::<rules::CallInputKeywordRule>::default(),
Box::<rules::SectionOrderingRule>::default(),
Box::<rules::DeprecatedObjectRule>::default(),
Box::<rules::MetaDescriptionRule>::default(),
Box::<rules::DeprecatedPlaceholderRule>::default(),
Box::new(rules::ExpectedRuntimeKeysRule::new(config)),
Box::<rules::EmptyDocCommentRule>::default(),
Box::<rules::DocMetaStringsRule>::default(),
Box::<rules::TodoCommentRule>::default(),
Box::<rules::MatchingOutputMetaRule<'_>>::default(),
Box::<rules::InputNameRule>::default(),
Box::<rules::OutputNameRule>::default(),
Box::new(rules::DeclarationNameRule::new(config)),
Box::<rules::RedundantNone>::default(),
Box::<rules::HostPathLiteralsRule>::default(),
Box::<rules::ContainerUriRule>::default(),
Box::<rules::RequirementsSectionRule>::default(),
Box::<rules::ExceptDirectiveValidRule>::default(),
Box::<rules::ParameterDescriptionRule>::default(),
Box::<rules::ConciseInputRule>::default(),
Box::<rules::ShellCheckRule>::default(),
Box::<rules::DescriptionLengthRule>::default(),
Box::<rules::DocCommentTabsRule>::default(),
Box::<rules::UnusedDocCommentsRule>::default(),
Box::<rules::DenyGlobStar>::default(),
Box::<rules::EmptyOutputs>::default(),
Box::new(rules::BashSetSyntax::new(config)),
Box::<rules::InlineInstall>::default(),
];
#[cfg(debug_assertions)]
{
use std::collections::HashSet;
use convert_case::Case;
use convert_case::Casing;
let mut lint_set = HashSet::new();
let analysis_set: HashSet<&str> =
HashSet::from_iter(analysis::rules().iter().map(|r| r.id()));
for r in &rules {
if r.id().to_case(Case::Pascal) != r.id() {
panic!("lint rule id `{id}` is not pascal case", id = r.id());
}
if !lint_set.insert(r.id()) {
panic!("duplicate rule id `{id}`", id = r.id());
}
if analysis_set.contains(r.id()) {
panic!("rule id `{id}` is in use by wdl-analysis", id = r.id());
}
let self_id = &r.id();
for related_id in r.related_rules() {
if related_id == self_id {
panic!(
"Rule `{self_id}` refers to itself in its related rules. This is not \
allowed."
);
}
}
}
}
rules
}