use super::types::ComplexityMetric;
pub const STRICT_THRESHOLD: f64 = 8.0;
pub const DEFAULT_THRESHOLD: f64 = 15.0;
pub const LENIENT_THRESHOLD: f64 = 25.0;
pub const STRICT_THRESHOLD_CYCLOMATIC: f64 = 8.0;
pub const DEFAULT_THRESHOLD_CYCLOMATIC: f64 = 15.0;
pub const LENIENT_THRESHOLD_CYCLOMATIC: f64 = 25.0;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ThresholdPreset {
Strict,
Default,
Lenient,
}
impl ThresholdPreset {
pub fn threshold(self, metric: ComplexityMetric) -> f64 {
match (metric, self) {
(ComplexityMetric::Cognitive, Self::Strict) => STRICT_THRESHOLD,
(ComplexityMetric::Cognitive, Self::Default) => DEFAULT_THRESHOLD,
(ComplexityMetric::Cognitive, Self::Lenient) => LENIENT_THRESHOLD,
(ComplexityMetric::Cyclomatic, Self::Strict) => STRICT_THRESHOLD_CYCLOMATIC,
(ComplexityMetric::Cyclomatic, Self::Default) => DEFAULT_THRESHOLD_CYCLOMATIC,
(ComplexityMetric::Cyclomatic, Self::Lenient) => LENIENT_THRESHOLD_CYCLOMATIC,
}
}
}
pub fn is_valid_threshold(value: f64) -> bool {
value.is_finite() && value > 0.0
}
#[derive(Debug, Clone, PartialEq)]
pub struct ThresholdOverride {
pub pattern: String,
pub threshold: f64,
}
#[derive(Debug, Clone, PartialEq)]
pub struct ThresholdConfig {
pub global: f64,
pub overrides: Vec<ThresholdOverride>,
}
impl Default for ThresholdConfig {
fn default() -> Self {
Self {
global: DEFAULT_THRESHOLD,
overrides: Vec::new(),
}
}
}
impl ThresholdConfig {
pub fn has_overrides(&self) -> bool {
!self.overrides.is_empty()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn threshold_constants() {
assert_eq!(STRICT_THRESHOLD, 8.0);
assert_eq!(DEFAULT_THRESHOLD, 15.0);
assert_eq!(LENIENT_THRESHOLD, 25.0);
assert_eq!(STRICT_THRESHOLD_CYCLOMATIC, 8.0);
assert_eq!(DEFAULT_THRESHOLD_CYCLOMATIC, 15.0);
assert_eq!(LENIENT_THRESHOLD_CYCLOMATIC, 25.0);
}
#[test]
fn preset_to_threshold_is_metric_keyed() {
use ComplexityMetric::{Cognitive, Cyclomatic};
assert_eq!(ThresholdPreset::Strict.threshold(Cognitive), 8.0);
assert_eq!(ThresholdPreset::Default.threshold(Cognitive), 15.0);
assert_eq!(ThresholdPreset::Lenient.threshold(Cognitive), 25.0);
assert_eq!(ThresholdPreset::Strict.threshold(Cyclomatic), 8.0);
assert_eq!(ThresholdPreset::Default.threshold(Cyclomatic), 15.0);
assert_eq!(ThresholdPreset::Lenient.threshold(Cyclomatic), 25.0);
}
#[test]
fn default_config_uses_default_threshold() {
let config = ThresholdConfig::default();
assert_eq!(config.global, DEFAULT_THRESHOLD);
assert!(config.overrides.is_empty());
}
#[test]
fn has_overrides_false_when_empty() {
let config = ThresholdConfig::default();
assert!(!config.has_overrides());
}
#[test]
fn has_overrides_true_when_present() {
let config = ThresholdConfig {
global: DEFAULT_THRESHOLD,
overrides: vec![ThresholdOverride {
pattern: "domain/**".to_string(),
threshold: 5.0,
}],
};
assert!(config.has_overrides());
}
#[test]
fn is_valid_threshold_accepts_positive_finite() {
assert!(is_valid_threshold(1.0));
assert!(is_valid_threshold(0.001));
assert!(is_valid_threshold(DEFAULT_THRESHOLD));
assert!(is_valid_threshold(100.0));
}
#[test]
fn is_valid_threshold_rejects_invalid() {
assert!(!is_valid_threshold(0.0));
assert!(!is_valid_threshold(-1.0));
assert!(!is_valid_threshold(f64::NAN));
assert!(!is_valid_threshold(f64::INFINITY));
assert!(!is_valid_threshold(f64::NEG_INFINITY));
}
#[test]
fn threshold_override_equality() {
let a = ThresholdOverride {
pattern: "src/**".to_string(),
threshold: 10.0,
};
let b = ThresholdOverride {
pattern: "src/**".to_string(),
threshold: 10.0,
};
assert_eq!(a, b);
}
}