Skip to main content

big_code_analysis/wire/
metrics.rs

1//! Per-metric wire structs and their `From<&<metric>::Stats>`
2//! projections — the per-metric arm of the wire shape (see the parent
3//! [`super`] module doc for the single-source-of-truth rationale).
4
5use super::*;
6
7/// Wire form of the `Abc` metric.
8#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
9pub struct Abc {
10    /// Sum of assignments across the space.
11    pub assignments: u64,
12    /// Sum of branches across the space.
13    pub branches: u64,
14    /// Sum of conditions across the space.
15    pub conditions: u64,
16    /// Euclidean ABC magnitude across the space (built from the summed
17    /// assignment/branch/condition accumulators).
18    #[serde(default = "nan_default", with = "non_finite")]
19    pub magnitude: f64,
20    /// This space's own ABC magnitude — `sqrt(A² + B² + C²)` over just
21    /// this space's assignments/branches/conditions, excluding nested
22    /// function/closure spaces. Equals [`magnitude`](Self::magnitude)
23    /// only at a leaf space; it is the per-space scalar the CLI
24    /// thresholds `abc` against (#958). Absent pre-#958 JSON
25    /// deserializes to `NaN` via `nan_default`.
26    #[serde(default = "nan_default", with = "non_finite")]
27    pub value: f64,
28    /// Average assignments per space.
29    #[serde(default = "nan_default", with = "non_finite")]
30    pub assignments_average: f64,
31    /// Average branches per space.
32    #[serde(default = "nan_default", with = "non_finite")]
33    pub branches_average: f64,
34    /// Average conditions per space.
35    #[serde(default = "nan_default", with = "non_finite")]
36    pub conditions_average: f64,
37    /// Minimum assignments in a single space.
38    pub assignments_min: u64,
39    /// Maximum assignments in a single space.
40    pub assignments_max: u64,
41    /// Minimum branches in a single space.
42    pub branches_min: u64,
43    /// Maximum branches in a single space.
44    pub branches_max: u64,
45    /// Minimum conditions in a single space.
46    pub conditions_min: u64,
47    /// Maximum conditions in a single space.
48    pub conditions_max: u64,
49}
50
51impl From<&abc::Stats> for Abc {
52    fn from(s: &abc::Stats) -> Self {
53        Self {
54            assignments: s.assignments_sum(),
55            branches: s.branches_sum(),
56            conditions: s.conditions_sum(),
57            magnitude: s.magnitude_sum(),
58            value: s.magnitude(),
59            assignments_average: s.assignments_average(),
60            branches_average: s.branches_average(),
61            conditions_average: s.conditions_average(),
62            assignments_min: s.assignments_min(),
63            assignments_max: s.assignments_max(),
64            branches_min: s.branches_min(),
65            branches_max: s.branches_max(),
66            conditions_min: s.conditions_min(),
67            conditions_max: s.conditions_max(),
68        }
69    }
70}
71
72/// Wire form of the `Cognitive` metric.
73#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
74pub struct Cognitive {
75    /// Cognitive-complexity sum across the space.
76    pub sum: u64,
77    /// This space's own cognitive complexity, excluding nested
78    /// function/closure spaces. Equals [`sum`](Self::sum) only at a leaf
79    /// space; at an interior space the sum rolls up descendants while
80    /// this stays the per-space scalar the CLI thresholds against
81    /// (#958). `#[serde(default)]` so a document written before #958
82    /// still deserializes through the public `wire` read-back path
83    /// STABILITY.md advertises. (`bca diff` is *not* that consumer — it
84    /// walks a raw `serde_json::Value`, see `metric_diff.rs`.)
85    #[serde(default)]
86    pub value: u64,
87    /// Average cognitive complexity per function.
88    #[serde(default = "nan_default", with = "non_finite")]
89    pub average: f64,
90    /// Minimum cognitive complexity in a single function.
91    pub min: u64,
92    /// Maximum cognitive complexity in a single function.
93    pub max: u64,
94}
95
96impl From<&cognitive::Stats> for Cognitive {
97    fn from(s: &cognitive::Stats) -> Self {
98        Self {
99            sum: s.cognitive_sum(),
100            value: s.cognitive(),
101            average: s.cognitive_average(),
102            min: s.cognitive_min(),
103            max: s.cognitive_max(),
104        }
105    }
106}
107
108/// Wire form of the modified-cyclomatic sub-record.
109#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
110pub struct CyclomaticModified {
111    /// Modified-cyclomatic sum across the space.
112    pub sum: u64,
113    /// This space's own modified-cyclomatic complexity, excluding nested
114    /// function/closure spaces (see [`Cyclomatic::value`]).
115    #[serde(default)]
116    pub value: u64,
117    /// Average modified cyclomatic complexity per function.
118    #[serde(default = "nan_default", with = "non_finite")]
119    pub average: f64,
120    /// Minimum modified cyclomatic complexity in a single function.
121    pub min: u64,
122    /// Maximum modified cyclomatic complexity in a single function.
123    pub max: u64,
124}
125
126/// Wire form of the `Cyclomatic` metric.
127#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
128pub struct Cyclomatic {
129    /// Cyclomatic-complexity sum across the space.
130    pub sum: u64,
131    /// This space's own cyclomatic complexity, excluding nested
132    /// function/closure spaces. Equals [`sum`](Self::sum) only at a leaf
133    /// space; at an interior space the sum rolls up descendants while
134    /// this stays the per-space scalar the CLI thresholds against
135    /// (#958). `#[serde(default)]` so pre-#958 JSON still deserializes.
136    #[serde(default)]
137    pub value: u64,
138    /// Average cyclomatic complexity per function.
139    #[serde(default = "nan_default", with = "non_finite")]
140    pub average: f64,
141    /// Minimum cyclomatic complexity in a single function.
142    pub min: u64,
143    /// Maximum cyclomatic complexity in a single function.
144    pub max: u64,
145    /// The modified-cyclomatic projection.
146    pub modified: CyclomaticModified,
147}
148
149impl From<&cyclomatic::Stats> for Cyclomatic {
150    fn from(s: &cyclomatic::Stats) -> Self {
151        Self {
152            sum: s.cyclomatic_sum(),
153            value: s.cyclomatic(),
154            average: s.cyclomatic_average(),
155            min: s.cyclomatic_min(),
156            max: s.cyclomatic_max(),
157            modified: CyclomaticModified {
158                sum: s.cyclomatic_modified_sum(),
159                value: s.cyclomatic_modified(),
160                average: s.cyclomatic_modified_average(),
161                min: s.cyclomatic_modified_min(),
162                max: s.cyclomatic_modified_max(),
163            },
164        }
165    }
166}
167
168/// Wire form of the `Nexits` (exit-points) metric.
169#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
170pub struct Nexits {
171    /// Exit-point sum across the space.
172    pub sum: u64,
173    /// Average exit points per function.
174    #[serde(default = "nan_default", with = "non_finite")]
175    pub average: f64,
176    /// Minimum exit points in a single function.
177    pub min: u64,
178    /// Maximum exit points in a single function.
179    pub max: u64,
180}
181
182impl From<&nexits::Stats> for Nexits {
183    fn from(s: &nexits::Stats) -> Self {
184        Self {
185            sum: s.nexits_sum(),
186            average: s.nexits_average(),
187            min: s.nexits_min(),
188            max: s.nexits_max(),
189        }
190    }
191}
192
193/// Wire form of the `Halstead` metric suite.
194#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
195pub struct Halstead {
196    /// Number of distinct operators (`n1`).
197    pub unique_operators: u64,
198    /// Total operator occurrences (`N1`).
199    pub total_operators: u64,
200    /// Number of distinct operands (`n2`).
201    pub unique_operands: u64,
202    /// Total operand occurrences (`N2`).
203    pub total_operands: u64,
204    /// Program length (`N = N1 + N2`).
205    pub length: u64,
206    /// Estimated program length.
207    #[serde(default = "nan_default", with = "non_finite")]
208    pub estimated_program_length: f64,
209    /// Purity ratio (estimated length / length).
210    #[serde(default = "nan_default", with = "non_finite")]
211    pub purity_ratio: f64,
212    /// Program vocabulary (`n = n1 + n2`).
213    pub vocabulary: u64,
214    /// Program volume.
215    #[serde(default = "nan_default", with = "non_finite")]
216    pub volume: f64,
217    /// Difficulty.
218    #[serde(default = "nan_default", with = "non_finite")]
219    pub difficulty: f64,
220    /// Program level (inverse difficulty).
221    #[serde(default = "nan_default", with = "non_finite")]
222    pub level: f64,
223    /// Effort.
224    #[serde(default = "nan_default", with = "non_finite")]
225    pub effort: f64,
226    /// Estimated time to program (seconds).
227    #[serde(default = "nan_default", with = "non_finite")]
228    pub time: f64,
229    /// Estimated number of delivered bugs.
230    #[serde(default = "nan_default", with = "non_finite")]
231    pub bugs: f64,
232}
233
234impl From<&halstead::Stats> for Halstead {
235    fn from(s: &halstead::Stats) -> Self {
236        Self {
237            unique_operators: s.unique_operators(),
238            total_operators: s.total_operators(),
239            unique_operands: s.unique_operands(),
240            total_operands: s.total_operands(),
241            length: s.length(),
242            estimated_program_length: s.estimated_program_length(),
243            purity_ratio: s.purity_ratio(),
244            vocabulary: s.vocabulary(),
245            volume: s.volume(),
246            difficulty: s.difficulty(),
247            level: s.level(),
248            effort: s.effort(),
249            time: s.time(),
250            bugs: s.bugs(),
251        }
252    }
253}
254
255/// Wire form of the `Loc` metric suite.
256#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
257pub struct Loc {
258    /// Source lines of code.
259    pub sloc: u64,
260    /// Physical lines of code.
261    pub ploc: u64,
262    /// Logical lines of code.
263    pub lloc: u64,
264    /// Comment lines of code.
265    pub cloc: u64,
266    /// Blank lines.
267    pub blank: u64,
268    /// Documentation rows (DLOC: doc comments, docstrings, POD); a
269    /// subset of `cloc`. Defaulted so documents written before #1597
270    /// still parse.
271    #[serde(default)]
272    pub dloc: u64,
273    /// Average SLOC per space.
274    #[serde(default = "nan_default", with = "non_finite")]
275    pub sloc_average: f64,
276    /// Average PLOC per space.
277    #[serde(default = "nan_default", with = "non_finite")]
278    pub ploc_average: f64,
279    /// Average LLOC per space.
280    #[serde(default = "nan_default", with = "non_finite")]
281    pub lloc_average: f64,
282    /// Average CLOC per space.
283    #[serde(default = "nan_default", with = "non_finite")]
284    pub cloc_average: f64,
285    /// Average blank lines per space.
286    #[serde(default = "nan_default", with = "non_finite")]
287    pub blank_average: f64,
288    /// Average documentation rows per space.
289    #[serde(default = "nan_default", with = "non_finite")]
290    pub dloc_average: f64,
291    /// Minimum SLOC in a single space.
292    pub sloc_min: u64,
293    /// Maximum SLOC in a single space.
294    pub sloc_max: u64,
295    /// Minimum CLOC in a single space.
296    pub cloc_min: u64,
297    /// Maximum CLOC in a single space.
298    pub cloc_max: u64,
299    /// Minimum PLOC in a single space.
300    pub ploc_min: u64,
301    /// Maximum PLOC in a single space.
302    pub ploc_max: u64,
303    /// Minimum LLOC in a single space.
304    pub lloc_min: u64,
305    /// Maximum LLOC in a single space.
306    pub lloc_max: u64,
307    /// Minimum blank lines in a single space.
308    pub blank_min: u64,
309    /// Maximum blank lines in a single space.
310    pub blank_max: u64,
311    /// Minimum documentation rows in a single space.
312    #[serde(default)]
313    pub dloc_min: u64,
314    /// Maximum documentation rows in a single space.
315    #[serde(default)]
316    pub dloc_max: u64,
317}
318
319impl From<&loc::Stats> for Loc {
320    fn from(s: &loc::Stats) -> Self {
321        Self {
322            sloc: s.sloc(),
323            ploc: s.ploc(),
324            lloc: s.lloc(),
325            cloc: s.cloc(),
326            blank: s.blank(),
327            dloc: s.dloc(),
328            sloc_average: s.sloc_average(),
329            ploc_average: s.ploc_average(),
330            lloc_average: s.lloc_average(),
331            cloc_average: s.cloc_average(),
332            blank_average: s.blank_average(),
333            dloc_average: s.dloc_average(),
334            sloc_min: s.sloc_min(),
335            sloc_max: s.sloc_max(),
336            cloc_min: s.cloc_min(),
337            cloc_max: s.cloc_max(),
338            ploc_min: s.ploc_min(),
339            ploc_max: s.ploc_max(),
340            lloc_min: s.lloc_min(),
341            lloc_max: s.lloc_max(),
342            blank_min: s.blank_min(),
343            blank_max: s.blank_max(),
344            dloc_min: s.dloc_min(),
345            dloc_max: s.dloc_max(),
346        }
347    }
348}
349
350/// Wire form of the `Mi` (maintainability index) metric.
351#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
352pub struct Mi {
353    /// Original maintainability index.
354    #[serde(default = "nan_default", with = "non_finite")]
355    pub original: f64,
356    /// SEI-derivative maintainability index.
357    #[serde(default = "nan_default", with = "non_finite")]
358    pub sei: f64,
359    /// Visual Studio-derivative maintainability index.
360    #[serde(default = "nan_default", with = "non_finite")]
361    pub visual_studio: f64,
362}
363
364impl From<&mi::Stats> for Mi {
365    fn from(s: &mi::Stats) -> Self {
366        Self {
367            original: s.original(),
368            sei: s.sei(),
369            visual_studio: s.visual_studio(),
370        }
371    }
372}
373
374/// Wire form of the `NArgs` metric.
375#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
376pub struct Nargs {
377    /// Sum of function arguments.
378    pub function_args: u64,
379    /// Sum of closure arguments.
380    pub closure_args: u64,
381    /// Average function arguments per function.
382    #[serde(default = "nan_default", with = "non_finite")]
383    pub function_args_average: f64,
384    /// Average closure arguments per closure.
385    #[serde(default = "nan_default", with = "non_finite")]
386    pub closure_args_average: f64,
387    /// Total arguments (functions + closures).
388    pub total: u64,
389    /// This space's own argument count — its function parameters plus
390    /// its closure parameters — excluding nested function/closure
391    /// spaces. Equals [`total`](Self::total) only at a leaf space; at an
392    /// interior space `total` rolls up descendants while this stays the
393    /// per-space scalar the CLI thresholds `nargs` against (#1196,
394    /// #1236). `#[serde(default)]` so a document written before #1236
395    /// still deserializes through the public `wire` read-back path
396    /// STABILITY.md advertises. (`bca diff` is *not* that consumer — it
397    /// walks a raw `serde_json::Value`, see `metric_diff.rs`.)
398    #[serde(default)]
399    pub value: u64,
400    /// Average arguments per function/closure.
401    #[serde(default = "nan_default", with = "non_finite")]
402    pub average: f64,
403    /// Minimum function arguments in a single function.
404    pub function_args_min: u64,
405    /// Maximum function arguments in a single function.
406    pub function_args_max: u64,
407    /// Minimum closure arguments in a single closure.
408    pub closure_args_min: u64,
409    /// Maximum closure arguments in a single closure.
410    pub closure_args_max: u64,
411}
412
413impl From<&nargs::Stats> for Nargs {
414    fn from(s: &nargs::Stats) -> Self {
415        Self {
416            function_args: s.function_args_sum(),
417            closure_args: s.closure_args_sum(),
418            function_args_average: s.function_args_average(),
419            closure_args_average: s.closure_args_average(),
420            total: s.total(),
421            value: s.own_args(),
422            average: s.average(),
423            function_args_min: s.function_args_min(),
424            function_args_max: s.function_args_max(),
425            closure_args_min: s.closure_args_min(),
426            closure_args_max: s.closure_args_max(),
427        }
428    }
429}
430
431/// Wire form of the `Nom` (number-of-methods) metric.
432#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
433pub struct Nom {
434    /// Sum of function definitions.
435    pub functions: u64,
436    /// Sum of closures.
437    pub closures: u64,
438    /// Average function definitions per space.
439    #[serde(default = "nan_default", with = "non_finite")]
440    pub functions_average: f64,
441    /// Average closures per space.
442    #[serde(default = "nan_default", with = "non_finite")]
443    pub closures_average: f64,
444    /// Total functions + closures.
445    pub total: u64,
446    /// Average functions + closures per space.
447    #[serde(default = "nan_default", with = "non_finite")]
448    pub average: f64,
449    /// Minimum function definitions in a single space.
450    pub functions_min: u64,
451    /// Maximum function definitions in a single space.
452    pub functions_max: u64,
453    /// Minimum closures in a single space.
454    pub closures_min: u64,
455    /// Maximum closures in a single space.
456    pub closures_max: u64,
457}
458
459impl From<&nom::Stats> for Nom {
460    fn from(s: &nom::Stats) -> Self {
461        Self {
462            functions: s.functions_sum(),
463            closures: s.closures_sum(),
464            functions_average: s.functions_average(),
465            closures_average: s.closures_average(),
466            total: s.total(),
467            average: s.average(),
468            functions_min: s.functions_min(),
469            functions_max: s.functions_max(),
470            closures_min: s.closures_min(),
471            closures_max: s.closures_max(),
472        }
473    }
474}
475
476/// Wire form of the `Npa` (number-of-public-attributes) metric.
477#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
478pub struct Npa {
479    /// Sum of public attributes across classes.
480    pub class_npa_sum: u64,
481    /// Sum of public attributes across interfaces.
482    pub interface_npa_sum: u64,
483    /// Sum of all class attributes.
484    pub class_attributes: u64,
485    /// Sum of all interface attributes.
486    pub interface_attributes: u64,
487    /// Class data accessibility ratio.
488    #[serde(default = "nan_default", with = "non_finite")]
489    pub class_cda: f64,
490    /// Interface data accessibility ratio.
491    #[serde(default = "nan_default", with = "non_finite")]
492    pub interface_cda: f64,
493    /// Total public attributes.
494    pub total: u64,
495    /// Total attributes.
496    pub total_attributes: u64,
497    /// Overall data accessibility ratio.
498    #[serde(default = "nan_default", with = "non_finite")]
499    pub cda: f64,
500}
501
502impl From<&npa::Stats> for Npa {
503    fn from(s: &npa::Stats) -> Self {
504        Self {
505            class_npa_sum: s.class_npa_sum(),
506            interface_npa_sum: s.interface_npa_sum(),
507            class_attributes: s.class_na_sum(),
508            interface_attributes: s.interface_na_sum(),
509            class_cda: s.class_cda(),
510            interface_cda: s.interface_cda(),
511            total: s.total_npa(),
512            total_attributes: s.total_na(),
513            cda: s.total_cda(),
514        }
515    }
516}
517
518/// Wire form of the `Npm` (number-of-public-methods) metric.
519#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
520pub struct Npm {
521    /// Sum of public methods across classes.
522    pub class_npm_sum: u64,
523    /// Sum of public methods across interfaces.
524    pub interface_npm_sum: u64,
525    /// Sum of all class methods.
526    pub class_methods: u64,
527    /// Sum of all interface methods.
528    pub interface_methods: u64,
529    /// Class operation accessibility ratio.
530    #[serde(default = "nan_default", with = "non_finite")]
531    pub class_coa: f64,
532    /// Interface operation accessibility ratio.
533    #[serde(default = "nan_default", with = "non_finite")]
534    pub interface_coa: f64,
535    /// Total public methods.
536    pub total: u64,
537    /// Total methods.
538    pub total_methods: u64,
539    /// Overall operation accessibility ratio.
540    #[serde(default = "nan_default", with = "non_finite")]
541    pub coa: f64,
542}
543
544impl From<&npm::Stats> for Npm {
545    fn from(s: &npm::Stats) -> Self {
546        Self {
547            class_npm_sum: s.class_npm_sum(),
548            interface_npm_sum: s.interface_npm_sum(),
549            class_methods: s.class_nm_sum(),
550            interface_methods: s.interface_nm_sum(),
551            class_coa: s.class_coa(),
552            interface_coa: s.interface_coa(),
553            total: s.total_npm(),
554            total_methods: s.total_nm(),
555            coa: s.total_coa(),
556        }
557    }
558}
559
560/// Wire form of the `Tokens` metric.
561#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
562pub struct Tokens {
563    /// Token-count sum across the space.
564    pub tokens: u64,
565    /// Average tokens per space.
566    #[serde(default = "nan_default", with = "non_finite")]
567    pub average: f64,
568    /// Minimum tokens in a single space.
569    pub min: u64,
570    /// Maximum tokens in a single space.
571    pub max: u64,
572}
573
574impl From<&tokens::Stats> for Tokens {
575    fn from(s: &tokens::Stats) -> Self {
576        Self {
577            tokens: s.tokens_sum(),
578            average: s.tokens_average(),
579            min: s.tokens_min(),
580            max: s.tokens_max(),
581        }
582    }
583}
584
585/// Wire form of the `Wmc` (weighted-methods-per-class) metric.
586#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
587pub struct Wmc {
588    /// Sum of weighted methods across classes.
589    pub class_wmc_sum: u64,
590    /// Sum of weighted methods across interfaces.
591    pub interface_wmc_sum: u64,
592    /// Total weighted methods.
593    pub total: u64,
594}
595
596impl From<&wmc::Stats> for Wmc {
597    fn from(s: &wmc::Stats) -> Self {
598        Self {
599            class_wmc_sum: s.class_wmc_sum(),
600            interface_wmc_sum: s.interface_wmc_sum(),
601            total: s.total_wmc(),
602        }
603    }
604}