rust_code_analysis_code_split/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 MozjsCode,
109 JavascriptCode,
110 TypescriptCode,
111 TsxCode,
112 RustCode,
113 CppCode,
114 PreprocCode,
115 CcommentCode,
116 JavaCode,
117 KotlinCode
118);
119
120#[cfg(test)]
121mod tests {
122 use crate::tools::check_metrics;
123
124 use super::*;
125
126 #[test]
127 fn check_mi_metrics() {
128 check_metrics::<PythonParser>(
131 "def f():
132 pass",
133 "foo.py",
134 |metric| {
135 insta::assert_json_snapshot!(
136 metric.mi,
137 @r###"
138 {
139 "mi_original": 151.2033158832232,
140 "mi_sei": 142.64306171748976,
141 "mi_visual_studio": 88.42299174457497
142 }"###
143 );
144 },
145 );
146 }
147}