1 use std::error::Error;
2 use std::fmt;
3 use std::result;
4 
5 use serde::de;
6 use serde::ser;
7 
8 #[derive(Debug)]
9 pub 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 
22 impl fmt::Display for Unexpected {
fmt(&self, f: &mut fmt::Formatter) -> result::Result<(), fmt::Error>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, "integer 64 bit `{}`", i),
27             Unexpected::I128(i) => write!(f, "integer 128 bit `{}`", i),
28             Unexpected::U64(i) => write!(f, "unsigned integer 64 bit `{}`", i),
29             Unexpected::U128(i) => write!(f, "unsigned integer 128 bit `{}`", 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 /// Represents all possible errors that can occur when working with
40 /// configuration.
41 pub enum ConfigError {
42     /// Configuration is frozen and no further mutations can be made.
43     Frozen,
44 
45     /// Configuration property was not found
46     NotFound(String),
47 
48     /// Configuration path could not be parsed.
49     PathParse(nom::error::ErrorKind),
50 
51     /// Configuration could not be parsed from file.
52     FileParse {
53         /// The URI used to access the file (if not loaded from a string).
54         /// Example: `/path/to/config.json`
55         uri: Option<String>,
56 
57         /// The captured error from attempting to parse the file in its desired format.
58         /// This is the actual error object from the library used for the parsing.
59         cause: Box<dyn Error + Send + Sync>,
60     },
61 
62     /// Value could not be converted into the requested type.
63     Type {
64         /// The URI that references the source that the value came from.
65         /// Example: `/path/to/config.json` or `Environment` or `etcd://localhost`
66         // TODO: Why is this called Origin but FileParse has a uri field?
67         origin: Option<String>,
68 
69         /// What we found when parsing the value
70         unexpected: Unexpected,
71 
72         /// What was expected when parsing the value
73         expected: &'static str,
74 
75         /// The key in the configuration hash of this value (if available where the
76         /// error is generated).
77         key: Option<String>,
78     },
79 
80     /// Custom message
81     Message(String),
82 
83     /// Unadorned error from a foreign origin.
84     Foreign(Box<dyn Error + Send + Sync>),
85 }
86 
87 impl ConfigError {
88     // FIXME: pub(crate)
89     #[doc(hidden)]
invalid_type( origin: Option<String>, unexpected: Unexpected, expected: &'static str, ) -> Self90     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     // Have a proper error fire if the root of a file is ever not a Table
104     // TODO: for now only json5 checked, need to finish others
105     #[doc(hidden)]
invalid_root(origin: Option<&String>, unexpected: Unexpected) -> Box<Self>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     // FIXME: pub(crate)
116     #[doc(hidden)]
117     #[must_use]
extend_with_key(self, key: &str) -> Self118     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]
prepend(self, segment: &str, add_dot: bool) -> Self137     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().get(0).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]
prepend_key(self, key: &str) -> Self165     pub(crate) fn prepend_key(self, key: &str) -> Self {
166         self.prepend(key, true)
167     }
168 
169     #[must_use]
prepend_index(self, idx: usize) -> Self170     pub(crate) fn prepend_index(self, idx: usize) -> Self {
171         self.prepend(&format!("[{}]", idx), false)
172     }
173 }
174 
175 /// Alias for a `Result` with the error type set to `ConfigError`.
176 pub type Result<T> = result::Result<T, ConfigError>;
177 
178 // Forward Debug to Display for readable panic! messages
179 impl fmt::Debug for ConfigError {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result180     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
181         write!(f, "{}", *self)
182     }
183 }
184 
185 impl fmt::Display for ConfigError {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result186     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 
232 impl Error for ConfigError {}
233 
234 impl de::Error for ConfigError {
custom<T: fmt::Display>(msg: T) -> Self235     fn custom<T: fmt::Display>(msg: T) -> Self {
236         Self::Message(msg.to_string())
237     }
238 }
239 
240 impl ser::Error for ConfigError {
custom<T: fmt::Display>(msg: T) -> Self241     fn custom<T: fmt::Display>(msg: T) -> Self {
242         Self::Message(msg.to_string())
243     }
244 }
245