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