use std::collections::HashSet;
use std::path::Path;
use crate::cop::{CopConfig, EnabledState};
use super::globutil::glob_matches;
use super::standard::is_plugin_department;
use super::types::{DepartmentConfig, NewCopsPolicy};
use super::ResolvedConfig;
pub(crate) struct EnableInputs {
pub cop_state: EnabledState,
pub dept_state: EnabledState,
pub disabled_by_default: bool,
pub project_mentioned: bool,
pub project_enabled_dept: bool,
pub restore_ok: bool,
}
pub(crate) fn resolve_enabled_state(i: &EnableInputs) -> EnabledState {
if i.cop_state == EnabledState::True {
return resolve_true_cop(i);
}
if i.cop_state != EnabledState::Unset {
return i.cop_state;
}
if i.dept_state == EnabledState::False {
return EnabledState::False;
}
if i.dept_state == EnabledState::True {
return resolve_dept_true(i);
}
EnabledState::Unset
}
fn resolve_true_cop(i: &EnableInputs) -> EnabledState {
if !i.disabled_by_default && i.dept_state == EnabledState::False && !i.project_mentioned {
EnabledState::False
} else {
EnabledState::True
}
}
fn resolve_dept_true(i: &EnableInputs) -> EnabledState {
if !i.disabled_by_default || (i.project_enabled_dept && i.restore_ok) {
EnabledState::True
} else {
EnabledState::Unset
}
}
pub(crate) fn state_to_enabled(
state: EnabledState,
new_cops: NewCopsPolicy,
disabled_by_default: bool,
default_enabled: bool,
) -> bool {
match state {
EnabledState::False => false,
EnabledState::Pending => new_cops == NewCopsPolicy::Enable && !disabled_by_default,
EnabledState::Unset => !disabled_by_default && default_enabled,
EnabledState::True => true,
}
}
pub(crate) fn dept_has_known_cops(known: &HashSet<String>, dept: &str) -> bool {
known
.iter()
.any(|c| c.starts_with(dept) && c.as_bytes().get(dept.len()) == Some(&b'/'))
}
pub(crate) fn version_gate_disables(
cfg: &ResolvedConfig,
name: &str,
dept: &str,
config: Option<&CopConfig>,
unset_only: bool,
) -> bool {
if plugin_dept_unloaded(cfg, dept, config) {
return true;
}
if plugin_version_unknown(cfg, name, dept, config, unset_only) {
return true;
}
if core_version_unknown(cfg, name, dept, config, unset_only) {
return true;
}
false
}
fn user_configured(config: Option<&CopConfig>, unset_only: bool) -> bool {
match config {
None => false,
Some(c) if unset_only => c.enabled != EnabledState::Unset,
Some(c) => c.enabled == EnabledState::True,
}
}
fn plugin_dept_unloaded(cfg: &ResolvedConfig, dept: &str, config: Option<&CopConfig>) -> bool {
is_plugin_department(dept)
&& !cfg.require_departments.contains(dept)
&& config.is_none_or(|c| c.enabled != EnabledState::True)
}
fn plugin_version_unknown(
cfg: &ResolvedConfig,
name: &str,
dept: &str,
config: Option<&CopConfig>,
unset_only: bool,
) -> bool {
dept_has_known_cops(&cfg.require_known_cops, dept)
&& cfg.require_departments.contains(dept)
&& !cfg.require_known_cops.contains(name)
&& !user_configured(config, unset_only)
}
fn core_version_unknown(
cfg: &ResolvedConfig,
name: &str,
dept: &str,
config: Option<&CopConfig>,
unset_only: bool,
) -> bool {
!cfg.rubocop_known_cops.is_empty()
&& !is_plugin_department(dept)
&& !cfg.rubocop_known_cops.contains(name)
&& !user_configured(config, unset_only)
}
pub(crate) fn redundant_constant_base_blocked(
name: &str,
lint_constant_resolution_enabled: bool,
) -> bool {
name == "Style/RedundantConstantBase" && lint_constant_resolution_enabled
}
pub(crate) fn lint_constant_resolution_on(cfg: &ResolvedConfig) -> bool {
cfg.cop_configs
.get("Lint/ConstantResolution")
.is_some_and(|c| c.enabled == EnabledState::True)
}
pub(crate) fn effective_include<'a>(
config: Option<&'a CopConfig>,
dept: Option<&'a DepartmentConfig>,
defaults: &'a [&str],
) -> Vec<&'a str> {
match config {
Some(c) if !c.include.is_empty() => c.include.iter().map(|s| s.as_str()).collect(),
_ => match dept {
Some(dc) if !dc.include.is_empty() => dc.include.iter().map(|s| s.as_str()).collect(),
_ => defaults.to_vec(),
},
}
}
pub(crate) fn effective_exclude<'a>(
config: Option<&'a CopConfig>,
dept: Option<&'a DepartmentConfig>,
defaults: &'a [&str],
) -> Vec<&'a str> {
match config {
Some(c) if !c.exclude.is_empty() => c.exclude.iter().map(|s| s.as_str()).collect(),
_ => match dept {
Some(dc) if !dc.exclude.is_empty() => dc.exclude.iter().map(|s| s.as_str()).collect(),
_ => defaults.to_vec(),
},
}
}
pub(crate) fn path_matches_any(patterns: &[&str], path: &Path) -> bool {
patterns.iter().any(|pat| glob_matches(pat, path))
}
pub(crate) fn global_exclude_hits(cfg: &ResolvedConfig, path: &Path) -> bool {
cfg.global_excludes
.iter()
.any(|pattern| glob_matches(pattern, path))
}
impl ResolvedConfig {
pub(crate) fn enable_inputs(
&self,
name: &str,
dept: &str,
config: Option<&CopConfig>,
restore_ok: bool,
) -> EnableInputs {
EnableInputs {
cop_state: config.map(|c| c.enabled).unwrap_or(EnabledState::Unset),
dept_state: self
.department_configs
.get(dept)
.map(|dc| dc.enabled)
.unwrap_or(EnabledState::Unset),
disabled_by_default: self.disabled_by_default,
project_mentioned: self.project_mentioned_cops.contains(name),
project_enabled_dept: self.project_enabled_depts.contains(dept),
restore_ok,
}
}
}