big_code_analysis/metrics/mod.rs
1//! Per-metric implementations.
2//!
3//! Each submodule defines one maintainability metric, its per-language
4//! traits, and its `Stats` accumulator. See the crate-level docs for an
5//! overview of the metric suite.
6
7/// Assignment / Branch / Condition counts.
8pub mod abc;
9/// Cognitive complexity.
10pub mod cognitive;
11/// Cyclomatic complexity.
12pub mod cyclomatic;
13/// Halstead suite (operators, operands, volume, difficulty, effort).
14pub mod halstead;
15/// Lines-of-code variants (SLOC, PLOC, LLOC, CLOC, blank).
16pub mod loc;
17/// Maintainability Index.
18pub mod mi;
19/// Number of arguments per function.
20pub mod nargs;
21/// Exit-point counting.
22pub mod nexits;
23/// Number of methods (functions + closures).
24pub mod nom;
25/// Number of public attributes.
26pub mod npa;
27/// Number of public methods.
28pub mod npm;
29/// Token count.
30pub mod tokens;
31/// Weighted Methods per Class.
32pub mod wmc;
33
34/// Divides a metric sum by a count, guarding the divisor with `.max(1)`.
35///
36/// Every "average over a count" metric routes through this helper so the
37/// divide-by-zero guard added for [#428] is applied uniformly rather than
38/// per call site. A `count` of `0` degrades to `sum / 1` (the sum itself)
39/// instead of producing `inf`/`NaN`, so a never-observed or count-less
40/// space still serializes a finite number.
41///
42/// The *meaning* of `count` is the caller's choice of denominator
43/// convention; the project uses two:
44///
45/// - **Per-function** averages (`cognitive`, `cyclomatic`, `exit`,
46/// `nargs`) divide by the function/closure count of the subtree, so the
47/// value reads as "average complexity per function". `cognitive`/
48/// `exit`/`nargs` source this count from `Nom`; `cyclomatic` counts its
49/// own function/closure *spaces* (equal in the common case, but
50/// independent of whether `Nom` is selected — see
51/// `cyclomatic::Stats::function_spaces`).
52/// - **Per-space** averages (`nom`, `loc`, `abc`, `tokens`) divide by the
53/// total number of spaces (functions, closures, classes, the file unit,
54/// …). These measure a property of each space rather than of each
55/// function, so a per-function denominator would not match their
56/// meaning (and for `nom` it would be circular — it *is* the function
57/// count).
58///
59/// [#428]: https://github.com/dekobon/big-code-analysis/issues/428
60#[inline]
61#[must_use]
62// `count as f64` is exact for any realistic space count; the cast mirrors
63// the per-metric modules' module-level allowance for count-to-float casts.
64#[allow(clippy::cast_precision_loss)]
65pub(crate) fn average(sum: f64, count: usize) -> f64 {
66 sum / count.max(1) as f64
67}