rust_code_analysis/metrics/
mi.rs1use serde::ser::{SerializeStruct, Serializer};
2use serde::Serialize;
3use std::fmt;
4
5use super::cyclomatic;
6use super::halstead;
7use super::loc;
8
9use crate::checker::Checker;
10
11use crate::*;
12
13#[derive(Default, Clone, Debug)]
15pub struct Stats {
16 halstead_length: f64,
17 halstead_vocabulary: f64,
18 halstead_volume: f64,
19 cyclomatic: f64,
20 sloc: f64,
21 comments_percentage: f64,
22}
23
24impl Serialize for Stats {
25 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
26 where
27 S: Serializer,
28 {
29 let mut st = serializer.serialize_struct("maintanability_index", 3)?;
30 st.serialize_field("mi_original", &self.mi_original())?;
31 st.serialize_field("mi_sei", &self.mi_sei())?;
32 st.serialize_field("mi_visual_studio", &self.mi_visual_studio())?;
33 st.end()
34 }
35}
36
37impl fmt::Display for Stats {
38 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
39 write!(
40 f,
41 "mi_original: {}, mi_sei: {}, mi_visual_studio: {}",
42 self.mi_original(),
43 self.mi_sei(),
44 self.mi_visual_studio()
45 )
46 }
47}
48
49impl Stats {
50 pub(crate) fn merge(&mut self, _other: &Stats) {}
51
52 #[inline(always)]
56 pub fn mi_original(&self) -> f64 {
57 171.0 - 5.2 * (self.halstead_volume).ln() - 0.23 * self.cyclomatic - 16.2 * self.sloc.ln()
59 }
60
61 #[inline(always)]
66 pub fn mi_sei(&self) -> f64 {
67 171.0 - 5.2 * self.halstead_volume.log2() - 0.23 * self.cyclomatic - 16.2 * self.sloc.log2()
69 + 50.0 * (self.comments_percentage * 2.4).sqrt().sin()
70 }
71
72 #[inline(always)]
75 pub fn mi_visual_studio(&self) -> f64 {
76 let formula = 171.0
78 - 5.2 * self.halstead_volume.ln()
79 - 0.23 * self.cyclomatic
80 - 16.2 * self.sloc.ln();
81 (formula * 100.0 / 171.0).max(0.)
82 }
83}
84
85#[doc(hidden)]
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
105impl Mi for RustCode {}
106impl Mi for CppCode {}
107impl Mi for PythonCode {}
108impl Mi for MozjsCode {}
109impl Mi for JavascriptCode {}
110impl Mi for TypescriptCode {}
111impl Mi for TsxCode {}
112impl Mi for PreprocCode {}
113impl Mi for CcommentCode {}
114impl Mi for JavaCode {}
115
116#[cfg(test)]
117mod tests {
118 use std::path::PathBuf;
119
120 use super::*;
121
122 #[test]
123 fn check_mi_metrics() {
124 check_metrics!(
127 "def f():
128 pass",
129 "foo.py",
130 PythonParser,
131 mi,
132 [],
133 [
134 (mi_original, 151.203_315_883_223_2),
135 (mi_sei, 142.643_061_717_489_76),
136 (mi_visual_studio, 88.422_991_744_574_97),
137 ]
138 );
139 }
140}