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}