use super::{DebtType, FileDebtItem, UnifiedDebtItem};
use std::collections::{HashMap, HashSet};
use std::path::PathBuf;
#[derive(Debug, Default)]
pub struct ItemAccumulatedMetrics {
pub total_debt_score: f64,
pub coverage_improvement: f64,
pub lines_reduction: u32,
pub complexity_reduction: f64,
pub risk_reduction: f64,
pub functions_to_test: usize,
pub unique_files: HashMap<PathBuf, usize>,
}
#[derive(Debug, Default)]
pub struct FileAccumulatedMetrics {
pub additional_debt_score: f64,
pub coverage_improvement: f64,
pub lines_reduction: u32,
pub complexity_reduction: f64,
pub unique_files: HashMap<PathBuf, usize>,
}
pub fn calculate_debt_density(total_debt_score: f64, total_lines_of_code: usize) -> f64 {
if total_lines_of_code > 0 {
(total_debt_score / total_lines_of_code as f64) * 1000.0
} else {
0.0
}
}
pub fn accumulate_item_metrics(
items: &im::Vector<UnifiedDebtItem>,
analyzed_files: &HashMap<PathBuf, usize>,
) -> ItemAccumulatedMetrics {
let mut result = ItemAccumulatedMetrics {
unique_files: analyzed_files.clone(),
..Default::default()
};
for item in items {
result.total_debt_score += item.unified_score.final_score;
if let Some(line_count) = item.file_line_count {
result
.unique_files
.insert(item.location.file.clone(), line_count);
}
if item.expected_impact.coverage_improvement > 0.0 {
result.functions_to_test += 1;
result.coverage_improvement += item.expected_impact.coverage_improvement / 100.0;
}
result.lines_reduction += item.expected_impact.lines_reduction;
result.complexity_reduction += item.expected_impact.complexity_reduction;
result.risk_reduction += item.expected_impact.risk_reduction;
}
result
}
pub fn collect_god_object_files(items: &im::Vector<UnifiedDebtItem>) -> HashSet<PathBuf> {
items
.iter()
.filter(|item| matches!(item.debt_type, DebtType::GodObject { .. }))
.map(|item| item.location.file.clone())
.collect()
}
pub fn accumulate_file_metrics(
file_items: &im::Vector<FileDebtItem>,
god_object_files: &HashSet<PathBuf>,
) -> FileAccumulatedMetrics {
let mut result = FileAccumulatedMetrics::default();
for file_item in file_items {
if !god_object_files.contains(&file_item.metrics.path) {
result.additional_debt_score += file_item.score;
}
result.unique_files.insert(
file_item.metrics.path.clone(),
file_item.metrics.total_lines,
);
result.complexity_reduction += file_item.impact.complexity_reduction;
result.lines_reduction += (file_item.metrics.total_lines / 10) as u32;
if file_item.metrics.coverage_percent < 0.8 {
result.coverage_improvement += (0.8 - file_item.metrics.coverage_percent) * 10.0;
}
}
result
}
pub fn scale_coverage_improvement(raw_coverage: f64) -> f64 {
(raw_coverage * 5.0).min(100.0)
}
pub fn calculate_total_loc(unique_files: &HashMap<PathBuf, usize>) -> usize {
unique_files.values().sum()
}
pub fn merge_unique_files(
base: HashMap<PathBuf, usize>,
overlay: HashMap<PathBuf, usize>,
) -> HashMap<PathBuf, usize> {
let mut result = base;
result.extend(overlay);
result
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_calculate_debt_density_normal() {
assert_eq!(calculate_debt_density(100.0, 1000), 100.0);
assert_eq!(calculate_debt_density(50.0, 500), 100.0);
assert_eq!(calculate_debt_density(250.0, 5000), 50.0);
}
#[test]
fn test_calculate_debt_density_zero_loc() {
assert_eq!(calculate_debt_density(100.0, 0), 0.0);
assert_eq!(calculate_debt_density(0.0, 0), 0.0);
}
#[test]
fn test_calculate_debt_density_zero_score() {
assert_eq!(calculate_debt_density(0.0, 1000), 0.0);
}
#[test]
fn test_scale_coverage_improvement() {
assert_eq!(scale_coverage_improvement(10.0), 50.0);
assert_eq!(scale_coverage_improvement(20.0), 100.0);
assert_eq!(scale_coverage_improvement(30.0), 100.0); assert_eq!(scale_coverage_improvement(0.0), 0.0);
}
#[test]
fn test_calculate_total_loc() {
let mut files = HashMap::new();
files.insert(PathBuf::from("a.rs"), 100);
files.insert(PathBuf::from("b.rs"), 200);
files.insert(PathBuf::from("c.rs"), 50);
assert_eq!(calculate_total_loc(&files), 350);
}
#[test]
fn test_calculate_total_loc_empty() {
let files = HashMap::new();
assert_eq!(calculate_total_loc(&files), 0);
}
#[test]
fn test_merge_unique_files() {
let mut base = HashMap::new();
base.insert(PathBuf::from("a.rs"), 100);
base.insert(PathBuf::from("b.rs"), 200);
let mut overlay = HashMap::new();
overlay.insert(PathBuf::from("b.rs"), 250); overlay.insert(PathBuf::from("c.rs"), 50);
let merged = merge_unique_files(base, overlay);
assert_eq!(merged.get(&PathBuf::from("a.rs")), Some(&100));
assert_eq!(merged.get(&PathBuf::from("b.rs")), Some(&250)); assert_eq!(merged.get(&PathBuf::from("c.rs")), Some(&50));
}
}