| use std::error::Error; |
| use std::fmt; |
| use std::result; |
| |
| use serde::de; |
| use serde::ser; |
| |
| #[derive(Debug)] |
| pub enum Unexpected { |
| Bool(bool), |
| I64(i64), |
| I128(i128), |
| U64(u64), |
| U128(u128), |
| Float(f64), |
| Str(String), |
| Unit, |
| Seq, |
| Map, |
| } |
| |
| impl fmt::Display for Unexpected { |
| fn fmt(&self, f: &mut fmt::Formatter) -> result::Result<(), fmt::Error> { |
| match *self { |
| Unexpected::Bool(b) => write!(f, "boolean `{}`", b), |
| Unexpected::I64(i) => write!(f, "integer 64 bit `{}`", i), |
| Unexpected::I128(i) => write!(f, "integer 128 bit `{}`", i), |
| Unexpected::U64(i) => write!(f, "unsigned integer 64 bit `{}`", i), |
| Unexpected::U128(i) => write!(f, "unsigned integer 128 bit `{}`", i), |
| Unexpected::Float(v) => write!(f, "floating point `{}`", v), |
| Unexpected::Str(ref s) => write!(f, "string {:?}", s), |
| Unexpected::Unit => write!(f, "unit value"), |
| Unexpected::Seq => write!(f, "sequence"), |
| Unexpected::Map => write!(f, "map"), |
| } |
| } |
| } |
| |
| /// Represents all possible errors that can occur when working with |
| /// configuration. |
| pub enum ConfigError { |
| /// Configuration is frozen and no further mutations can be made. |
| Frozen, |
| |
| /// Configuration property was not found |
| NotFound(String), |
| |
| /// Configuration path could not be parsed. |
| PathParse(nom::error::ErrorKind), |
| |
| /// Configuration could not be parsed from file. |
| FileParse { |
| /// The URI used to access the file (if not loaded from a string). |
| /// Example: `/path/to/config.json` |
| uri: Option<String>, |
| |
| /// The captured error from attempting to parse the file in its desired format. |
| /// This is the actual error object from the library used for the parsing. |
| cause: Box<dyn Error + Send + Sync>, |
| }, |
| |
| /// Value could not be converted into the requested type. |
| Type { |
| /// The URI that references the source that the value came from. |
| /// Example: `/path/to/config.json` or `Environment` or `etcd://localhost` |
| // TODO: Why is this called Origin but FileParse has a uri field? |
| origin: Option<String>, |
| |
| /// What we found when parsing the value |
| unexpected: Unexpected, |
| |
| /// What was expected when parsing the value |
| expected: &'static str, |
| |
| /// The key in the configuration hash of this value (if available where the |
| /// error is generated). |
| key: Option<String>, |
| }, |
| |
| /// Custom message |
| Message(String), |
| |
| /// Unadorned error from a foreign origin. |
| Foreign(Box<dyn Error + Send + Sync>), |
| } |
| |
| impl ConfigError { |
| // FIXME: pub(crate) |
| #[doc(hidden)] |
| pub fn invalid_type( |
| origin: Option<String>, |
| unexpected: Unexpected, |
| expected: &'static str, |
| ) -> Self { |
| Self::Type { |
| origin, |
| unexpected, |
| expected, |
| key: None, |
| } |
| } |
| |
| // Have a proper error fire if the root of a file is ever not a Table |
| // TODO: for now only json5 checked, need to finish others |
| #[doc(hidden)] |
| pub fn invalid_root(origin: Option<&String>, unexpected: Unexpected) -> Box<Self> { |
| Box::new(Self::Type { |
| origin: origin.cloned(), |
| unexpected, |
| expected: "a map", |
| key: None, |
| }) |
| } |
| |
| // FIXME: pub(crate) |
| #[doc(hidden)] |
| #[must_use] |
| pub fn extend_with_key(self, key: &str) -> Self { |
| match self { |
| Self::Type { |
| origin, |
| unexpected, |
| expected, |
| .. |
| } => Self::Type { |
| origin, |
| unexpected, |
| expected, |
| key: Some(key.into()), |
| }, |
| |
| _ => self, |
| } |
| } |
| |
| #[must_use] |
| fn prepend(self, segment: &str, add_dot: bool) -> Self { |
| let concat = |key: Option<String>| { |
| let key = key.unwrap_or_default(); |
| let dot = if add_dot && key.as_bytes().get(0).unwrap_or(&b'[') != &b'[' { |
| "." |
| } else { |
| "" |
| }; |
| format!("{}{}{}", segment, dot, key) |
| }; |
| match self { |
| Self::Type { |
| origin, |
| unexpected, |
| expected, |
| key, |
| } => Self::Type { |
| origin, |
| unexpected, |
| expected, |
| key: Some(concat(key)), |
| }, |
| Self::NotFound(key) => Self::NotFound(concat(Some(key))), |
| _ => self, |
| } |
| } |
| |
| #[must_use] |
| pub(crate) fn prepend_key(self, key: &str) -> Self { |
| self.prepend(key, true) |
| } |
| |
| #[must_use] |
| pub(crate) fn prepend_index(self, idx: usize) -> Self { |
| self.prepend(&format!("[{}]", idx), false) |
| } |
| } |
| |
| /// Alias for a `Result` with the error type set to `ConfigError`. |
| pub type Result<T> = result::Result<T, ConfigError>; |
| |
| // Forward Debug to Display for readable panic! messages |
| impl fmt::Debug for ConfigError { |
| fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| write!(f, "{}", *self) |
| } |
| } |
| |
| impl fmt::Display for ConfigError { |
| fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| match *self { |
| ConfigError::Frozen => write!(f, "configuration is frozen"), |
| |
| ConfigError::PathParse(ref kind) => write!(f, "{}", kind.description()), |
| |
| ConfigError::Message(ref s) => write!(f, "{}", s), |
| |
| ConfigError::Foreign(ref cause) => write!(f, "{}", cause), |
| |
| ConfigError::NotFound(ref key) => { |
| write!(f, "configuration property {:?} not found", key) |
| } |
| |
| ConfigError::Type { |
| ref origin, |
| ref unexpected, |
| expected, |
| ref key, |
| } => { |
| write!(f, "invalid type: {}, expected {}", unexpected, expected)?; |
| |
| if let Some(ref key) = *key { |
| write!(f, " for key `{}`", key)?; |
| } |
| |
| if let Some(ref origin) = *origin { |
| write!(f, " in {}", origin)?; |
| } |
| |
| Ok(()) |
| } |
| |
| ConfigError::FileParse { ref cause, ref uri } => { |
| write!(f, "{}", cause)?; |
| |
| if let Some(ref uri) = *uri { |
| write!(f, " in {}", uri)?; |
| } |
| |
| Ok(()) |
| } |
| } |
| } |
| } |
| |
| impl Error for ConfigError {} |
| |
| impl de::Error for ConfigError { |
| fn custom<T: fmt::Display>(msg: T) -> Self { |
| Self::Message(msg.to_string()) |
| } |
| } |
| |
| impl ser::Error for ConfigError { |
| fn custom<T: fmt::Display>(msg: T) -> Self { |
| Self::Message(msg.to_string()) |
| } |
| } |