Skip to main content

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}