use nom;
use serde::de;
use serde::ser;
use std::borrow::Cow;
use std::error::Error;
use std::fmt;
use std::result;
#[derive(Debug)]
pub enum Unexpected {
Bool(bool),
Integer(i64),
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::Integer(i) => write!(f, "integer `{}`", 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"),
}
}
}
pub enum ConfigError {
Frozen,
NotFound(String),
PathParse(nom::error::ErrorKind),
FileParse {
uri: Option<String>,
cause: Box<Error + Send + Sync>,
},
Type {
origin: Option<String>,
unexpected: Unexpected,
expected: &'static str,
key: Option<String>,
},
Message(String),
Foreign(Box<Error + Send + Sync>),
}
impl ConfigError {
#[doc(hidden)]
pub fn invalid_type(
origin: Option<String>,
unexpected: Unexpected,
expected: &'static str,
) -> Self {
ConfigError::Type {
origin: origin,
unexpected: unexpected,
expected: expected,
key: None,
}
}
#[doc(hidden)]
pub fn extend_with_key(self, key: &str) -> Self {
match self {
ConfigError::Type {
origin,
unexpected,
expected,
..
} => ConfigError::Type {
origin: origin,
unexpected: unexpected,
expected: expected,
key: Some(key.into()),
},
_ => self,
}
}
fn prepend(self, segment: String, add_dot: bool) -> Self {
let concat = |key: Option<String>| {
let key = key.unwrap_or_else(String::new);
let dot = if add_dot && key.as_bytes().get(0).unwrap_or(&b'[') != &b'[' {
"."
} else {
""
};
format!("{}{}{}", segment, dot, key)
};
match self {
ConfigError::Type {
origin,
unexpected,
expected,
key,
} => {
ConfigError::Type {
origin,
unexpected,
expected,
key: Some(concat(key)),
}
}
ConfigError::NotFound(key) => ConfigError::NotFound(concat(Some(key))),
_ => self,
}
}
pub(crate) fn prepend_key(self, key: String) -> Self {
self.prepend(key, true)
}
pub(crate) fn prepend_index(self, idx: usize) -> Self {
self.prepend(format!("[{}]", idx), false)
}
}
pub type Result<T> = result::Result<T, ConfigError>;
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 | ConfigError::PathParse(_) => write!(f, "{}", self.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 {
fn description(&self) -> &str {
match *self {
ConfigError::Frozen => "configuration is frozen",
ConfigError::NotFound(_) => "configuration property not found",
ConfigError::Type { .. } => "invalid type",
ConfigError::Foreign(ref cause) | ConfigError::FileParse { ref cause, .. } => {
cause.description()
}
ConfigError::PathParse(ref kind) => kind.description(),
_ => "configuration error",
}
}
fn cause(&self) -> Option<&Error> {
match *self {
ConfigError::Foreign(ref cause) | ConfigError::FileParse { ref cause, .. } => {
Some(cause.as_ref())
}
_ => None,
}
}
}
impl de::Error for ConfigError {
fn custom<T: fmt::Display>(msg: T) -> Self {
ConfigError::Message(msg.to_string())
}
}
impl ser::Error for ConfigError {
fn custom<T: fmt::Display>(msg: T) -> Self {
ConfigError::Message(msg.to_string())
}
}