use std::{error::Error as StdError, fmt};
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Errors {
pub(crate) inner: Vec<Error>,
}
impl Errors {
pub fn into_inner(self) -> Vec<Error> {
self.inner
}
}
impl From<Vec<Error>> for Errors {
fn from(other: Vec<Error>) -> Self {
Self {
inner: other,
}
}
}
impl StdError for Errors {}
impl fmt::Display for Errors {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let uncloseds = self.inner.iter().filter_map(|e| {
if let Error::Unclosed(i) = e {
Some(i)
} else {
None
}
});
let missings = self.inner.iter().filter_map(|e| {
if let Error::Missing(k) = e {
Some(k)
} else {
None
}
});
let extras = self.inner.iter().filter_map(|e| {
if let Error::Extra(k) = e {
Some(k)
} else {
None
}
});
let uncloseds_count = uncloseds.clone().count();
let missings_count = missings.clone().count();
let extras_count = extras.clone().count();
format_error(
f,
"unclosed delimiter opened at byte",
uncloseds,
uncloseds_count,
false,
)?;
if uncloseds_count > 0 && missings_count > 0 {
write!(f, ", ")?;
}
format_error(f, "missing key", missings, missings_count, true)?;
if (uncloseds_count > 0 || missings_count > 0) && extras_count > 0 {
write!(f, ", ")?;
}
format_error(f, "extraneous key", extras, extras_count, true)?;
Ok(())
}
}
fn format_error(
f: &mut fmt::Formatter<'_>,
problem: &str,
iter: impl Iterator<Item = impl fmt::Display>,
count: usize,
quotes: bool,
) -> fmt::Result {
if count > 0 {
let s = if count != 1 {
"s"
} else {
""
};
write!(f, "{}{} ", problem, s)?;
for (i, error) in iter.enumerate() {
let sep = if i + 1 == count {
""
} else if i + 2 == count {
if count == 2 {
" and "
} else {
", and "
}
} else {
", "
};
if quotes {
write!(f, "\"{}\"{}", error, sep)?;
} else {
write!(f, "{}{}", error, sep)?;
}
}
}
Ok(())
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum Error {
Missing(String),
Extra(String),
Unclosed(usize),
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Missing(k) => write!(f, "missing key \"{}\"", k),
Self::Extra(k) => write!(f, "extraneous key \"{}\"", k),
Self::Unclosed(i) => {
write!(f, "unclosed delimitor opened at byte {}", i)
}
}
}
}