Skip to main content

big_code_analysis/output/
dump_metrics.rs

1use std::io::Write;
2use termcolor::{Color, ColorChoice, StandardStream, StandardStreamLock};
3
4use crate::abc;
5use crate::cognitive;
6use crate::cyclomatic;
7use crate::exit;
8use crate::halstead;
9use crate::loc;
10use crate::mi;
11use crate::nargs;
12use crate::nom;
13use crate::npa;
14use crate::npm;
15use crate::tokens;
16use crate::wmc;
17
18use crate::spaces::{CodeMetrics, FuncSpace};
19
20use crate::tools::{color, intense_color};
21
22/// Dumps the metrics of a code.
23///
24/// Returns a [`Result`] value, when an error occurs.
25///
26/// # Errors
27///
28/// Propagates any [`std::io::Error`] produced by the color-aware
29/// writer that backs `stdout` (broken pipe, write failure, …).
30///
31/// # Examples
32///
33/// ```
34/// use big_code_analysis::{analyze, dump_root, LANG, MetricsOptions, Source};
35///
36/// // Compute metrics via the non-generic `analyze` entry point.
37/// let space = analyze(
38///     Source::new(LANG::Cpp, b"int a = 42;"),
39///     MetricsOptions::default(),
40/// )
41/// .expect("snippet has a top-level FuncSpace");
42///
43/// // Dump all metrics
44/// dump_root(&space).unwrap();
45/// ```
46///
47/// [`Result`]: #variant.Result
48pub fn dump_root(space: &FuncSpace) -> std::io::Result<()> {
49    let stdout = StandardStream::stdout(ColorChoice::Always);
50    let mut stdout = stdout.lock();
51    dump_space(space, "", true, &mut stdout)?;
52    color(&mut stdout, Color::White)?;
53
54    Ok(())
55}
56
57fn dump_space(
58    space: &FuncSpace,
59    prefix: &str,
60    last: bool,
61    stdout: &mut StandardStreamLock,
62) -> std::io::Result<()> {
63    let (pref_child, pref) = if last { ("   ", "`- ") } else { ("|  ", "|- ") };
64
65    color(stdout, Color::Blue)?;
66    write!(stdout, "{prefix}{pref}")?;
67
68    intense_color(stdout, Color::Yellow)?;
69    write!(stdout, "{}: ", space.kind)?;
70
71    intense_color(stdout, Color::Cyan)?;
72    write!(stdout, "{}", space.name.as_ref().map_or("", |name| name))?;
73
74    intense_color(stdout, Color::Red)?;
75    writeln!(stdout, " (@{})", space.start_line)?;
76
77    let prefix = format!("{prefix}{pref_child}");
78    dump_metrics(&space.metrics, &prefix, space.spaces.is_empty(), stdout)?;
79
80    if let Some((last, spaces)) = space.spaces.split_last() {
81        for space in spaces {
82            dump_space(space, &prefix, false, stdout)?;
83        }
84        dump_space(last, &prefix, true, stdout)?;
85    }
86
87    Ok(())
88}
89
90fn dump_metrics(
91    metrics: &CodeMetrics,
92    prefix: &str,
93    last: bool,
94    stdout: &mut StandardStreamLock,
95) -> std::io::Result<()> {
96    let (pref_child, pref) = if last { ("   ", "`- ") } else { ("|  ", "|- ") };
97
98    color(stdout, Color::Blue)?;
99    write!(stdout, "{prefix}{pref}")?;
100
101    intense_color(stdout, Color::Yellow)?;
102    writeln!(stdout, "metrics")?;
103
104    let prefix = format!("{prefix}{pref_child}");
105    dump_cognitive(&metrics.cognitive, &prefix, false, stdout)?;
106    dump_cyclomatic(&metrics.cyclomatic, &prefix, false, stdout)?;
107    dump_nargs(&metrics.nargs, &prefix, false, stdout)?;
108    dump_nexits(&metrics.nexits, &prefix, false, stdout)?;
109    dump_halstead(&metrics.halstead, &prefix, false, stdout)?;
110    dump_loc(&metrics.loc, &prefix, false, stdout)?;
111    dump_nom(&metrics.nom, &prefix, false, stdout)?;
112    dump_tokens(&metrics.tokens, &prefix, false, stdout)?;
113    dump_mi(&metrics.mi, &prefix, false, stdout)?;
114    dump_abc(&metrics.abc, &prefix, false, stdout)?;
115    dump_wmc(&metrics.wmc, &prefix, false, stdout)?;
116    dump_npm(&metrics.npm, &prefix, false, stdout)?;
117    dump_npa(&metrics.npa, &prefix, true, stdout)
118}
119
120fn dump_cognitive(
121    stats: &cognitive::Stats,
122    prefix: &str,
123    last: bool,
124    stdout: &mut StandardStreamLock,
125) -> std::io::Result<()> {
126    let (pref_child, pref) = if last { ("   ", "`- ") } else { ("|  ", "|- ") };
127
128    color(stdout, Color::Blue)?;
129    write!(stdout, "{prefix}{pref}")?;
130
131    intense_color(stdout, Color::Green)?;
132    writeln!(stdout, "cognitive")?;
133
134    let prefix = format!("{prefix}{pref_child}");
135
136    dump_value("sum", stats.cognitive_sum(), &prefix, false, stdout)?;
137    dump_value("average", stats.cognitive_average(), &prefix, true, stdout)
138}
139
140fn dump_cyclomatic(
141    stats: &cyclomatic::Stats,
142    prefix: &str,
143    last: bool,
144    stdout: &mut StandardStreamLock,
145) -> std::io::Result<()> {
146    let (pref_child, pref) = if last { ("   ", "`- ") } else { ("|  ", "|- ") };
147
148    color(stdout, Color::Blue)?;
149    write!(stdout, "{prefix}{pref}")?;
150
151    intense_color(stdout, Color::Green)?;
152    writeln!(stdout, "cyclomatic")?;
153
154    let prefix = format!("{prefix}{pref_child}");
155
156    dump_value("sum", stats.cyclomatic_sum(), &prefix, false, stdout)?;
157    dump_value("average", stats.cyclomatic_average(), &prefix, true, stdout)
158}
159
160fn dump_halstead(
161    stats: &halstead::Stats,
162    prefix: &str,
163    last: bool,
164    stdout: &mut StandardStreamLock,
165) -> std::io::Result<()> {
166    let (pref_child, pref) = if last { ("   ", "`- ") } else { ("|  ", "|- ") };
167
168    color(stdout, Color::Blue)?;
169    write!(stdout, "{prefix}{pref}")?;
170
171    intense_color(stdout, Color::Green)?;
172    writeln!(stdout, "halstead")?;
173
174    let prefix = format!("{prefix}{pref_child}");
175
176    dump_value("n1", stats.u_operators(), &prefix, false, stdout)?;
177    dump_value("N1", stats.operators(), &prefix, false, stdout)?;
178    dump_value("n2", stats.u_operands(), &prefix, false, stdout)?;
179    dump_value("N2", stats.operands(), &prefix, false, stdout)?;
180
181    dump_value("length", stats.length(), &prefix, false, stdout)?;
182    dump_value(
183        "estimated program length",
184        stats.estimated_program_length(),
185        &prefix,
186        false,
187        stdout,
188    )?;
189    dump_value("purity ratio", stats.purity_ratio(), &prefix, false, stdout)?;
190    dump_value("vocabulary", stats.vocabulary(), &prefix, false, stdout)?;
191    dump_value("volume", stats.volume(), &prefix, false, stdout)?;
192    dump_value("difficulty", stats.difficulty(), &prefix, false, stdout)?;
193    dump_value("level", stats.level(), &prefix, false, stdout)?;
194    dump_value("effort", stats.effort(), &prefix, false, stdout)?;
195    dump_value("time", stats.time(), &prefix, false, stdout)?;
196    dump_value("bugs", stats.bugs(), &prefix, true, stdout)
197}
198
199fn dump_loc(
200    stats: &loc::Stats,
201    prefix: &str,
202    last: bool,
203    stdout: &mut StandardStreamLock,
204) -> std::io::Result<()> {
205    let (pref_child, pref) = if last { ("   ", "`- ") } else { ("|  ", "|- ") };
206
207    color(stdout, Color::Blue)?;
208    write!(stdout, "{prefix}{pref}")?;
209
210    intense_color(stdout, Color::Green)?;
211    writeln!(stdout, "loc")?;
212
213    let prefix = format!("{prefix}{pref_child}");
214    dump_value("sloc", stats.sloc(), &prefix, false, stdout)?;
215    dump_value("ploc", stats.ploc(), &prefix, false, stdout)?;
216    dump_value("lloc", stats.lloc(), &prefix, false, stdout)?;
217    dump_value("cloc", stats.cloc(), &prefix, false, stdout)?;
218    dump_value("blank", stats.blank(), &prefix, true, stdout)
219}
220
221fn dump_nom(
222    stats: &nom::Stats,
223    prefix: &str,
224    last: bool,
225    stdout: &mut StandardStreamLock,
226) -> std::io::Result<()> {
227    let (pref_child, pref) = if last { ("   ", "`- ") } else { ("|  ", "|- ") };
228
229    color(stdout, Color::Blue)?;
230    write!(stdout, "{prefix}{pref}")?;
231
232    intense_color(stdout, Color::Green)?;
233    writeln!(stdout, "nom")?;
234
235    let prefix = format!("{prefix}{pref_child}");
236    dump_value("functions", stats.functions(), &prefix, false, stdout)?;
237    dump_value("closures", stats.closures(), &prefix, false, stdout)?;
238    dump_value("total", stats.total(), &prefix, true, stdout)
239}
240
241fn dump_tokens(
242    stats: &tokens::Stats,
243    prefix: &str,
244    last: bool,
245    stdout: &mut StandardStreamLock,
246) -> std::io::Result<()> {
247    let (pref_child, pref) = if last { ("   ", "`- ") } else { ("|  ", "|- ") };
248
249    color(stdout, Color::Blue)?;
250    write!(stdout, "{prefix}{pref}")?;
251
252    intense_color(stdout, Color::Green)?;
253    writeln!(stdout, "tokens")?;
254
255    let prefix = format!("{prefix}{pref_child}");
256    dump_value("sum", stats.tokens_sum(), &prefix, false, stdout)?;
257    dump_value("average", stats.tokens_average(), &prefix, false, stdout)?;
258    dump_value("min", stats.tokens_min(), &prefix, false, stdout)?;
259    dump_value("max", stats.tokens_max(), &prefix, true, stdout)
260}
261
262fn dump_mi(
263    stats: &mi::Stats,
264    prefix: &str,
265    last: bool,
266    stdout: &mut StandardStreamLock,
267) -> std::io::Result<()> {
268    let (pref_child, pref) = if last { ("   ", "`- ") } else { ("|  ", "|- ") };
269
270    color(stdout, Color::Blue)?;
271    write!(stdout, "{prefix}{pref}")?;
272
273    intense_color(stdout, Color::Green)?;
274    writeln!(stdout, "mi")?;
275
276    let prefix = format!("{prefix}{pref_child}");
277    dump_value("mi_original", stats.mi_original(), &prefix, false, stdout)?;
278    dump_value("mi_sei", stats.mi_sei(), &prefix, false, stdout)?;
279    dump_value(
280        "mi_visual_studio",
281        stats.mi_visual_studio(),
282        &prefix,
283        true,
284        stdout,
285    )
286}
287
288fn dump_nargs(
289    stats: &nargs::Stats,
290    prefix: &str,
291    last: bool,
292    stdout: &mut StandardStreamLock,
293) -> std::io::Result<()> {
294    let (pref_child, pref) = if last { ("   ", "`- ") } else { ("|  ", "|- ") };
295
296    color(stdout, Color::Blue)?;
297    write!(stdout, "{prefix}{pref}")?;
298
299    intense_color(stdout, Color::Green)?;
300    writeln!(stdout, "nargs")?;
301
302    let prefix = format!("{prefix}{pref_child}");
303    dump_value("functions", stats.fn_args(), &prefix, false, stdout)?;
304    dump_value("closures", stats.closure_args(), &prefix, false, stdout)?;
305    dump_value("total", stats.nargs_total(), &prefix, false, stdout)?;
306    dump_value("average", stats.nargs_average(), &prefix, true, stdout)
307}
308
309fn dump_nexits(
310    stats: &exit::Stats,
311    prefix: &str,
312    last: bool,
313    stdout: &mut StandardStreamLock,
314) -> std::io::Result<()> {
315    let pref = if last { "`- " } else { "|- " };
316
317    color(stdout, Color::Blue)?;
318    write!(stdout, "{prefix}{pref}")?;
319
320    intense_color(stdout, Color::Green)?;
321    write!(stdout, "nexits: ")?;
322
323    color(stdout, Color::White)?;
324    writeln!(stdout, "{}", stats.exit())
325}
326
327fn dump_abc(
328    stats: &abc::Stats,
329    prefix: &str,
330    last: bool,
331    stdout: &mut StandardStreamLock,
332) -> std::io::Result<()> {
333    let (pref_child, pref) = if last { ("   ", "`- ") } else { ("|  ", "|- ") };
334
335    color(stdout, Color::Blue)?;
336    write!(stdout, "{prefix}{pref}")?;
337
338    intense_color(stdout, Color::Green)?;
339    writeln!(stdout, "abc")?;
340
341    let prefix = format!("{prefix}{pref_child}");
342
343    dump_value(
344        "assignments",
345        stats.assignments_sum(),
346        &prefix,
347        false,
348        stdout,
349    )?;
350    dump_value("branches", stats.branches_sum(), &prefix, false, stdout)?;
351    dump_value("conditions", stats.conditions_sum(), &prefix, false, stdout)?;
352    dump_value("magnitude", stats.magnitude_sum(), &prefix, true, stdout)
353}
354
355fn dump_wmc(
356    stats: &wmc::Stats,
357    prefix: &str,
358    last: bool,
359    stdout: &mut StandardStreamLock,
360) -> std::io::Result<()> {
361    if stats.is_disabled() {
362        return Ok(());
363    }
364
365    let (pref_child, pref) = if last { ("   ", "`- ") } else { ("|  ", "|- ") };
366
367    color(stdout, Color::Blue)?;
368    write!(stdout, "{prefix}{pref}")?;
369
370    intense_color(stdout, Color::Green)?;
371    writeln!(stdout, "wmc")?;
372
373    let prefix = format!("{prefix}{pref_child}");
374    dump_value("classes", stats.class_wmc_sum(), &prefix, false, stdout)?;
375    dump_value(
376        "interfaces",
377        stats.interface_wmc_sum(),
378        &prefix,
379        false,
380        stdout,
381    )?;
382    dump_value("total", stats.total_wmc(), &prefix, true, stdout)
383}
384
385fn dump_npm(
386    stats: &npm::Stats,
387    prefix: &str,
388    last: bool,
389    stdout: &mut StandardStreamLock,
390) -> std::io::Result<()> {
391    if stats.is_disabled() {
392        return Ok(());
393    }
394
395    let (pref_child, pref) = if last { ("   ", "`- ") } else { ("|  ", "|- ") };
396
397    color(stdout, Color::Blue)?;
398    write!(stdout, "{prefix}{pref}")?;
399
400    intense_color(stdout, Color::Green)?;
401    writeln!(stdout, "npm")?;
402
403    let prefix = format!("{prefix}{pref_child}");
404    dump_value("classes", stats.class_npm_sum(), &prefix, false, stdout)?;
405    dump_value(
406        "interfaces",
407        stats.interface_npm_sum(),
408        &prefix,
409        false,
410        stdout,
411    )?;
412    dump_value("total", stats.total_npm(), &prefix, false, stdout)?;
413    dump_value("average", stats.total_coa(), &prefix, true, stdout)
414}
415
416fn dump_npa(
417    stats: &npa::Stats,
418    prefix: &str,
419    last: bool,
420    stdout: &mut StandardStreamLock,
421) -> std::io::Result<()> {
422    if stats.is_disabled() {
423        return Ok(());
424    }
425
426    let (pref_child, pref) = if last { ("   ", "`- ") } else { ("|  ", "|- ") };
427
428    color(stdout, Color::Blue)?;
429    write!(stdout, "{prefix}{pref}")?;
430
431    intense_color(stdout, Color::Green)?;
432    writeln!(stdout, "npa")?;
433
434    let prefix = format!("{prefix}{pref_child}");
435    dump_value("classes", stats.class_npa_sum(), &prefix, false, stdout)?;
436    dump_value(
437        "interfaces",
438        stats.interface_npa_sum(),
439        &prefix,
440        false,
441        stdout,
442    )?;
443    dump_value("total", stats.total_npa(), &prefix, false, stdout)?;
444    dump_value("average", stats.total_cda(), &prefix, true, stdout)
445}
446
447fn dump_value(
448    name: &str,
449    val: f64,
450    prefix: &str,
451    last: bool,
452    stdout: &mut StandardStreamLock,
453) -> std::io::Result<()> {
454    let pref = if last { "`- " } else { "|- " };
455
456    color(stdout, Color::Blue)?;
457    write!(stdout, "{prefix}{pref}")?;
458
459    intense_color(stdout, Color::Magenta)?;
460    write!(stdout, "{name}: ")?;
461
462    color(stdout, Color::White)?;
463    writeln!(stdout, "{val}")
464}