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 {}