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
39pub enum ConfigError {
42 Frozen,
44
45 NotFound(String),
47
48 PathParse(nom::error::ErrorKind),
50
51 FileParse {
53 uri: Option<String>,
56
57 cause: Box<dyn Error + Send + Sync>,
60 },
61
62 Type {
64 origin: Option<String>,
68
69 unexpected: Unexpected,
71
72 expected: &'static str,
74
75 key: Option<String>,
78 },
79
80 Message(String),
82
83 Foreign(Box<dyn Error + Send + Sync>),
85}
86
87impl ConfigError {
88 #[doc(hidden)]
90 pub fn invalid_type(
91 origin: Option<String>,
92 unexpected: Unexpected,
93 expected: &'static str,
94 ) -> Self {
95 Self::Type {
96 origin,
97 unexpected,
98 expected,
99 key: None,
100 }
101 }
102
103 #[doc(hidden)]
106 pub fn invalid_root(origin: Option<&String>, unexpected: Unexpected) -> Box<Self> {
107 Box::new(Self::Type {
108 origin: origin.cloned(),
109 unexpected,
110 expected: "a map",
111 key: None,
112 })
113 }
114
115 #[doc(hidden)]
117 #[must_use]
118 pub fn extend_with_key(self, key: &str) -> Self {
119 match self {
120 Self::Type {
121 origin,
122 unexpected,
123 expected,
124 ..
125 } => Self::Type {
126 origin,
127 unexpected,
128 expected,
129 key: Some(key.into()),
130 },
131
132 _ => self,
133 }
134 }
135
136 #[must_use]
137 fn prepend(self, segment: &str, add_dot: bool) -> Self {
138 let concat = |key: Option<String>| {
139 let key = key.unwrap_or_default();
140 let dot = if add_dot && key.as_bytes().first().unwrap_or(&b'[') != &b'[' {
141 "."
142 } else {
143 ""
144 };
145 format!("{}{}{}", segment, dot, key)
146 };
147 match self {
148 Self::Type {
149 origin,
150 unexpected,
151 expected,
152 key,
153 } => Self::Type {
154 origin,
155 unexpected,
156 expected,
157 key: Some(concat(key)),
158 },
159 Self::NotFound(key) => Self::NotFound(concat(Some(key))),
160 _ => self,
161 }
162 }
163
164 #[must_use]
165 pub(crate) fn prepend_key(self, key: &str) -> Self {
166 self.prepend(key, true)
167 }
168
169 #[must_use]
170 pub(crate) fn prepend_index(self, idx: usize) -> Self {
171 self.prepend(&format!("[{}]", idx), false)
172 }
173}
174
175pub type Result<T> = result::Result<T, ConfigError>;
177
178impl fmt::Debug for ConfigError {
180 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
181 write!(f, "{}", *self)
182 }
183}
184
185impl fmt::Display for ConfigError {
186 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
187 match *self {
188 ConfigError::Frozen => write!(f, "configuration is frozen"),
189
190 ConfigError::PathParse(ref kind) => write!(f, "{}", kind.description()),
191
192 ConfigError::Message(ref s) => write!(f, "{}", s),
193
194 ConfigError::Foreign(ref cause) => write!(f, "{}", cause),
195
196 ConfigError::NotFound(ref key) => {
197 write!(f, "configuration property {:?} not found", key)
198 }
199
200 ConfigError::Type {
201 ref origin,
202 ref unexpected,
203 expected,
204 ref key,
205 } => {
206 write!(f, "invalid type: {}, expected {}", unexpected, expected)?;
207
208 if let Some(ref key) = *key {
209 write!(f, " for key `{}`", key)?;
210 }
211
212 if let Some(ref origin) = *origin {
213 write!(f, " in {}", origin)?;
214 }
215
216 Ok(())
217 }
218
219 ConfigError::FileParse { ref cause, ref uri } => {
220 write!(f, "{}", cause)?;
221
222 if let Some(ref uri) = *uri {
223 write!(f, " in {}", uri)?;
224 }
225
226 Ok(())
227 }
228 }
229 }
230}
231
232impl Error for ConfigError {}
233
234impl de::Error for ConfigError {
235 fn custom<T: fmt::Display>(msg: T) -> Self {
236 Self::Message(msg.to_string())
237 }
238}
239
240impl ser::Error for ConfigError {
241 fn custom<T: fmt::Display>(msg: T) -> Self {
242 Self::Message(msg.to_string())
243 }
244}