use std::sync::Arc;
use tracing::warn;
use wdl_ast::Severity;
use wdl_ast::SupportedVersion;
use wdl_ast::SyntaxNode;
use crate::Exceptable as _;
use crate::Rule;
use crate::UNNECESSARY_FUNCTION_CALL;
use crate::UNUSED_CALL_RULE_ID;
use crate::UNUSED_DECL_RULE_ID;
use crate::UNUSED_IMPORT_RULE_ID;
use crate::UNUSED_INPUT_RULE_ID;
use crate::USING_FALLBACK_VERSION;
use crate::rules;
#[derive(Clone, PartialEq, Eq, serde::Deserialize, serde::Serialize)]
pub struct Config {
#[serde(flatten)]
inner: Arc<ConfigInner>,
}
impl std::fmt::Debug for Config {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Config")
.field("diagnostics", &self.inner.diagnostics)
.field("fallback_version", &self.inner.fallback_version)
.finish()
}
}
impl Default for Config {
fn default() -> Self {
Self {
inner: Arc::new(ConfigInner {
diagnostics: Default::default(),
fallback_version: None,
ignore_filename: None,
all_rules: Default::default(),
feature_flags: FeatureFlags::default(),
}),
}
}
}
impl Config {
pub fn diagnostics_config(&self) -> &DiagnosticsConfig {
&self.inner.diagnostics
}
pub fn fallback_version(&self) -> Option<SupportedVersion> {
self.inner.fallback_version
}
pub fn ignore_filename(&self) -> Option<&str> {
self.inner.ignore_filename.as_deref()
}
pub fn all_rules(&self) -> &[String] {
&self.inner.all_rules
}
pub fn feature_flags(&self) -> &FeatureFlags {
&self.inner.feature_flags
}
pub fn with_diagnostics_config(&self, diagnostics: DiagnosticsConfig) -> Self {
let mut inner = (*self.inner).clone();
inner.diagnostics = diagnostics;
Self {
inner: Arc::new(inner),
}
}
pub fn with_fallback_version(&self, fallback_version: Option<SupportedVersion>) -> Self {
let mut inner = (*self.inner).clone();
inner.fallback_version = fallback_version;
Self {
inner: Arc::new(inner),
}
}
pub fn with_ignore_filename(&self, filename: Option<String>) -> Self {
let mut inner = (*self.inner).clone();
inner.ignore_filename = filename;
Self {
inner: Arc::new(inner),
}
}
pub fn with_all_rules(&self, rules: Vec<String>) -> Self {
let mut inner = (*self.inner).clone();
inner.all_rules = rules;
Self {
inner: Arc::new(inner),
}
}
pub fn with_feature_flags(&self, feature_flags: FeatureFlags) -> Self {
let mut inner = (*self.inner).clone();
inner.feature_flags = feature_flags;
Self {
inner: Arc::new(inner),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, serde::Deserialize, serde::Serialize)]
struct ConfigInner {
#[serde(default)]
diagnostics: DiagnosticsConfig,
#[serde(default)]
fallback_version: Option<SupportedVersion>,
ignore_filename: Option<String>,
#[serde(default)]
all_rules: Vec<String>,
#[serde(default)]
feature_flags: FeatureFlags,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Deserialize, serde::Serialize)]
pub struct FeatureFlags {
#[serde(default = "default_wdl_1_3")]
wdl_1_3: bool,
}
fn default_wdl_1_3() -> bool {
true
}
impl Default for FeatureFlags {
fn default() -> Self {
Self { wdl_1_3: true }
}
}
impl FeatureFlags {
pub fn wdl_1_3(&self) -> bool {
self.wdl_1_3
}
#[deprecated(note = "WDL 1.3 is now enabled by default; this method is a no-op")]
pub fn with_wdl_1_3(self) -> Self {
self
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Deserialize, serde::Serialize)]
pub struct DiagnosticsConfig {
pub unused_import: Option<Severity>,
pub unused_input: Option<Severity>,
pub unused_declaration: Option<Severity>,
pub unused_call: Option<Severity>,
pub unnecessary_function_call: Option<Severity>,
pub using_fallback_version: Option<Severity>,
}
impl Default for DiagnosticsConfig {
fn default() -> Self {
Self::new(rules())
}
}
impl DiagnosticsConfig {
pub fn new<T: AsRef<dyn Rule>>(rules: impl IntoIterator<Item = T>) -> Self {
let mut unused_import = None;
let mut unused_input = None;
let mut unused_declaration = None;
let mut unused_call = None;
let mut unnecessary_function_call = None;
let mut using_fallback_version = None;
for rule in rules {
let rule = rule.as_ref();
match rule.id() {
UNUSED_IMPORT_RULE_ID => unused_import = Some(rule.severity()),
UNUSED_INPUT_RULE_ID => unused_input = Some(rule.severity()),
UNUSED_DECL_RULE_ID => unused_declaration = Some(rule.severity()),
UNUSED_CALL_RULE_ID => unused_call = Some(rule.severity()),
UNNECESSARY_FUNCTION_CALL => unnecessary_function_call = Some(rule.severity()),
USING_FALLBACK_VERSION => using_fallback_version = Some(rule.severity()),
unrecognized => {
warn!(unrecognized, "unrecognized rule");
if cfg!(test) {
panic!("unrecognized rule: {unrecognized}");
}
}
}
}
Self {
unused_import,
unused_input,
unused_declaration,
unused_call,
unnecessary_function_call,
using_fallback_version,
}
}
pub fn excepted_for_node(mut self, node: &SyntaxNode) -> Self {
let exceptions = node.rule_exceptions();
if exceptions.contains(UNUSED_IMPORT_RULE_ID) {
self.unused_import = None;
}
if exceptions.contains(UNUSED_INPUT_RULE_ID) {
self.unused_input = None;
}
if exceptions.contains(UNUSED_DECL_RULE_ID) {
self.unused_declaration = None;
}
if exceptions.contains(UNUSED_CALL_RULE_ID) {
self.unused_call = None;
}
if exceptions.contains(UNNECESSARY_FUNCTION_CALL) {
self.unnecessary_function_call = None;
}
if exceptions.contains(USING_FALLBACK_VERSION) {
self.using_fallback_version = None;
}
self
}
pub fn except_all() -> Self {
Self {
unused_import: None,
unused_input: None,
unused_declaration: None,
unused_call: None,
unnecessary_function_call: None,
using_fallback_version: None,
}
}
}