Skip to main content

rlx_core/preset/schema/
error.rs

1//! [`PresetError`]: why a preset failed to load.
2//!
3//! Every variant is recoverable. A bad preset returns `Err` and never panics,
4//! so the caller degrades to the last good preset (ADR-0002 / NFR 10).
5
6// A continuation of one module split across several files, so it needs the
7// names `preset/schema/mod.rs` has in scope.
8use super::*;
9
10/// Why a preset failed to load. Every variant is recoverable — the caller
11/// keeps the previous good preset.
12#[derive(Debug)]
13pub enum PresetError {
14    /// The TOML itself was malformed.
15    Toml(toml::de::Error),
16    /// `system` named a built-in that does not exist.
17    UnknownSystem(String),
18    /// A parameter's expression failed to compile.
19    Expr {
20        /// The parameter whose expression was invalid.
21        param: String,
22        /// The compile error.
23        err: ExprError,
24    },
25    /// A structural-config table (`[curve]`/`[generator]`) was invalid — an
26    /// unknown family, an out-of-range value, an undefined grammar symbol.
27    Config(String),
28    /// The preset file could not be read (message from the I/O error).
29    Io(String),
30}
31
32impl PresetError {
33    /// The byte range in the preset source this error points at, when the parser
34    /// gave one.
35    ///
36    /// Only the TOML arm can have one: every other variant is raised by the
37    /// loader's own validation, which runs against parsed values rather than
38    /// against the document, and a value carries no position. A caller turns the
39    /// offset into a line and column against the source it read — which it still
40    /// has and this module never did.
41    pub fn span(&self) -> Option<std::ops::Range<usize>> {
42        match self {
43            PresetError::Toml(err) => err.span(),
44            _ => None,
45        }
46    }
47
48    /// The parameter whose expression failed to compile, for the arm that has
49    /// one.
50    ///
51    /// The name is already in [`Display`](fmt::Display)'s sentence; this is the
52    /// same fact as a field, so a structured consumer does not have to read it
53    /// back out of prose.
54    pub fn param(&self) -> Option<&str> {
55        match self {
56            PresetError::Expr { param, .. } => Some(param),
57            _ => None,
58        }
59    }
60}
61
62impl fmt::Display for PresetError {
63    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
64        match self {
65            PresetError::Toml(e) => write!(f, "invalid preset TOML: {e}"),
66            PresetError::UnknownSystem(s) => write!(f, "unknown system '{s}'"),
67            PresetError::Expr { param, err } => {
68                write!(f, "parameter '{param}' has an invalid expression: {err}")
69            }
70            PresetError::Config(msg) => write!(f, "invalid structural config: {msg}"),
71            PresetError::Io(msg) => write!(f, "could not read preset file: {msg}"),
72        }
73    }
74}
75
76impl std::error::Error for PresetError {}