Skip to main content

reliar_core/
settings.rs

1//! The shared error every `*Settings::from_env` returns (SRS §7.2, §23.1, 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 (contract §7 I3).
7#[derive(Clone, Debug, PartialEq)]
8#[non_exhaustive]
9pub enum SettingsError {
10    /// A present variable could not be parsed as its declared type. The value is **never
11    /// echoed** — it may carry an operator's typo of something sensitive.
12    Parse {
13        /// The full environment variable name, including the prefix.
14        key: String,
15        /// The type or shape that was expected, e.g. `"u32"`, `"milliseconds"`.
16        value_kind: &'static str,
17    },
18    /// A present variable parsed but violated a documented bound.
19    OutOfRange {
20        /// The full environment variable name, including the prefix.
21        key: String,
22        /// The bound that was violated.
23        message: &'static str,
24    },
25}
26
27/// **Public constructors, because every provider's `from_env` returns this type** (contract §7
28/// I3). `SettingsError` is `#[non_exhaustive]`, so a crate other than the one defining a given
29/// `Settings` type — e.g. `reliar-store-postgres` — cannot build a variant with struct-literal
30/// syntax; without these a provider is forced into a parallel, unrelated error type, and a host
31/// wiring two `from_env` calls ends up handling two different errors for the same class of
32/// failure (ADR 0019).
33impl SettingsError {
34    /// The variable was present but did not parse. `value_kind` names the expected shape
35    /// (`"u32"`, `"milliseconds"`); the offending **value is never carried**.
36    #[must_use]
37    pub fn parse(key: impl Into<String>, value_kind: &'static str) -> Self {
38        Self::Parse {
39            key: key.into(),
40            value_kind,
41        }
42    }
43
44    /// The variable parsed but is outside the range the setting accepts.
45    #[must_use]
46    pub fn out_of_range(key: impl Into<String>, message: &'static str) -> Self {
47        Self::OutOfRange {
48            key: key.into(),
49            message,
50        }
51    }
52
53    /// The full environment-variable name, prefix included — what an operator has to go fix.
54    #[must_use]
55    pub fn key(&self) -> &str {
56        match self {
57            Self::Parse { key, .. } | Self::OutOfRange { key, .. } => key,
58        }
59    }
60}
61
62impl fmt::Display for SettingsError {
63    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
64        match self {
65            Self::Parse { key, value_kind } => {
66                write!(f, "{key} could not be parsed as {value_kind}")
67            }
68            Self::OutOfRange { key, message } => {
69                write!(f, "{key} is out of range: {message}")
70            }
71        }
72    }
73}
74
75impl std::error::Error for SettingsError {}