use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ThresholdConfig {
pub good: u32,
pub warning: u32,
pub high: u32,
}
impl Default for ThresholdConfig {
fn default() -> Self {
Self {
good: 10,
warning: 20, high: 30, }
}
}
impl ThresholdConfig {
pub fn normalize(&mut self) {
self.warning = self.warning.max(self.good);
self.high = self.high.max(self.warning);
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Thresholds {
pub cyclomatic: ThresholdConfig,
pub cognitive: ThresholdConfig,
pub function_length: ThresholdConfig,
pub nesting_depth: ThresholdConfig,
pub parameters: ThresholdConfig,
pub file_length: ThresholdConfig,
}
impl Default for Thresholds {
fn default() -> Self {
Self {
cyclomatic: ThresholdConfig {
good: 10,
warning: 20,
high: 30,
},
cognitive: ThresholdConfig {
good: 15,
warning: 30,
high: 60,
},
function_length: ThresholdConfig {
good: 50,
warning: 100,
high: 200,
},
nesting_depth: ThresholdConfig {
good: 4,
warning: 6,
high: 8,
},
parameters: ThresholdConfig {
good: 4,
warning: 6,
high: 10,
},
file_length: ThresholdConfig {
good: 300,
warning: 500,
high: 1000,
},
}
}
}
impl Thresholds {
pub fn new() -> Self {
Self::default()
}
pub fn strict() -> Self {
Self {
cyclomatic: ThresholdConfig {
good: 5,
warning: 10,
high: 20,
},
cognitive: ThresholdConfig {
good: 10,
warning: 15,
high: 30,
},
function_length: ThresholdConfig {
good: 25,
warning: 50,
high: 100,
},
nesting_depth: ThresholdConfig {
good: 3,
warning: 4,
high: 5,
},
parameters: ThresholdConfig {
good: 3,
warning: 4,
high: 6,
},
file_length: ThresholdConfig {
good: 200,
warning: 300,
high: 500,
},
}
}
pub fn lenient() -> Self {
Self {
cyclomatic: ThresholdConfig {
good: 15,
warning: 25,
high: 35,
},
cognitive: ThresholdConfig {
good: 20,
warning: 40,
high: 80,
},
function_length: ThresholdConfig {
good: 75,
warning: 150,
high: 300,
},
nesting_depth: ThresholdConfig {
good: 5,
warning: 7,
high: 10,
},
parameters: ThresholdConfig {
good: 5,
warning: 8,
high: 12,
},
file_length: ThresholdConfig {
good: 500,
warning: 750,
high: 1500,
},
}
}
pub fn from_toml(content: &str) -> Result<Self, String> {
toml::from_str(content).map_err(|e| format!("Failed to parse thresholds: {}", e))
}
pub fn check_cyclomatic(&self, value: u32) -> &'static str {
if value <= self.cyclomatic.good {
"good"
} else if value <= self.cyclomatic.warning {
"warning"
} else if value <= self.cyclomatic.high {
"high"
} else {
"critical"
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_threshold_defaults() {
let thresholds = Thresholds::default();
assert_eq!(thresholds.cyclomatic.good, 10);
assert_eq!(thresholds.cyclomatic.warning, 20);
}
#[test]
fn test_threshold_strict() {
let thresholds = Thresholds::strict();
assert!(thresholds.cyclomatic.good < Thresholds::default().cyclomatic.good);
}
#[test]
fn test_check_cyclomatic() {
let thresholds = Thresholds::default();
assert_eq!(thresholds.check_cyclomatic(5), "good");
assert_eq!(thresholds.check_cyclomatic(15), "warning");
assert_eq!(thresholds.check_cyclomatic(25), "high");
assert_eq!(thresholds.check_cyclomatic(35), "critical");
}
}