Skip to main content

reliar_core/
settings.rs

1//! The shared error every `*Settings::from_env` returns (ADR 0019, ADR 0032).
2
3use core::fmt;
4
5/// Why a `*Settings::from_env` call failed. `OutboxSettings::from_env` (`reliar-outbox`) was the
6/// first caller; every provider's own `from_env` returns this same type, so a host wiring
7/// several `from_env` calls handles one error type for the whole family.
8///
9/// ```
10/// use reliar_core::SettingsError;
11///
12/// let err = SettingsError::parse("RELIAR_OUTBOX_BATCH_SIZE", "u32");
13/// assert_eq!(err.key(), "RELIAR_OUTBOX_BATCH_SIZE");
14/// assert!(err.to_string().contains("could not be parsed"));
15/// ```
16#[derive(Clone, Debug, PartialEq)]
17#[non_exhaustive]
18pub enum SettingsError {
19    /// A present variable could not be parsed as its declared type. The value is **never
20    /// echoed** — it may carry an operator's typo of something sensitive.
21    Parse {
22        /// The full environment variable name, including the prefix.
23        key: String,
24        /// The type or shape that was expected, e.g. `"u32"`, `"milliseconds"`.
25        value_kind: &'static str,
26    },
27
28    /// A present variable parsed but violated a documented bound.
29    OutOfRange {
30        /// The full environment variable name, including the prefix.
31        key: String,
32        /// The bound that was violated.
33        message: &'static str,
34    },
35}
36
37/// **Public constructors, because every provider's `from_env` returns this type.**
38/// `SettingsError` is `#[non_exhaustive]`, so a crate other than the one defining a given
39/// `Settings` type — e.g. `reliar-store-postgres` — cannot build a variant with struct-literal
40/// syntax; without these a provider is forced into a parallel, unrelated error type, and a host
41/// wiring two `from_env` calls ends up handling two different errors for the same class of
42/// failure (ADR 0019).
43impl SettingsError {
44    /// The variable was present but did not parse. `value_kind` names the expected shape
45    /// (`"u32"`, `"milliseconds"`); the offending **value is never carried**.
46    ///
47    /// ```
48    /// use reliar_core::SettingsError;
49    ///
50    /// let err = SettingsError::parse("RELIAR_OUTBOX_BATCH_SIZE", "u32");
51    /// assert_eq!(err.key(), "RELIAR_OUTBOX_BATCH_SIZE");
52    /// ```
53    #[must_use]
54    pub fn parse(key: impl Into<String>, value_kind: &'static str) -> Self {
55        Self::Parse {
56            key: key.into(),
57            value_kind,
58        }
59    }
60
61    /// The variable parsed but is outside the range the setting accepts.
62    ///
63    /// ```
64    /// use reliar_core::SettingsError;
65    ///
66    /// let err = SettingsError::out_of_range("RELIAR_OUTBOX_LEASE_SECS", "must be at least 1");
67    /// assert!(err.to_string().contains("out of range"));
68    /// ```
69    #[must_use]
70    pub fn out_of_range(key: impl Into<String>, message: &'static str) -> Self {
71        Self::OutOfRange {
72            key: key.into(),
73            message,
74        }
75    }
76
77    /// The full environment-variable name, prefix included — what an operator has to go fix.
78    ///
79    /// ```
80    /// use reliar_core::SettingsError;
81    ///
82    /// let err = SettingsError::out_of_range("RELIAR_OUTBOX_BATCH_SIZE", "must be at least 1");
83    /// assert_eq!(err.key(), "RELIAR_OUTBOX_BATCH_SIZE");
84    /// ```
85    #[must_use]
86    pub fn key(&self) -> &str {
87        match self {
88            Self::Parse { key, .. } | Self::OutOfRange { key, .. } => key,
89        }
90    }
91}
92
93impl fmt::Display for SettingsError {
94    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
95        match self {
96            Self::Parse { key, value_kind } => {
97                write!(f, "{key} could not be parsed as {value_kind}")
98            }
99            Self::OutOfRange { key, message } => {
100                write!(f, "{key} is out of range: {message}")
101            }
102        }
103    }
104}
105
106impl std::error::Error for SettingsError {}