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    /// Average SLOC per space.
269    #[serde(default = "nan_default", with = "non_finite")]
270    pub sloc_average: f64,
271    /// Average PLOC per space.
272    #[serde(default = "nan_default", with = "non_finite")]
273    pub ploc_average: f64,
274    /// Average LLOC per space.
275    #[serde(default = "nan_default", with = "non_finite")]
276    pub lloc_average: f64,
277    /// Average CLOC per space.
278    #[serde(default = "nan_default", with = "non_finite")]
279    pub cloc_average: f64,
280    /// Average blank lines per space.
281    #[serde(default = "nan_default", with = "non_finite")]
282    pub blank_average: f64,
283    /// Minimum SLOC in a single space.
284    pub sloc_min: u64,
285    /// Maximum SLOC in a single space.
286    pub sloc_max: u64,
287    /// Minimum CLOC in a single space.
288    pub cloc_min: u64,
289    /// Maximum CLOC in a single space.
290    pub cloc_max: u64,
291    /// Minimum PLOC in a single space.
292    pub ploc_min: u64,
293    /// Maximum PLOC in a single space.
294    pub ploc_max: u64,
295    /// Minimum LLOC in a single space.
296    pub lloc_min: u64,
297    /// Maximum LLOC in a single space.
298    pub lloc_max: u64,
299    /// Minimum blank lines in a single space.
300    pub blank_min: u64,
301    /// Maximum blank lines in a single space.
302    pub blank_max: u64,
303}
304
305impl From<&loc::Stats> for Loc {
306    fn from(s: &loc::Stats) -> Self {
307        Self {
308            sloc: s.sloc(),
309            ploc: s.ploc(),
310            lloc: s.lloc(),
311            cloc: s.cloc(),
312            blank: s.blank(),
313            sloc_average: s.sloc_average(),
314            ploc_average: s.ploc_average(),
315            lloc_average: s.lloc_average(),
316            cloc_average: s.cloc_average(),
317            blank_average: s.blank_average(),
318            sloc_min: s.sloc_min(),
319            sloc_max: s.sloc_max(),
320            cloc_min: s.cloc_min(),
321            cloc_max: s.cloc_max(),
322            ploc_min: s.ploc_min(),
323            ploc_max: s.ploc_max(),
324            lloc_min: s.lloc_min(),
325            lloc_max: s.lloc_max(),
326            blank_min: s.blank_min(),
327            blank_max: s.blank_max(),
328        }
329    }
330}
331
332/// Wire form of the `Mi` (maintainability index) metric.
333#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
334pub struct Mi {
335    /// Original maintainability index.
336    #[serde(default = "nan_default", with = "non_finite")]
337    pub original: f64,
338    /// SEI-derivative maintainability index.
339    #[serde(default = "nan_default", with = "non_finite")]
340    pub sei: f64,
341    /// Visual Studio-derivative maintainability index.
342    #[serde(default = "nan_default", with = "non_finite")]
343    pub visual_studio: f64,
344}
345
346impl From<&mi::Stats> for Mi {
347    fn from(s: &mi::Stats) -> Self {
348        Self {
349            original: s.original(),
350            sei: s.sei(),
351            visual_studio: s.visual_studio(),
352        }
353    }
354}
355
356/// Wire form of the `NArgs` metric.
357#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
358pub struct Nargs {
359    /// Sum of function arguments.
360    pub function_args: u64,
361    /// Sum of closure arguments.
362    pub closure_args: u64,
363    /// Average function arguments per function.
364    #[serde(default = "nan_default", with = "non_finite")]
365    pub function_args_average: f64,
366    /// Average closure arguments per closure.
367    #[serde(default = "nan_default", with = "non_finite")]
368    pub closure_args_average: f64,
369    /// Total arguments (functions + closures).
370    pub total: u64,
371    /// This space's own argument count — its function parameters plus
372    /// its closure parameters — excluding nested function/closure
373    /// spaces. Equals [`total`](Self::total) only at a leaf space; at an
374    /// interior space `total` rolls up descendants while this stays the
375    /// per-space scalar the CLI thresholds `nargs` against (#1196,
376    /// #1236). `#[serde(default)]` so a document written before #1236
377    /// still deserializes through the public `wire` read-back path
378    /// STABILITY.md advertises. (`bca diff` is *not* that consumer — it
379    /// walks a raw `serde_json::Value`, see `metric_diff.rs`.)
380    #[serde(default)]
381    pub value: u64,
382    /// Average arguments per function/closure.
383    #[serde(default = "nan_default", with = "non_finite")]
384    pub average: f64,
385    /// Minimum function arguments in a single function.
386    pub function_args_min: u64,
387    /// Maximum function arguments in a single function.
388    pub function_args_max: u64,
389    /// Minimum closure arguments in a single closure.
390    pub closure_args_min: u64,
391    /// Maximum closure arguments in a single closure.
392    pub closure_args_max: u64,
393}
394
395impl From<&nargs::Stats> for Nargs {
396    fn from(s: &nargs::Stats) -> Self {
397        Self {
398            function_args: s.function_args_sum(),
399            closure_args: s.closure_args_sum(),
400            function_args_average: s.function_args_average(),
401            closure_args_average: s.closure_args_average(),
402            total: s.total(),
403            value: s.own_args(),
404            average: s.average(),
405            function_args_min: s.function_args_min(),
406            function_args_max: s.function_args_max(),
407            closure_args_min: s.closure_args_min(),
408            closure_args_max: s.closure_args_max(),
409        }
410    }
411}
412
413/// Wire form of the `Nom` (number-of-methods) metric.
414#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
415pub struct Nom {
416    /// Sum of function definitions.
417    pub functions: u64,
418    /// Sum of closures.
419    pub closures: u64,
420    /// Average function definitions per space.
421    #[serde(default = "nan_default", with = "non_finite")]
422    pub functions_average: f64,
423    /// Average closures per space.
424    #[serde(default = "nan_default", with = "non_finite")]
425    pub closures_average: f64,
426    /// Total functions + closures.
427    pub total: u64,
428    /// Average functions + closures per space.
429    #[serde(default = "nan_default", with = "non_finite")]
430    pub average: f64,
431    /// Minimum function definitions in a single space.
432    pub functions_min: u64,
433    /// Maximum function definitions in a single space.
434    pub functions_max: u64,
435    /// Minimum closures in a single space.
436    pub closures_min: u64,
437    /// Maximum closures in a single space.
438    pub closures_max: u64,
439}
440
441impl From<&nom::Stats> for Nom {
442    fn from(s: &nom::Stats) -> Self {
443        Self {
444            functions: s.functions_sum(),
445            closures: s.closures_sum(),
446            functions_average: s.functions_average(),
447            closures_average: s.closures_average(),
448            total: s.total(),
449            average: s.average(),
450            functions_min: s.functions_min(),
451            functions_max: s.functions_max(),
452            closures_min: s.closures_min(),
453            closures_max: s.closures_max(),
454        }
455    }
456}
457
458/// Wire form of the `Npa` (number-of-public-attributes) metric.
459#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
460pub struct Npa {
461    /// Sum of public attributes across classes.
462    pub class_npa_sum: u64,
463    /// Sum of public attributes across interfaces.
464    pub interface_npa_sum: u64,
465    /// Sum of all class attributes.
466    pub class_attributes: u64,
467    /// Sum of all interface attributes.
468    pub interface_attributes: u64,
469    /// Class data accessibility ratio.
470    #[serde(default = "nan_default", with = "non_finite")]
471    pub class_cda: f64,
472    /// Interface data accessibility ratio.
473    #[serde(default = "nan_default", with = "non_finite")]
474    pub interface_cda: f64,
475    /// Total public attributes.
476    pub total: u64,
477    /// Total attributes.
478    pub total_attributes: u64,
479    /// Overall data accessibility ratio.
480    #[serde(default = "nan_default", with = "non_finite")]
481    pub cda: f64,
482}
483
484impl From<&npa::Stats> for Npa {
485    fn from(s: &npa::Stats) -> Self {
486        Self {
487            class_npa_sum: s.class_npa_sum(),
488            interface_npa_sum: s.interface_npa_sum(),
489            class_attributes: s.class_na_sum(),
490            interface_attributes: s.interface_na_sum(),
491            class_cda: s.class_cda(),
492            interface_cda: s.interface_cda(),
493            total: s.total_npa(),
494            total_attributes: s.total_na(),
495            cda: s.total_cda(),
496        }
497    }
498}
499
500/// Wire form of the `Npm` (number-of-public-methods) metric.
501#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
502pub struct Npm {
503    /// Sum of public methods across classes.
504    pub class_npm_sum: u64,
505    /// Sum of public methods across interfaces.
506    pub interface_npm_sum: u64,
507    /// Sum of all class methods.
508    pub class_methods: u64,
509    /// Sum of all interface methods.
510    pub interface_methods: u64,
511    /// Class operation accessibility ratio.
512    #[serde(default = "nan_default", with = "non_finite")]
513    pub class_coa: f64,
514    /// Interface operation accessibility ratio.
515    #[serde(default = "nan_default", with = "non_finite")]
516    pub interface_coa: f64,
517    /// Total public methods.
518    pub total: u64,
519    /// Total methods.
520    pub total_methods: u64,
521    /// Overall operation accessibility ratio.
522    #[serde(default = "nan_default", with = "non_finite")]
523    pub coa: f64,
524}
525
526impl From<&npm::Stats> for Npm {
527    fn from(s: &npm::Stats) -> Self {
528        Self {
529            class_npm_sum: s.class_npm_sum(),
530            interface_npm_sum: s.interface_npm_sum(),
531            class_methods: s.class_nm_sum(),
532            interface_methods: s.interface_nm_sum(),
533            class_coa: s.class_coa(),
534            interface_coa: s.interface_coa(),
535            total: s.total_npm(),
536            total_methods: s.total_nm(),
537            coa: s.total_coa(),
538        }
539    }
540}
541
542/// Wire form of the `Tokens` metric.
543#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
544pub struct Tokens {
545    /// Token-count sum across the space.
546    pub tokens: u64,
547    /// Average tokens per space.
548    #[serde(default = "nan_default", with = "non_finite")]
549    pub average: f64,
550    /// Minimum tokens in a single space.
551    pub min: u64,
552    /// Maximum tokens in a single space.
553    pub max: u64,
554}
555
556impl From<&tokens::Stats> for Tokens {
557    fn from(s: &tokens::Stats) -> Self {
558        Self {
559            tokens: s.tokens_sum(),
560            average: s.tokens_average(),
561            min: s.tokens_min(),
562            max: s.tokens_max(),
563        }
564    }
565}
566
567/// Wire form of the `Wmc` (weighted-methods-per-class) metric.
568#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
569pub struct Wmc {
570    /// Sum of weighted methods across classes.
571    pub class_wmc_sum: u64,
572    /// Sum of weighted methods across interfaces.
573    pub interface_wmc_sum: u64,
574    /// Total weighted methods.
575    pub total: u64,
576}
577
578impl From<&wmc::Stats> for Wmc {
579    fn from(s: &wmc::Stats) -> Self {
580        Self {
581            class_wmc_sum: s.class_wmc_sum(),
582            interface_wmc_sum: s.interface_wmc_sum(),
583            total: s.total_wmc(),
584        }
585    }
586}