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 pre-#958 JSON (which lacks the
82    /// field) still deserializes — e.g. when `bca diff` reads an older
83    /// metrics file.
84    #[serde(default)]
85    pub value: u64,
86    /// Average cognitive complexity per function.
87    #[serde(default = "nan_default", with = "non_finite")]
88    pub average: f64,
89    /// Minimum cognitive complexity in a single function.
90    pub min: u64,
91    /// Maximum cognitive complexity in a single function.
92    pub max: u64,
93}
94
95impl From<&cognitive::Stats> for Cognitive {
96    fn from(s: &cognitive::Stats) -> Self {
97        Self {
98            sum: s.cognitive_sum(),
99            value: s.cognitive(),
100            average: s.cognitive_average(),
101            min: s.cognitive_min(),
102            max: s.cognitive_max(),
103        }
104    }
105}
106
107/// Wire form of the modified-cyclomatic sub-record.
108#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
109pub struct CyclomaticModified {
110    /// Modified-cyclomatic sum across the space.
111    pub sum: u64,
112    /// This space's own modified-cyclomatic complexity, excluding nested
113    /// function/closure spaces (see [`Cyclomatic::value`]).
114    #[serde(default)]
115    pub value: u64,
116    /// Average modified cyclomatic complexity per function.
117    #[serde(default = "nan_default", with = "non_finite")]
118    pub average: f64,
119    /// Minimum modified cyclomatic complexity in a single function.
120    pub min: u64,
121    /// Maximum modified cyclomatic complexity in a single function.
122    pub max: u64,
123}
124
125/// Wire form of the `Cyclomatic` metric.
126#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
127pub struct Cyclomatic {
128    /// Cyclomatic-complexity sum across the space.
129    pub sum: u64,
130    /// This space's own cyclomatic complexity, excluding nested
131    /// function/closure spaces. Equals [`sum`](Self::sum) only at a leaf
132    /// space; at an interior space the sum rolls up descendants while
133    /// this stays the per-space scalar the CLI thresholds against
134    /// (#958). `#[serde(default)]` so pre-#958 JSON still deserializes.
135    #[serde(default)]
136    pub value: u64,
137    /// Average cyclomatic complexity per function.
138    #[serde(default = "nan_default", with = "non_finite")]
139    pub average: f64,
140    /// Minimum cyclomatic complexity in a single function.
141    pub min: u64,
142    /// Maximum cyclomatic complexity in a single function.
143    pub max: u64,
144    /// The modified-cyclomatic projection.
145    pub modified: CyclomaticModified,
146}
147
148impl From<&cyclomatic::Stats> for Cyclomatic {
149    fn from(s: &cyclomatic::Stats) -> Self {
150        Self {
151            sum: s.cyclomatic_sum(),
152            value: s.cyclomatic(),
153            average: s.cyclomatic_average(),
154            min: s.cyclomatic_min(),
155            max: s.cyclomatic_max(),
156            modified: CyclomaticModified {
157                sum: s.cyclomatic_modified_sum(),
158                value: s.cyclomatic_modified(),
159                average: s.cyclomatic_modified_average(),
160                min: s.cyclomatic_modified_min(),
161                max: s.cyclomatic_modified_max(),
162            },
163        }
164    }
165}
166
167/// Wire form of the `Nexits` (exit-points) metric.
168#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
169pub struct Nexits {
170    /// Exit-point sum across the space.
171    pub sum: u64,
172    /// Average exit points per function.
173    #[serde(default = "nan_default", with = "non_finite")]
174    pub average: f64,
175    /// Minimum exit points in a single function.
176    pub min: u64,
177    /// Maximum exit points in a single function.
178    pub max: u64,
179}
180
181impl From<&nexits::Stats> for Nexits {
182    fn from(s: &nexits::Stats) -> Self {
183        Self {
184            sum: s.nexits_sum(),
185            average: s.nexits_average(),
186            min: s.nexits_min(),
187            max: s.nexits_max(),
188        }
189    }
190}
191
192/// Wire form of the `Halstead` metric suite.
193#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
194pub struct Halstead {
195    /// Number of distinct operators (`n1`).
196    pub unique_operators: u64,
197    /// Total operator occurrences (`N1`).
198    pub total_operators: u64,
199    /// Number of distinct operands (`n2`).
200    pub unique_operands: u64,
201    /// Total operand occurrences (`N2`).
202    pub total_operands: u64,
203    /// Program length (`N = N1 + N2`).
204    pub length: u64,
205    /// Estimated program length.
206    #[serde(default = "nan_default", with = "non_finite")]
207    pub estimated_program_length: f64,
208    /// Purity ratio (estimated length / length).
209    #[serde(default = "nan_default", with = "non_finite")]
210    pub purity_ratio: f64,
211    /// Program vocabulary (`n = n1 + n2`).
212    pub vocabulary: u64,
213    /// Program volume.
214    #[serde(default = "nan_default", with = "non_finite")]
215    pub volume: f64,
216    /// Difficulty.
217    #[serde(default = "nan_default", with = "non_finite")]
218    pub difficulty: f64,
219    /// Program level (inverse difficulty).
220    #[serde(default = "nan_default", with = "non_finite")]
221    pub level: f64,
222    /// Effort.
223    #[serde(default = "nan_default", with = "non_finite")]
224    pub effort: f64,
225    /// Estimated time to program (seconds).
226    #[serde(default = "nan_default", with = "non_finite")]
227    pub time: f64,
228    /// Estimated number of delivered bugs.
229    #[serde(default = "nan_default", with = "non_finite")]
230    pub bugs: f64,
231}
232
233impl From<&halstead::Stats> for Halstead {
234    fn from(s: &halstead::Stats) -> Self {
235        Self {
236            unique_operators: s.unique_operators(),
237            total_operators: s.total_operators(),
238            unique_operands: s.unique_operands(),
239            total_operands: s.total_operands(),
240            length: s.length(),
241            estimated_program_length: s.estimated_program_length(),
242            purity_ratio: s.purity_ratio(),
243            vocabulary: s.vocabulary(),
244            volume: s.volume(),
245            difficulty: s.difficulty(),
246            level: s.level(),
247            effort: s.effort(),
248            time: s.time(),
249            bugs: s.bugs(),
250        }
251    }
252}
253
254/// Wire form of the `Loc` metric suite.
255#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
256pub struct Loc {
257    /// Source lines of code.
258    pub sloc: u64,
259    /// Physical lines of code.
260    pub ploc: u64,
261    /// Logical lines of code.
262    pub lloc: u64,
263    /// Comment lines of code.
264    pub cloc: u64,
265    /// Blank lines.
266    pub blank: u64,
267    /// Average SLOC per space.
268    #[serde(default = "nan_default", with = "non_finite")]
269    pub sloc_average: f64,
270    /// Average PLOC per space.
271    #[serde(default = "nan_default", with = "non_finite")]
272    pub ploc_average: f64,
273    /// Average LLOC per space.
274    #[serde(default = "nan_default", with = "non_finite")]
275    pub lloc_average: f64,
276    /// Average CLOC per space.
277    #[serde(default = "nan_default", with = "non_finite")]
278    pub cloc_average: f64,
279    /// Average blank lines per space.
280    #[serde(default = "nan_default", with = "non_finite")]
281    pub blank_average: f64,
282    /// Minimum SLOC in a single space.
283    pub sloc_min: u64,
284    /// Maximum SLOC in a single space.
285    pub sloc_max: u64,
286    /// Minimum CLOC in a single space.
287    pub cloc_min: u64,
288    /// Maximum CLOC in a single space.
289    pub cloc_max: u64,
290    /// Minimum PLOC in a single space.
291    pub ploc_min: u64,
292    /// Maximum PLOC in a single space.
293    pub ploc_max: u64,
294    /// Minimum LLOC in a single space.
295    pub lloc_min: u64,
296    /// Maximum LLOC in a single space.
297    pub lloc_max: u64,
298    /// Minimum blank lines in a single space.
299    pub blank_min: u64,
300    /// Maximum blank lines in a single space.
301    pub blank_max: u64,
302}
303
304impl From<&loc::Stats> for Loc {
305    fn from(s: &loc::Stats) -> Self {
306        Self {
307            sloc: s.sloc(),
308            ploc: s.ploc(),
309            lloc: s.lloc(),
310            cloc: s.cloc(),
311            blank: s.blank(),
312            sloc_average: s.sloc_average(),
313            ploc_average: s.ploc_average(),
314            lloc_average: s.lloc_average(),
315            cloc_average: s.cloc_average(),
316            blank_average: s.blank_average(),
317            sloc_min: s.sloc_min(),
318            sloc_max: s.sloc_max(),
319            cloc_min: s.cloc_min(),
320            cloc_max: s.cloc_max(),
321            ploc_min: s.ploc_min(),
322            ploc_max: s.ploc_max(),
323            lloc_min: s.lloc_min(),
324            lloc_max: s.lloc_max(),
325            blank_min: s.blank_min(),
326            blank_max: s.blank_max(),
327        }
328    }
329}
330
331/// Wire form of the `Mi` (maintainability index) metric.
332#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
333pub struct Mi {
334    /// Original maintainability index.
335    #[serde(default = "nan_default", with = "non_finite")]
336    pub original: f64,
337    /// SEI-derivative maintainability index.
338    #[serde(default = "nan_default", with = "non_finite")]
339    pub sei: f64,
340    /// Visual Studio-derivative maintainability index.
341    #[serde(default = "nan_default", with = "non_finite")]
342    pub visual_studio: f64,
343}
344
345impl From<&mi::Stats> for Mi {
346    fn from(s: &mi::Stats) -> Self {
347        Self {
348            original: s.original(),
349            sei: s.sei(),
350            visual_studio: s.visual_studio(),
351        }
352    }
353}
354
355/// Wire form of the `NArgs` metric.
356#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
357pub struct Nargs {
358    /// Sum of function arguments.
359    pub function_args: u64,
360    /// Sum of closure arguments.
361    pub closure_args: u64,
362    /// Average function arguments per function.
363    #[serde(default = "nan_default", with = "non_finite")]
364    pub function_args_average: f64,
365    /// Average closure arguments per closure.
366    #[serde(default = "nan_default", with = "non_finite")]
367    pub closure_args_average: f64,
368    /// Total arguments (functions + closures).
369    pub total: u64,
370    /// Average arguments per function/closure.
371    #[serde(default = "nan_default", with = "non_finite")]
372    pub average: f64,
373    /// Minimum function arguments in a single function.
374    pub function_args_min: u64,
375    /// Maximum function arguments in a single function.
376    pub function_args_max: u64,
377    /// Minimum closure arguments in a single closure.
378    pub closure_args_min: u64,
379    /// Maximum closure arguments in a single closure.
380    pub closure_args_max: u64,
381}
382
383impl From<&nargs::Stats> for Nargs {
384    fn from(s: &nargs::Stats) -> Self {
385        Self {
386            function_args: s.function_args_sum(),
387            closure_args: s.closure_args_sum(),
388            function_args_average: s.function_args_average(),
389            closure_args_average: s.closure_args_average(),
390            total: s.total(),
391            average: s.average(),
392            function_args_min: s.function_args_min(),
393            function_args_max: s.function_args_max(),
394            closure_args_min: s.closure_args_min(),
395            closure_args_max: s.closure_args_max(),
396        }
397    }
398}
399
400/// Wire form of the `Nom` (number-of-methods) metric.
401#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
402pub struct Nom {
403    /// Sum of function definitions.
404    pub functions: u64,
405    /// Sum of closures.
406    pub closures: u64,
407    /// Average function definitions per space.
408    #[serde(default = "nan_default", with = "non_finite")]
409    pub functions_average: f64,
410    /// Average closures per space.
411    #[serde(default = "nan_default", with = "non_finite")]
412    pub closures_average: f64,
413    /// Total functions + closures.
414    pub total: u64,
415    /// Average functions + closures per space.
416    #[serde(default = "nan_default", with = "non_finite")]
417    pub average: f64,
418    /// Minimum function definitions in a single space.
419    pub functions_min: u64,
420    /// Maximum function definitions in a single space.
421    pub functions_max: u64,
422    /// Minimum closures in a single space.
423    pub closures_min: u64,
424    /// Maximum closures in a single space.
425    pub closures_max: u64,
426}
427
428impl From<&nom::Stats> for Nom {
429    fn from(s: &nom::Stats) -> Self {
430        Self {
431            functions: s.functions_sum(),
432            closures: s.closures_sum(),
433            functions_average: s.functions_average(),
434            closures_average: s.closures_average(),
435            total: s.total(),
436            average: s.average(),
437            functions_min: s.functions_min(),
438            functions_max: s.functions_max(),
439            closures_min: s.closures_min(),
440            closures_max: s.closures_max(),
441        }
442    }
443}
444
445/// Wire form of the `Npa` (number-of-public-attributes) metric.
446#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
447pub struct Npa {
448    /// Sum of public attributes across classes.
449    pub class_npa_sum: u64,
450    /// Sum of public attributes across interfaces.
451    pub interface_npa_sum: u64,
452    /// Sum of all class attributes.
453    pub class_attributes: u64,
454    /// Sum of all interface attributes.
455    pub interface_attributes: u64,
456    /// Class data accessibility ratio.
457    #[serde(default = "nan_default", with = "non_finite")]
458    pub class_cda: f64,
459    /// Interface data accessibility ratio.
460    #[serde(default = "nan_default", with = "non_finite")]
461    pub interface_cda: f64,
462    /// Total public attributes.
463    pub total: u64,
464    /// Total attributes.
465    pub total_attributes: u64,
466    /// Overall data accessibility ratio.
467    #[serde(default = "nan_default", with = "non_finite")]
468    pub cda: f64,
469}
470
471impl From<&npa::Stats> for Npa {
472    fn from(s: &npa::Stats) -> Self {
473        Self {
474            class_npa_sum: s.class_npa_sum(),
475            interface_npa_sum: s.interface_npa_sum(),
476            class_attributes: s.class_na_sum(),
477            interface_attributes: s.interface_na_sum(),
478            class_cda: s.class_cda(),
479            interface_cda: s.interface_cda(),
480            total: s.total_npa(),
481            total_attributes: s.total_na(),
482            cda: s.total_cda(),
483        }
484    }
485}
486
487/// Wire form of the `Npm` (number-of-public-methods) metric.
488#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
489pub struct Npm {
490    /// Sum of public methods across classes.
491    pub class_npm_sum: u64,
492    /// Sum of public methods across interfaces.
493    pub interface_npm_sum: u64,
494    /// Sum of all class methods.
495    pub class_methods: u64,
496    /// Sum of all interface methods.
497    pub interface_methods: u64,
498    /// Class operation accessibility ratio.
499    #[serde(default = "nan_default", with = "non_finite")]
500    pub class_coa: f64,
501    /// Interface operation accessibility ratio.
502    #[serde(default = "nan_default", with = "non_finite")]
503    pub interface_coa: f64,
504    /// Total public methods.
505    pub total: u64,
506    /// Total methods.
507    pub total_methods: u64,
508    /// Overall operation accessibility ratio.
509    #[serde(default = "nan_default", with = "non_finite")]
510    pub coa: f64,
511}
512
513impl From<&npm::Stats> for Npm {
514    fn from(s: &npm::Stats) -> Self {
515        Self {
516            class_npm_sum: s.class_npm_sum(),
517            interface_npm_sum: s.interface_npm_sum(),
518            class_methods: s.class_nm_sum(),
519            interface_methods: s.interface_nm_sum(),
520            class_coa: s.class_coa(),
521            interface_coa: s.interface_coa(),
522            total: s.total_npm(),
523            total_methods: s.total_nm(),
524            coa: s.total_coa(),
525        }
526    }
527}
528
529/// Wire form of the `Tokens` metric.
530#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
531pub struct Tokens {
532    /// Token-count sum across the space.
533    pub tokens: u64,
534    /// Average tokens per space.
535    #[serde(default = "nan_default", with = "non_finite")]
536    pub average: f64,
537    /// Minimum tokens in a single space.
538    pub min: u64,
539    /// Maximum tokens in a single space.
540    pub max: u64,
541}
542
543impl From<&tokens::Stats> for Tokens {
544    fn from(s: &tokens::Stats) -> Self {
545        Self {
546            tokens: s.tokens_sum(),
547            average: s.tokens_average(),
548            min: s.tokens_min(),
549            max: s.tokens_max(),
550        }
551    }
552}
553
554/// Wire form of the `Wmc` (weighted-methods-per-class) metric.
555#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
556pub struct Wmc {
557    /// Sum of weighted methods across classes.
558    pub class_wmc_sum: u64,
559    /// Sum of weighted methods across interfaces.
560    pub interface_wmc_sum: u64,
561    /// Total weighted methods.
562    pub total: u64,
563}
564
565impl From<&wmc::Stats> for Wmc {
566    fn from(s: &wmc::Stats) -> Self {
567        Self {
568            class_wmc_sum: s.class_wmc_sum(),
569            interface_wmc_sum: s.interface_wmc_sum(),
570            total: s.total_wmc(),
571        }
572    }
573}