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