codelore_rca/metrics/
mi.rs1use serde::Serialize;
2use serde::ser::{SerializeStruct, Serializer};
3use std::fmt;
4
5use super::cyclomatic;
6use super::halstead;
7use super::loc;
8
9use crate::checker::Checker;
10use crate::macros::implement_metric_trait;
11
12use crate::*;
13
14#[derive(Default, Clone, Debug)]
16pub struct Stats {
17 halstead_length: f64,
18 halstead_vocabulary: f64,
19 halstead_volume: f64,
20 cyclomatic: f64,
21 sloc: f64,
22 comments_percentage: f64,
23}
24
25impl Serialize for Stats {
26 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
27 where
28 S: Serializer,
29 {
30 let mut st = serializer.serialize_struct("maintainability_index", 3)?;
31 st.serialize_field("mi_original", &self.mi_original())?;
32 st.serialize_field("mi_sei", &self.mi_sei())?;
33 st.serialize_field("mi_visual_studio", &self.mi_visual_studio())?;
34 st.end()
35 }
36}
37
38impl fmt::Display for Stats {
39 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
40 write!(
41 f,
42 "mi_original: {}, mi_sei: {}, mi_visual_studio: {}",
43 self.mi_original(),
44 self.mi_sei(),
45 self.mi_visual_studio()
46 )
47 }
48}
49
50impl Stats {
51 pub(crate) fn merge(&mut self, _other: &Stats) {}
52
53 #[inline(always)]
57 pub fn mi_original(&self) -> f64 {
58 171.0 - 5.2 * (self.halstead_volume).ln() - 0.23 * self.cyclomatic - 16.2 * self.sloc.ln()
60 }
61
62 #[inline(always)]
67 pub fn mi_sei(&self) -> f64 {
68 171.0 - 5.2 * self.halstead_volume.log2() - 0.23 * self.cyclomatic - 16.2 * self.sloc.log2()
70 + 50.0 * (self.comments_percentage * 2.4).sqrt().sin()
71 }
72
73 #[inline(always)]
76 pub fn mi_visual_studio(&self) -> f64 {
77 let formula = 171.0
79 - 5.2 * self.halstead_volume.ln()
80 - 0.23 * self.cyclomatic
81 - 16.2 * self.sloc.ln();
82 (formula * 100.0 / 171.0).max(0.)
83 }
84}
85
86pub trait Mi
87where
88 Self: Checker,
89{
90 fn compute(
91 loc: &loc::Stats,
92 cyclomatic: &cyclomatic::Stats,
93 halstead: &halstead::Stats,
94 stats: &mut Stats,
95 ) {
96 stats.halstead_length = halstead.length();
97 stats.halstead_vocabulary = halstead.vocabulary();
98 stats.halstead_volume = halstead.volume();
99 stats.cyclomatic = cyclomatic.cyclomatic_sum();
100 stats.sloc = loc.sloc();
101 stats.comments_percentage = loc.cloc() / stats.sloc;
102 }
103}
104
105implement_metric_trait!(
106 [Mi],
107 PythonCode,
108 JavascriptCode,
109 TypescriptCode,
110 TsxCode,
111 RustCode,
112 CppCode,
113 PreprocCode,
114 CcommentCode,
115 JavaCode,
116 KotlinCode
117);
118
119#[cfg(test)]
120mod tests {
121 use crate::tools::check_metrics;
122
123 use super::*;
124
125 #[test]
126 fn check_mi_metrics() {
127 check_metrics::<PythonParser>(
130 "def f():
131 pass",
132 "foo.py",
133 |metric| {
134 insta::assert_json_snapshot!(
135 metric.mi,
136 @r###"
137 {
138 "mi_original": 151.2033158832232,
139 "mi_sei": 142.64306171748976,
140 "mi_visual_studio": 88.42299174457497
141 }"###
142 );
143 },
144 );
145 }
146}