1use std::error::Error;
2use std::fmt;
3use std::result;
4
5use serde::de;
6use serde::ser;
7
8#[derive(Debug)]
9pub enum Unexpected {
10 Bool(bool),
11 I64(i64),
12 I128(i128),
13 U64(u64),
14 U128(u128),
15 Float(f64),
16 Str(String),
17 Unit,
18 Seq,
19 Map,
20}
21
22impl fmt::Display for Unexpected {
23 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> result::Result<(), fmt::Error> {
24 match *self {
25 Unexpected::Bool(b) => write!(f, "boolean `{b}`"),
26 Unexpected::I64(i) => write!(f, "64-bit integer `{i}`"),
27 Unexpected::I128(i) => write!(f, "128-bit integer `{i}`"),
28 Unexpected::U64(i) => write!(f, "64-bit unsigned integer `{i}`"),
29 Unexpected::U128(i) => write!(f, "128-bit unsigned integer `{i}`"),
30 Unexpected::Float(v) => write!(f, "floating point `{v}`"),
31 Unexpected::Str(ref s) => write!(f, "string {s:?}"),
32 Unexpected::Unit => write!(f, "unit value"),
33 Unexpected::Seq => write!(f, "sequence"),
34 Unexpected::Map => write!(f, "map"),
35 }
36 }
37}
38
39#[non_exhaustive]
42pub enum ConfigError {
43 Frozen,
45
46 NotFound(String),
48
49 PathParse { cause: Box<dyn Error + Send + Sync> },
51
52 FileParse {
54 uri: Option<String>,
57
58 cause: Box<dyn Error + Send + Sync>,
61 },
62
63 Type {
65 origin: Option<String>,
69
70 unexpected: Unexpected,
72
73 expected: &'static str,
75
76 key: Option<String>,
79 },
80
81 At {
83 error: Box<ConfigError>,
85
86 origin: Option<String>,
90
91 key: Option<String>,
94 },
95
96 Message(String),
98
99 Foreign(Box<dyn Error + Send + Sync>),
101}
102
103impl ConfigError {
104 #[doc(hidden)]
106 pub fn invalid_type(
107 origin: Option<String>,
108 unexpected: Unexpected,
109 expected: &'static str,
110 ) -> Self {
111 Self::Type {
112 origin,
113 unexpected,
114 expected,
115 key: None,
116 }
117 }
118
119 #[doc(hidden)]
122 pub fn invalid_root(origin: Option<&String>, unexpected: Unexpected) -> Box<Self> {
123 Box::new(Self::Type {
124 origin: origin.cloned(),
125 unexpected,
126 expected: "a map",
127 key: None,
128 })
129 }
130
131 #[doc(hidden)]
133 #[must_use]
134 pub fn extend_with_key(self, key: &str) -> Self {
135 match self {
136 Self::Type {
137 origin,
138 unexpected,
139 expected,
140 ..
141 } => Self::Type {
142 origin,
143 unexpected,
144 expected,
145 key: Some(key.into()),
146 },
147
148 Self::At { origin, error, .. } => Self::At {
149 error,
150 origin,
151 key: Some(key.into()),
152 },
153
154 other => Self::At {
155 error: Box::new(other),
156 origin: None,
157 key: Some(key.into()),
158 },
159 }
160 }
161
162 #[must_use]
163 fn prepend(self, segment: &str, add_dot: bool) -> Self {
164 let concat = |key: Option<String>| {
165 let key = key.unwrap_or_default();
166 let dot = if add_dot && key.as_bytes().first().unwrap_or(&b'[') != &b'[' {
167 "."
168 } else {
169 ""
170 };
171 format!("{segment}{dot}{key}")
172 };
173 match self {
174 Self::Type {
175 origin,
176 unexpected,
177 expected,
178 key,
179 } => Self::Type {
180 origin,
181 unexpected,
182 expected,
183 key: Some(concat(key)),
184 },
185 Self::At { error, origin, key } => Self::At {
186 error,
187 origin,
188 key: Some(concat(key)),
189 },
190 Self::NotFound(key) => Self::NotFound(concat(Some(key))),
191 other => Self::At {
192 error: Box::new(other),
193 origin: None,
194 key: Some(concat(None)),
195 },
196 }
197 }
198
199 #[must_use]
200 pub(crate) fn prepend_key(self, key: &str) -> Self {
201 self.prepend(key, true)
202 }
203
204 #[must_use]
205 pub(crate) fn prepend_index(self, idx: usize) -> Self {
206 self.prepend(&format!("[{idx}]"), false)
207 }
208}
209
210pub(crate) type Result<T, E = ConfigError> = result::Result<T, E>;
212
213impl fmt::Debug for ConfigError {
215 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
216 write!(f, "{}", *self)
217 }
218}
219
220impl fmt::Display for ConfigError {
221 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
222 match *self {
223 ConfigError::Frozen => write!(f, "configuration is frozen"),
224
225 ConfigError::PathParse { ref cause } => write!(f, "{cause}"),
226
227 ConfigError::Message(ref s) => write!(f, "{s}"),
228
229 ConfigError::Foreign(ref cause) => write!(f, "{cause}"),
230
231 ConfigError::NotFound(ref key) => {
232 write!(f, "configuration property {key:?} not found")
233 }
234
235 ConfigError::Type {
236 ref origin,
237 ref unexpected,
238 expected,
239 ref key,
240 } => {
241 write!(f, "invalid type: {unexpected}, expected {expected}")?;
242
243 if let Some(ref key) = *key {
244 write!(f, " for key `{key}`")?;
245 }
246
247 if let Some(ref origin) = *origin {
248 write!(f, " in {origin}")?;
249 }
250
251 Ok(())
252 }
253
254 ConfigError::At {
255 ref error,
256 ref origin,
257 ref key,
258 } => {
259 write!(f, "{error}")?;
260
261 if let Some(ref key) = *key {
262 write!(f, " for key `{key}`")?;
263 }
264
265 if let Some(ref origin) = *origin {
266 write!(f, " in {origin}")?;
267 }
268
269 Ok(())
270 }
271
272 ConfigError::FileParse { ref cause, ref uri } => {
273 write!(f, "{cause}")?;
274
275 if let Some(ref uri) = *uri {
276 write!(f, " in {uri}")?;
277 }
278
279 Ok(())
280 }
281 }
282 }
283}
284
285impl Error for ConfigError {}
286
287impl de::Error for ConfigError {
288 fn custom<T: fmt::Display>(msg: T) -> Self {
289 Self::Message(msg.to_string())
290 }
291}
292
293impl ser::Error for ConfigError {
294 fn custom<T: fmt::Display>(msg: T) -> Self {
295 Self::Message(msg.to_string())
296 }
297}