big_code_analysis/spaces/options.rs
1//! `Default` and inherent impl blocks for [`super::MetricsOptions`].
2//!
3//! Split out of `spaces.rs` to keep that module focused on the public
4//! API type definitions. The blocks are moved verbatim; method and
5//! trait resolution is by type, so `crate::spaces::MetricsOptions`'s
6//! methods and `Default` impl resolve unchanged.
7
8use super::*;
9
10impl Default for MetricsOptions {
11 /// Defaults preserve every metric value emitted by the pre-#182
12 /// [`analyze`] entry point: every metric selected, tests
13 /// included, and Rust `?` counted toward cyclomatic (#409).
14 fn default() -> Self {
15 Self {
16 exclude_tests: false,
17 metrics: MetricSet::default(),
18 count_cyclomatic_try: true,
19 mi_charges_docs: false,
20 }
21 }
22}
23
24impl MetricsOptions {
25 /// Builder-style setter for `MetricsOptions::exclude_tests`.
26 ///
27 /// Provided because `MetricsOptions` is `#[non_exhaustive]` — the
28 /// struct-literal form is unavailable to downstream crates, so
29 /// external callers chain `MetricsOptions::default()
30 /// .with_exclude_tests(true)` instead.
31 #[inline]
32 #[must_use]
33 pub fn with_exclude_tests(mut self, exclude_tests: bool) -> Self {
34 self.exclude_tests = exclude_tests;
35 self
36 }
37
38 /// Builder-style setter for `MetricsOptions::count_cyclomatic_try`.
39 ///
40 /// Pass `false` to stop Rust's `?` operator from contributing to
41 /// cyclomatic complexity (standard and modified). The default is
42 /// `true`, which keeps every published metric value unchanged
43 /// (#409). Inert for non-Rust languages, none of which emit the
44 /// `try_expression` grammar node.
45 #[inline]
46 #[must_use]
47 pub fn with_count_cyclomatic_try(mut self, count: bool) -> Self {
48 self.count_cyclomatic_try = count;
49 self
50 }
51
52 /// Builder-style setter for `MetricsOptions::mi_charges_docs`.
53 ///
54 /// Pass `true` to count documentation rows as comments in `mi.sei`'s
55 /// comment ratio, `cloc / sloc` — the inputs the comment share read
56 /// before #1597, on the *current* LOC classification. The default,
57 /// `false`, forms the ratio as `(cloc − dloc) / (sloc −` the `dloc`
58 /// rows that carry no code`)`, so a docstring, a `///` block or a
59 /// Javadoc comment does not move it. Every `mi.*` variant sizes a
60 /// space on `ploc` in both modes (#1607), so `mi.original` and
61 /// `mi.visual_studio` read the same either way and only `mi.sei`
62 /// moves; charging documentation can only raise it. It does not
63 /// reproduce an earlier release's numbers. Only `mi.*` reads this:
64 /// `loc.dloc` and every other metric are the same in both modes.
65 #[inline]
66 #[must_use]
67 pub fn with_mi_charges_docs(mut self, charges_docs: bool) -> Self {
68 self.mi_charges_docs = charges_docs;
69 self
70 }
71
72 /// Restrict computation to the given metrics. Metrics outside
73 /// this set are skipped during the walk; their `Stats` fields on
74 /// [`CodeMetrics`] remain at their `Default` value and are
75 /// elided from the [`Serialize`](serde::Serialize) output. Pass an empty slice to
76 /// disable every metric (the walker still runs and produces the
77 /// space tree, but no metric values are populated).
78 ///
79 /// # Dependencies
80 ///
81 /// Derived metrics implicitly pull in the inputs they require:
82 ///
83 /// - [`Metric::Mi`] adds [`Metric::Loc`], [`Metric::Cyclomatic`],
84 /// [`Metric::Halstead`].
85 /// - [`Metric::Wmc`] adds [`Metric::Cyclomatic`] and
86 /// [`Metric::Nom`].
87 ///
88 /// This auto-resolution is silent: a caller asking for `Mi`
89 /// alone gets a populated `Mi` value, not a zero. See
90 /// [`Metric::dependencies`] for the source of truth.
91 ///
92 /// # Examples
93 ///
94 /// ```
95 /// use big_code_analysis::{Metric, MetricsOptions};
96 ///
97 /// // Compute LoC only.
98 /// let _opts = MetricsOptions::default().with_only(&[Metric::Loc]);
99 ///
100 /// // Compute Mi: Loc + Cyclomatic + Halstead are auto-added.
101 /// let _opts = MetricsOptions::default().with_only(&[Metric::Mi]);
102 /// ```
103 #[inline]
104 #[must_use]
105 pub fn with_only(mut self, metrics: &[Metric]) -> Self {
106 self.metrics = MetricSet::from_slice_with_deps(metrics);
107 self
108 }
109
110 /// Restrict computation to the metrics in `metrics`, closing the
111 /// set under [`Metric::dependencies`] before storing it.
112 ///
113 /// Like [`MetricsOptions::with_only`], a derived metric pulls in
114 /// the inputs it needs: passing `MetricSet::empty().with(Metric::Mi)`
115 /// also selects [`Metric::Loc`], [`Metric::Cyclomatic`], and
116 /// [`Metric::Halstead`], so the maintainability index is computed
117 /// from real inputs rather than zero-valued defaults (#743). The
118 /// resolution is idempotent: an already-closed set is stored
119 /// unchanged.
120 ///
121 /// Use this builder when you already hold a [`MetricSet`]; reach
122 /// for [`MetricsOptions::with_only`] when you have a `&[Metric]`.
123 ///
124 /// # Examples
125 ///
126 /// ```
127 /// use big_code_analysis::{Metric, MetricSet, MetricsOptions};
128 ///
129 /// // `Mi` alone — Loc + Cyclomatic + Halstead are auto-added so the
130 /// // resulting MI value is meaningful.
131 /// let set = MetricSet::empty().with(Metric::Mi);
132 /// let _opts = MetricsOptions::default().with_metric_set(set);
133 /// ```
134 #[inline]
135 #[must_use]
136 pub fn with_metric_set(mut self, metrics: MetricSet) -> Self {
137 self.metrics = metrics.resolved();
138 self
139 }
140}