Skip to main content

codelens_core/insight/
mod.rs

1//! Code insight analysis — health scoring, hotspot detection, trend tracking.
2
3pub mod health;
4pub mod hotspot;
5pub mod scoring;
6pub mod trend;
7
8use serde::Serialize;
9use std::fmt;
10
11/// Health grade: A through F.
12#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize)]
13pub enum Grade {
14    A,
15    B,
16    C,
17    D,
18    F,
19}
20
21impl fmt::Display for Grade {
22    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
23        match self {
24            Grade::A => write!(f, "A"),
25            Grade::B => write!(f, "B"),
26            Grade::C => write!(f, "C"),
27            Grade::D => write!(f, "D"),
28            Grade::F => write!(f, "F"),
29        }
30    }
31}
32
33/// Generic delta between two values.
34#[derive(Debug, Clone, Serialize)]
35pub struct DeltaValue<T: Serialize> {
36    pub from: T,
37    pub to: T,
38    pub delta: T,
39    pub percent: f64,
40}
41
42impl DeltaValue<usize> {
43    pub fn new(from: usize, to: usize) -> Self {
44        let delta = to.saturating_sub(from);
45        let percent = if from > 0 {
46            (to as f64 - from as f64) / from as f64 * 100.0
47        } else if to > 0 {
48            100.0
49        } else {
50            0.0
51        };
52        Self {
53            from,
54            to,
55            delta,
56            percent,
57        }
58    }
59
60    pub fn signed_delta(&self) -> i64 {
61        self.to as i64 - self.from as i64
62    }
63}
64
65impl DeltaValue<f64> {
66    pub fn new_f64(from: f64, to: f64) -> Self {
67        let delta = to - from;
68        let percent = if from.abs() > f64::EPSILON {
69            delta / from * 100.0
70        } else if to.abs() > f64::EPSILON {
71            100.0
72        } else {
73            0.0
74        };
75        Self {
76            from,
77            to,
78            delta,
79            percent,
80        }
81    }
82}
83
84#[cfg(test)]
85mod tests {
86    use super::*;
87
88    #[test]
89    fn test_grade_display() {
90        assert_eq!(Grade::A.to_string(), "A");
91        assert_eq!(Grade::F.to_string(), "F");
92    }
93
94    #[test]
95    fn test_grade_ordering() {
96        assert!(Grade::A < Grade::B);
97        assert!(Grade::B < Grade::F);
98    }
99
100    #[test]
101    fn test_delta_value_increase() {
102        let d = DeltaValue::new(100, 120);
103        assert_eq!(d.delta, 20);
104        assert!((d.percent - 20.0).abs() < 0.01);
105        assert_eq!(d.signed_delta(), 20);
106    }
107
108    #[test]
109    fn test_delta_value_decrease() {
110        let d = DeltaValue::new(100, 80);
111        assert_eq!(d.delta, 0);
112        assert_eq!(d.signed_delta(), -20);
113        assert!((d.percent - (-20.0)).abs() < 0.01);
114    }
115
116    #[test]
117    fn test_delta_value_from_zero() {
118        let d = DeltaValue::new(0, 50);
119        assert!((d.percent - 100.0).abs() < 0.01);
120    }
121
122    #[test]
123    fn test_delta_value_f64() {
124        let d = DeltaValue::new_f64(82.0, 78.0);
125        assert!((d.delta - (-4.0)).abs() < 0.01);
126    }
127}