use std::fmt::{self, Display};
use std::borrow::Cow;
use serde::{ser, de};
use crate::{Figment, Profile, Metadata, value::Tag};
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Clone, Debug, PartialEq)]
pub struct Error {
tag: Tag,
pub profile: Option<Profile>,
pub metadata: Option<Metadata>,
pub path: Vec<String>,
pub kind: Kind,
prev: Option<Box<Error>>,
}
#[derive(Clone, Debug, PartialEq)]
pub enum Kind {
Message(String),
InvalidType(Actual, String),
InvalidValue(Actual, String),
InvalidLength(usize, String),
UnknownVariant(String, &'static [&'static str]),
UnknownField(String, &'static [&'static str]),
MissingField(Cow<'static, str>),
DuplicateField(&'static str),
ISizeOutOfRange(isize),
USizeOutOfRange(usize),
Unsupported(Actual),
UnsupportedKey(Actual, Cow<'static, str>),
}
impl Error {
pub(crate) fn prefixed(mut self, path: &str) -> Self {
self.path.insert(0, path.into());
self
}
pub(crate) fn chain(self, mut error: Error) -> Self {
error.prev = Some(Box::new(self));
error
}
pub(crate) fn retagged(mut self, tag: Tag) -> Self {
if self.tag.is_default() {
self.tag = tag;
}
self
}
pub(crate) fn resolved(mut self, config: &Figment) -> Self {
let mut error = Some(&mut self);
while let Some(e) = error {
e.metadata = config.get_metadata(e.tag).cloned();
e.profile = e.tag.profile()
.or_else(|| Some(config.profile().clone()));
error = e.prev.as_mut().map(|v| &mut **v);
}
self
}
}
impl Error {
pub fn missing(&self) -> bool {
match self.kind {
Kind::MissingField(..) => true,
_ => false
}
}
pub fn with_path(mut self, path: &str) -> Self {
self.path.push(path.into());
self
}
}
pub struct IntoIter(Option<Error>);
impl Iterator for IntoIter {
type Item = Error;
fn next(&mut self) -> Option<Self::Item> {
if let Some(mut error) = self.0.take() {
self.0 = error.prev.take().map(|e| *e);
Some(error)
} else {
None
}
}
}
impl IntoIterator for Error {
type Item = Error;
type IntoIter = IntoIter;
fn into_iter(self) -> Self::IntoIter {
IntoIter(Some(self))
}
}
#[allow(missing_docs)]
#[derive(Clone, Debug, PartialEq)]
pub enum Actual {
Bool(bool),
Unsigned(u64),
Signed(i64),
Float(f64),
Char(char),
Str(String),
Bytes(Vec<u8>),
Unit,
Option,
NewtypeStruct,
Seq,
Map,
Enum,
UnitVariant,
NewtypeVariant,
TupleVariant,
StructVariant,
Other(String),
}
impl fmt::Display for Actual {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Actual::Bool(v) => write!(f, "bool {}", v),
Actual::Unsigned(v) => write!(f, "unsigned int `{}`", v),
Actual::Signed(v) => write!(f, "signed int `{}`", v),
Actual::Float(v) => write!(f, "float `{}`", v),
Actual::Char(v) => write!(f, "char {:?}", v),
Actual::Str(v) => write!(f, "string {:?}", v),
Actual::Bytes(v) => write!(f, "bytes {:?}", v),
Actual::Unit => write!(f, "unit"),
Actual::Option => write!(f, "option"),
Actual::NewtypeStruct => write!(f, "new-type struct"),
Actual::Seq => write!(f, "sequence"),
Actual::Map => write!(f, "map"),
Actual::Enum => write!(f, "enum"),
Actual::UnitVariant => write!(f, "unit variant"),
Actual::NewtypeVariant => write!(f, "new-type variant"),
Actual::TupleVariant => write!(f, "tuple variant"),
Actual::StructVariant => write!(f, "struct variant"),
Actual::Other(v) => v.fmt(f),
}
}
}
impl From<de::Unexpected<'_>> for Actual {
fn from(value: de::Unexpected<'_>) -> Actual {
match value {
de::Unexpected::Bool(v) => Actual::Bool(v),
de::Unexpected::Unsigned(v) => Actual::Unsigned(v),
de::Unexpected::Signed(v) => Actual::Signed(v),
de::Unexpected::Float(v) => Actual::Float(v),
de::Unexpected::Char(v) => Actual::Char(v),
de::Unexpected::Str(v) => Actual::Str(v.into()),
de::Unexpected::Bytes(v) => Actual::Bytes(v.into()),
de::Unexpected::Unit => Actual::Unit,
de::Unexpected::Option => Actual::Option,
de::Unexpected::NewtypeStruct => Actual::NewtypeStruct,
de::Unexpected::Seq => Actual::Seq,
de::Unexpected::Map => Actual::Map,
de::Unexpected::Enum => Actual::Enum,
de::Unexpected::UnitVariant => Actual::UnitVariant,
de::Unexpected::NewtypeVariant => Actual::NewtypeVariant,
de::Unexpected::TupleVariant => Actual::TupleVariant,
de::Unexpected::StructVariant => Actual::StructVariant,
de::Unexpected::Other(v) => Actual::Other(v.into())
}
}
}
impl de::Error for Error {
fn custom<T: Display>(msg: T) -> Self {
Kind::Message(msg.to_string()).into()
}
fn invalid_type(unexp: de::Unexpected, exp: &dyn de::Expected) -> Self {
Kind::InvalidType(unexp.into(), exp.to_string()).into()
}
fn invalid_value(unexp: de::Unexpected, exp: &dyn de::Expected) -> Self {
Kind::InvalidValue(unexp.into(), exp.to_string()).into()
}
fn invalid_length(len: usize, exp: &dyn de::Expected) -> Self {
Kind::InvalidLength(len, exp.to_string()).into()
}
fn unknown_variant(variant: &str, expected: &'static [&'static str]) -> Self {
Kind::UnknownVariant(variant.into(), expected).into()
}
fn unknown_field(field: &str, expected: &'static [&'static str]) -> Self {
Kind::UnknownField(field.into(), expected).into()
}
fn missing_field(field: &'static str) -> Self {
Kind::MissingField(field.into()).into()
}
fn duplicate_field(field: &'static str) -> Self {
Kind::DuplicateField(field).into()
}
}
impl ser::Error for Error {
fn custom<T: Display>(msg: T) -> Self {
Kind::Message(msg.to_string()).into()
}
}
impl From<Kind> for Error {
fn from(kind: Kind) -> Error {
Error {
tag: Tag::Default,
path: vec![],
profile: None,
metadata: None,
prev: None,
kind,
}
}
}
impl From<String> for Error {
fn from(string: String) -> Error {
Kind::Message(string).into()
}
}
impl Display for Kind {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Kind::Message(msg) => f.write_str(&msg),
Kind::InvalidType(v, exp) => {
write!(f, "invalid type: found {}, expected {}", v, exp)
}
Kind::InvalidValue(v, exp) => {
write!(f, "invalid value {}, expected {}", v, exp)
},
Kind::InvalidLength(v, exp) => {
write!(f, "invalid length {}, expected {}", v, exp)
},
Kind::UnknownVariant(v, exp) => {
write!(f, "unknown variant: found `{}`, expected `{}`", v, OneOf(exp))
}
Kind::UnknownField(v, exp) => {
write!(f, "unknown field: found `{}`, expected `{}`", v, OneOf(exp))
}
Kind::MissingField(v) => {
write!(f, "missing field `{}`", v)
}
Kind::DuplicateField(v) => {
write!(f, "duplicate field `{}`", v)
}
Kind::ISizeOutOfRange(v) => {
write!(f, "signed integer `{}` is out of range", v)
}
Kind::USizeOutOfRange(v) => {
write!(f, "unsigned integer `{}` is out of range", v)
}
Kind::Unsupported(v) => {
write!(f, "unsupported type `{}`", v)
}
Kind::UnsupportedKey(a, e) => {
write!(f, "unsupported type `{}` for key: must be `{}`", a, e)
}
}
}
}
impl Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.kind.fmt(f)?;
if let (Some(ref profile), Some(ref md)) = (&self.profile, &self.metadata) {
write!(f, ":")?;
if !self.path.is_empty() {
write!(f, " `{}`", md.interpolate(profile, &self.path))?;
}
}
if let Some(source) = self.metadata.as_ref().and_then(|m| m.source.as_ref()) {
write!(f, " in {}", source)?;
}
if let Some(prev) = &self.prev {
write!(f, "\n{}", prev)?;
}
Ok(())
}
}
impl std::error::Error for Error {}
pub struct OneOf(pub &'static [&'static str]);
impl fmt::Display for OneOf {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.0.len() {
0 => write!(f, "none"),
1 => write!(f, "`{}`", self.0[0]),
2 => write!(f, "`{}` or `{}`", self.0[0], self.0[1]),
_ => {
write!(f, "one of ")?;
for (i, alt) in self.0.iter().enumerate() {
if i > 0 { write!(f, ", ")?; }
write!(f, "`{}`", alt)?;
}
Ok(())
}
}
}
}
impl de::Expected for OneOf {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
Display::fmt(self, f)
}
}