1use crate::config::DebtWeights;
2use crate::{Finding, Severity};
3
4#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
6pub enum Grade {
7 A,
8 B,
9 C,
10 D,
11 F,
12}
13
14impl std::fmt::Display for Grade {
15 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
16 f.write_str(match self {
17 Grade::A => "A",
18 Grade::B => "B",
19 Grade::C => "C",
20 Grade::D => "D",
21 Grade::F => "F",
22 })
23 }
24}
25
26#[derive(Debug, Clone)]
28pub struct HealthScore {
29 pub path: String,
30 pub grade: Grade,
31 pub debt_minutes: u32,
33 pub lines: usize,
34}
35
36pub fn score_files(
38 findings: &[Finding],
39 file_lines: &[(String, usize)],
40 weights: &DebtWeights,
41) -> Vec<HealthScore> {
42 use std::collections::HashMap;
43 let mut debt: HashMap<String, u32> = HashMap::new();
44 for f in findings {
45 let path = f.location.path.to_string_lossy().to_string();
46 *debt.entry(path).or_default() += debt_minutes(f.severity, weights);
47 }
48 file_lines
49 .iter()
50 .map(|(path, lines)| {
51 let mins = debt.get(path).copied().unwrap_or(0);
52 let grade = rate(mins, *lines);
53 HealthScore {
54 path: path.clone(),
55 grade,
56 debt_minutes: mins,
57 lines: *lines,
58 }
59 })
60 .collect()
61}
62
63fn debt_minutes(s: Severity, w: &DebtWeights) -> u32 {
65 match s {
66 Severity::Hint => w.hint,
67 Severity::Warning => w.warning,
68 Severity::Error => w.error,
69 }
70}
71
72fn rate(debt_mins: u32, lines: usize) -> Grade {
75 if lines == 0 {
76 return Grade::A;
77 }
78 let ratio = (debt_mins as f64) / (lines as f64) * 100.0;
79 if ratio <= 5.0 {
80 Grade::A
81 } else if ratio <= 10.0 {
82 Grade::B
83 } else if ratio <= 20.0 {
84 Grade::C
85 } else if ratio <= 40.0 {
86 Grade::D
87 } else {
88 Grade::F
89 }
90}