Skip to main content

grex_core/vars/
error.rs

1//! Error type for variable expansion.
2//!
3//! All failures produced by [`crate::vars::expand`] surface as
4//! [`VarExpandError`]. Every variant carries a byte offset into the input
5//! string so CLI callers can highlight the offending position, and — where
6//! applicable — the variable name involved. Messages are designed to stand
7//! alone via `Display` (no additional context needed from the caller).
8
9use thiserror::Error;
10
11/// Errors produced by [`crate::vars::expand`].
12///
13/// Each variant is designed to be self-describing when rendered through
14/// `Display` (via `thiserror`), so `eprintln!("{err}")` is sufficient for a
15/// CLI diagnostic. The `offset` field always points at the first byte of the
16/// offending placeholder (`$`, `${`, or `%`), never inside it.
17///
18/// Marked `#[non_exhaustive]` so new diagnostic variants can land without
19/// breaking external match sites.
20#[non_exhaustive]
21#[derive(Debug, Error, PartialEq, Eq)]
22pub enum VarExpandError {
23    /// A well-formed placeholder referenced a variable name that was not
24    /// present in the [`crate::vars::VarEnv`].
25    #[error("variable `{name}` is not set (at byte offset {offset})")]
26    MissingVariable {
27        /// Variable name as written in the input.
28        name: String,
29        /// Byte offset of the opening sigil (`$` or `%`).
30        offset: usize,
31    },
32
33    /// A placeholder contained a name that does not match
34    /// `^[A-Za-z_][A-Za-z0-9_]*$`.
35    #[error("invalid variable name {got:?} (at byte offset {offset}): must match ^[A-Za-z_][A-Za-z0-9_]*$")]
36    InvalidVariableName {
37        /// Raw name bytes as scanned from the input (lossy if non-UTF-8).
38        got: String,
39        /// Byte offset of the opening sigil.
40        offset: usize,
41    },
42
43    /// A `${` placeholder was never closed with `}` before end of input.
44    #[error("unclosed `${{...}}` expansion starting at byte offset {offset}")]
45    UnclosedBraceExpansion {
46        /// Byte offset of the opening `$`.
47        offset: usize,
48    },
49
50    /// A `%` opening sigil was never matched by a closing `%` before end of
51    /// input.
52    #[error("unclosed `%...%` expansion starting at byte offset {offset}")]
53    UnclosedPercentExpansion {
54        /// Byte offset of the opening `%`.
55        offset: usize,
56    },
57
58    /// A braced placeholder had an empty name: `${}`.
59    #[error("empty `${{}}` expansion at byte offset {offset}")]
60    EmptyBraceExpansion {
61        /// Byte offset of the opening `$`.
62        offset: usize,
63    },
64}