rust_code_analysis/output/
dump_metrics.rs

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