Skip to main content

big_code_analysis/
error.rs

1//! Error type returned from the library's top-level entry points.
2//!
3//! Prior to this module, every entry point returned `Option<…>` and
4//! collapsed parse failure, empty input, non-UTF-8 paths, and
5//! disabled-language builds into a single `None`. [`MetricsError`]
6//! distinguishes those cases so library consumers can react
7//! appropriately (e.g. log the parse failure but skip a non-UTF-8
8//! path).
9//!
10//! New variants may be added in future minor versions, so consumers
11//! must include a `_` arm when matching exhaustively — this is enforced
12//! by the [`#[non_exhaustive]`][non_exhaustive] attribute on the enum.
13//!
14//! [non_exhaustive]: https://doc.rust-lang.org/reference/attributes/type_system.html#the-non_exhaustive-attribute
15
16use crate::LANG;
17
18/// Error returned by the library's metric-computation entry points.
19///
20/// # Stability
21///
22/// The variant set is additive: new variants may be introduced in
23/// minor versions, so the enum is marked `#[non_exhaustive]`. Existing
24/// variants will not be removed without a major version bump. The
25/// [`std::error::Error`] and [`std::fmt::Display`] impls are part of
26/// the stable surface; the exact wording of the `Display` output is
27/// not.
28///
29/// # Examples
30///
31/// Most variants are reserved for features that have not yet landed
32/// (see each variant's documentation for the issue tracking it). The
33/// exception is [`MetricsError::LanguageDisabled`], which is
34/// produced by every dispatch entry point when the caller selects a
35/// [`LANG`] whose per-language Cargo feature is not enabled in the
36/// current build (see #252). The example exercises the happy path
37/// and demonstrates the exhaustive-with-`_` match shape that callers
38/// should adopt to stay forward-compatible with future variants.
39///
40/// ```
41/// use big_code_analysis::{analyze, MetricsError, MetricsOptions, Source, LANG};
42///
43/// let source = Source::new(LANG::Cpp, b"int a = 42;");
44/// let result = analyze(source, MetricsOptions::default());
45///
46/// // Today this call succeeds; the match below documents the shape
47/// // callers must adopt so adding a future variant is non-breaking.
48/// assert!(result.is_ok());
49///
50/// match result {
51///     Ok(_space) => {}
52///     Err(MetricsError::EmptyRoot) => {
53///         // Reserved: walker produced no top-level FuncSpace.
54///     }
55///     Err(MetricsError::ParseHasErrors) => {
56///         // Reserved: future strict-parsing toggle on `MetricsOptions`.
57///     }
58///     Err(MetricsError::LanguageDisabled(_lang)) => {
59///         // The `LANG` variant the caller asked for has no grammar
60///         // crate compiled in for this build (per-language feature
61///         // disabled — see the `[features]` table in Cargo.toml).
62///     }
63///     Err(MetricsError::NonUtf8Path) => {
64///         // Reserved: strict-identifier mode (see issue #254).
65///     }
66///     // `MetricsError` is `#[non_exhaustive]`; new variants may be added.
67///     Err(_) => {}
68/// }
69/// ```
70#[non_exhaustive]
71#[derive(Clone, Copy, Debug, PartialEq, Eq)]
72pub enum MetricsError {
73    /// The walker produced no top-level [`FuncSpace`][crate::FuncSpace].
74    ///
75    /// Reserved — not produced today. `metrics_with_options` always
76    /// pushes a synthetic top-level [`SpaceKind::Unit`][crate::SpaceKind]
77    /// `FuncSpace` onto its state stack before walking the AST, so
78    /// every parse — including empty input, whitespace-only input,
79    /// and comment-only input — currently returns
80    /// `Ok(FuncSpace { kind: Unit, .. })`. The variant is kept for a
81    /// future walker change that lets the state stack legitimately
82    /// drain to empty (e.g. an option that suppresses the synthetic
83    /// root for sources with no parseable structure).
84    ///
85    /// [`FuncSpace`]: crate::FuncSpace
86    EmptyRoot,
87    /// The requested [`LANG`] is not enabled in this build.
88    ///
89    /// Produced by every dispatch entry point
90    /// ([`crate::analyze`], [`crate::metrics_from_tree`],
91    /// [`crate::action`], [`crate::get_ops`], the deprecated
92    /// `get_function_spaces*` shims, and [`crate::LANG::get_tree_sitter_language`])
93    /// when the caller selects a [`LANG`] variant whose per-language
94    /// Cargo feature is not enabled in the current build — see the
95    /// `[features]` table in the root `Cargo.toml` for the list.
96    /// The default feature set (`default = ["all-languages"]`) keeps
97    /// every grammar compiled in, matching the library's historical
98    /// behaviour; callers that opt into a narrower set with
99    /// `--no-default-features --features rust,…` are the only ones
100    /// that observe this variant.
101    LanguageDisabled(LANG),
102    /// The supplied path could not be losslessly converted to UTF-8.
103    ///
104    /// Reserved for callers that opt into strict-identifier mode.
105    /// As of #254, the recommended [`crate::analyze`] entry point
106    /// accepts a [`crate::Source`] with an explicit
107    /// `Source::name: Option<String>` so callers never need to round-
108    /// trip a non-UTF-8 path through lossy conversion in the first
109    /// place. The deprecated path-positional entry points
110    /// (`metrics`, [`crate::get_function_spaces`], …) still
111    /// fall back to `Path::to_string_lossy`. This variant is not
112    /// produced today; it is kept for future strict-identifier
113    /// validators that reject lossy names up front.
114    NonUtf8Path,
115    /// The tree-sitter parse tree contains syntax errors and the
116    /// caller opted into strict mode.
117    ///
118    /// Reserved for a future strict-parsing toggle on
119    /// [`MetricsOptions`][crate::MetricsOptions]; the current entry
120    /// points still tolerate `ERROR` nodes and compute best-effort
121    /// metrics, so this variant is not produced today.
122    ParseHasErrors,
123}
124
125impl std::fmt::Display for MetricsError {
126    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
127        match self {
128            Self::EmptyRoot => {
129                f.write_str("no top-level FuncSpace could be produced from the source AST")
130            }
131            Self::LanguageDisabled(lang) => {
132                write!(
133                    f,
134                    "language {} is not enabled in this build",
135                    lang.get_name()
136                )
137            }
138            Self::NonUtf8Path => f.write_str("path is not valid UTF-8"),
139            Self::ParseHasErrors => f.write_str("tree-sitter parse tree contains syntax errors"),
140        }
141    }
142}
143
144impl std::error::Error for MetricsError {}