1use tokmd_analysis_types::{DerivedReport, EcoLabel, FunReport};
10
11pub fn build_fun_report(derived: &DerivedReport) -> FunReport {
13 let bytes = derived.totals.bytes as u64;
14 let mb = bytes as f64 / (1024.0 * 1024.0);
15 let (label, score) = fun_band(mb);
16
17 FunReport {
18 eco_label: Some(EcoLabel {
19 score,
20 label: label.to_string(),
21 bytes,
22 notes: format!("Size-based eco label ({} MB)", round_to_two(mb)),
23 }),
24 }
25}
26
27fn fun_band(mb: f64) -> (&'static str, f64) {
28 if mb <= 1.0 {
29 ("A", 95.0)
30 } else if mb <= 10.0 {
31 ("B", 80.0)
32 } else if mb <= 50.0 {
33 ("C", 65.0)
34 } else if mb <= 200.0 {
35 ("D", 45.0)
36 } else {
37 ("E", 30.0)
38 }
39}
40
41fn round_to_two(value: f64) -> f64 {
42 (value * 100.0).round() / 100.0
43}
44
45#[cfg(test)]
46mod tests {
47 use super::{build_fun_report, fun_band};
48 use tokmd_analysis_types::{
49 BoilerplateReport, DerivedReport, DerivedTotals, DistributionReport, FileStatRow,
50 IntegrityReport, LangPurityReport, MaxFileReport, NestingReport, PolyglotReport,
51 RateReport, RateRow, RatioReport, RatioRow, ReadingTimeReport, TestDensityReport,
52 TodoReport, TopOffenders,
53 };
54
55 fn tiny_derived(bytes: usize) -> DerivedReport {
56 let zero_row = FileStatRow {
57 path: "small.rs".to_string(),
58 module: "src".to_string(),
59 lang: "Rust".to_string(),
60 code: 0,
61 comments: 0,
62 blanks: 0,
63 lines: 0,
64 bytes,
65 tokens: 0,
66 doc_pct: Some(0.0),
67 bytes_per_line: Some(0.0),
68 depth: 0,
69 };
70
71 DerivedReport {
72 totals: DerivedTotals {
73 files: 1,
74 code: 1,
75 comments: 0,
76 blanks: 0,
77 lines: 1,
78 bytes,
79 tokens: 1,
80 },
81 doc_density: RatioReport {
82 total: RatioRow {
83 key: "All".to_string(),
84 numerator: 0,
85 denominator: 1,
86 ratio: 0.0,
87 },
88 by_lang: vec![],
89 by_module: vec![],
90 },
91 whitespace: RatioReport {
92 total: RatioRow {
93 key: "All".to_string(),
94 numerator: 0,
95 denominator: 1,
96 ratio: 0.0,
97 },
98 by_lang: vec![],
99 by_module: vec![],
100 },
101 verbosity: RateReport {
102 total: RateRow {
103 key: "All".to_string(),
104 numerator: 0,
105 denominator: 1,
106 rate: 0.0,
107 },
108 by_lang: vec![],
109 by_module: vec![],
110 },
111 max_file: MaxFileReport {
112 overall: zero_row.clone(),
113 by_lang: vec![],
114 by_module: vec![],
115 },
116 lang_purity: LangPurityReport { rows: vec![] },
117 nesting: NestingReport {
118 max: 0,
119 avg: 0.0,
120 by_module: vec![],
121 },
122 test_density: TestDensityReport {
123 test_lines: 0,
124 prod_lines: 0,
125 test_files: 0,
126 prod_files: 0,
127 ratio: 0.0,
128 },
129 boilerplate: BoilerplateReport {
130 infra_lines: 0,
131 logic_lines: 0,
132 ratio: 0.0,
133 infra_langs: vec![],
134 },
135 polyglot: PolyglotReport {
136 lang_count: 0,
137 entropy: 0.0,
138 dominant_lang: "unknown".to_string(),
139 dominant_lines: 0,
140 dominant_pct: 0.0,
141 },
142 distribution: DistributionReport {
143 count: 1,
144 min: 1,
145 max: 1,
146 mean: 0.0,
147 median: 0.0,
148 p90: 0.0,
149 p99: 0.0,
150 gini: 0.0,
151 },
152 histogram: Vec::new(),
153 top: TopOffenders {
154 largest_lines: vec![zero_row.clone()],
155 largest_tokens: vec![zero_row.clone()],
156 largest_bytes: vec![zero_row.clone()],
157 least_documented: vec![zero_row.clone()],
158 most_dense: vec![zero_row],
159 },
160 tree: None,
161 reading_time: ReadingTimeReport {
162 minutes: 0.0,
163 lines_per_minute: 0,
164 basis_lines: 0,
165 },
166 context_window: None,
167 cocomo: None,
168 todo: Some(TodoReport {
169 total: 0,
170 density_per_kloc: 0.0,
171 tags: vec![],
172 }),
173 integrity: IntegrityReport {
174 algo: "sha1".to_string(),
175 hash: "placeholder".to_string(),
176 entries: 0,
177 },
178 }
179 }
180
181 #[test]
182 fn fun_grade_boundaries_are_stable() {
183 assert_eq!(fun_band(0.5), ("A", 95.0));
184 assert_eq!(fun_band(10.0), ("B", 80.0));
185 assert_eq!(fun_band(50.0), ("C", 65.0));
186 assert_eq!(fun_band(200.0), ("D", 45.0));
187 assert_eq!(fun_band(200.1), ("E", 30.0));
188 }
189
190 #[test]
191 fn fun_report_contains_notes_and_bytes() {
192 let bytes = 1024 * 1024;
193 let report = build_fun_report(&tiny_derived(bytes));
194 let eco = report.eco_label.expect("eco_label expected");
195
196 assert_eq!(eco.label, "A");
197 assert_eq!(eco.score, 95.0);
198 assert_eq!(eco.bytes, bytes as u64);
199 assert_eq!(eco.notes, "Size-based eco label (1 MB)");
200 }
201}