#![cfg_attr(coverage_nightly, coverage(off))]
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize, Clone, Copy, Default)]
pub struct ComplexityMetrics {
pub cyclomatic: u16,
pub cognitive: u16,
pub nesting_max: u8,
pub lines: u16,
pub halstead: Option<HalsteadMetrics>,
}
impl ComplexityMetrics {
#[must_use]
pub fn new(cyclomatic: u16, cognitive: u16, nesting_max: u8, lines: u16) -> Self {
Self {
cyclomatic,
cognitive,
nesting_max,
lines,
halstead: None, }
}
#[must_use]
pub fn with_halstead(
cyclomatic: u16,
cognitive: u16,
nesting_max: u8,
lines: u16,
halstead: HalsteadMetrics,
) -> Self {
Self {
cyclomatic,
cognitive,
nesting_max,
lines,
halstead: Some(halstead),
}
}
#[must_use]
pub fn is_simple(&self) -> bool {
self.cyclomatic <= 5 && self.cognitive <= 7
}
#[must_use]
pub fn needs_refactoring(&self) -> bool {
self.cyclomatic > 10 || self.cognitive > 15
}
#[must_use]
pub fn complexity_score(&self) -> f64 {
(f64::from(self.cyclomatic) * 1.0)
+ (f64::from(self.cognitive) * 1.2)
+ (f64::from(self.nesting_max) * 2.0)
+ (f64::from(self.lines) * 0.1)
}
}
#[derive(Debug, Serialize, Deserialize, Clone, Copy, Default)]
pub struct HalsteadMetrics {
pub operators_unique: u32,
pub operands_unique: u32,
pub operators_total: u32,
pub operands_total: u32,
pub volume: f64,
pub difficulty: f64,
pub effort: f64,
pub time: f64,
pub bugs: f64,
}
impl HalsteadMetrics {
#[must_use]
pub fn new(
operators_unique: u32,
operands_unique: u32,
operators_total: u32,
operands_total: u32,
) -> Self {
Self {
operators_unique,
operands_unique,
operators_total,
operands_total,
volume: 0.0,
difficulty: 0.0,
effort: 0.0,
time: 0.0,
bugs: 0.0,
}
}
#[must_use]
pub fn calculate_derived(mut self) -> Self {
if self.operators_unique == 0 || self.operands_unique == 0 {
return self;
}
let total = self.operators_total + self.operands_total;
let unique = self.operators_unique + self.operands_unique;
if unique > 0 {
self.volume = f64::from(total) * f64::from(unique).log2();
}
if self.operands_unique > 0 {
self.difficulty = (f64::from(self.operators_unique) / 2.0)
* (f64::from(self.operands_total) / f64::from(self.operands_unique));
}
self.effort = self.volume * self.difficulty;
self.time = self.effort / 18.0;
self.bugs = self.effort.powf(2.0 / 3.0) / 3000.0;
self
}
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct FileComplexityMetrics {
pub path: String,
pub total_complexity: ComplexityMetrics,
pub functions: Vec<FunctionComplexity>,
pub classes: Vec<ClassComplexity>,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct FunctionComplexity {
pub name: String,
pub line_start: u32,
pub line_end: u32,
pub metrics: ComplexityMetrics,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct ClassComplexity {
pub name: String,
pub line_start: u32,
pub line_end: u32,
pub metrics: ComplexityMetrics,
pub methods: Vec<FunctionComplexity>,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct ComplexityThresholds {
pub cyclomatic_warn: u16,
pub cyclomatic_error: u16,
pub cognitive_warn: u16,
pub cognitive_error: u16,
pub nesting_max: u8,
pub method_length: u16,
}
impl Default for ComplexityThresholds {
fn default() -> Self {
Self {
cyclomatic_warn: 10,
cyclomatic_error: 20,
cognitive_warn: 15,
cognitive_error: 30,
nesting_max: 5,
method_length: 50,
}
}
}
#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(tag = "severity", rename_all = "lowercase")]
pub enum Violation {
Error {
rule: String,
message: String,
value: u16,
threshold: u16,
file: String,
line: u32,
function: Option<String>,
},
Warning {
rule: String,
message: String,
value: u16,
threshold: u16,
file: String,
line: u32,
function: Option<String>,
},
}
#[derive(Debug, Serialize, Deserialize, Clone, Default)]
pub struct ComplexitySummary {
pub total_files: usize,
pub total_functions: usize,
pub median_cyclomatic: f32,
pub median_cognitive: f32,
pub max_cyclomatic: u16,
pub max_cognitive: u16,
pub p90_cyclomatic: u16,
pub p90_cognitive: u16,
pub technical_debt_hours: f32,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct ComplexityHotspot {
pub file: String,
pub function: Option<String>,
pub line: u32,
pub complexity: u16,
pub complexity_type: String,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct ComplexityReport {
pub summary: ComplexitySummary,
pub violations: Vec<Violation>,
pub hotspots: Vec<ComplexityHotspot>,
pub files: Vec<FileComplexityMetrics>,
}