use std::fmt;
use std::path::PathBuf;
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct Fault {
kind: FaultKind,
prompt: Option<String>,
path: Option<PathBuf>,
detail: String,
}
impl Fault {
pub(crate) fn new(
kind: FaultKind,
prompt: Option<String>,
path: Option<PathBuf>,
detail: impl Into<String>,
) -> Fault {
Fault {
kind,
prompt,
path,
detail: detail.into(),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum FaultKind {
Pattern,
Unreadable,
Unparsable,
InvalidName,
Duplicate,
StaleOverride,
Empty,
}
#[derive(Debug, Clone, Copy)]
pub struct FaultRef<'a> {
inner: &'a Fault,
}
impl<'a> FaultRef<'a> {
#[must_use]
pub fn prompt(&self) -> Option<&'a str> {
self.inner.prompt.as_deref()
}
#[must_use]
pub fn path(&self) -> Option<&'a std::path::Path> {
self.inner.path.as_deref()
}
#[must_use]
pub fn detail(&self) -> &'a str {
&self.inner.detail
}
#[must_use]
pub fn kind(&self) -> FaultKind {
self.inner.kind
}
}
impl fmt::Display for FaultRef<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(self.inner, f)
}
}
#[derive(Debug, Clone)]
pub struct Faults<'a> {
iter: std::slice::Iter<'a, Fault>,
}
impl<'a> Iterator for Faults<'a> {
type Item = FaultRef<'a>;
fn next(&mut self) -> Option<FaultRef<'a>> {
self.iter.next().map(|inner| FaultRef { inner })
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.iter.size_hint()
}
}
impl DoubleEndedIterator for Faults<'_> {
fn next_back(&mut self) -> Option<Self::Item> {
self.iter.next_back().map(|inner| FaultRef { inner })
}
}
impl ExactSizeIterator for Faults<'_> {}
impl std::iter::FusedIterator for Faults<'_> {}
impl fmt::Display for Fault {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match (&self.prompt, &self.path) {
(Some(prompt), Some(path)) => {
write!(f, "{prompt} ({}): {}", path.display(), self.detail)
}
(Some(prompt), None) => write!(f, "{prompt}: {}", self.detail),
(None, Some(path)) => write!(f, "{}: {}", path.display(), self.detail),
(None, None) => f.write_str(&self.detail),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub struct CatalogError {
faults: Vec<Fault>,
}
impl CatalogError {
pub(crate) fn new(faults: Vec<Fault>) -> CatalogError {
CatalogError { faults }
}
#[must_use]
pub fn faults(&self) -> Faults<'_> {
Faults {
iter: self.faults.iter(),
}
}
#[must_use]
pub fn kind(&self) -> CatalogErrorKind {
if self
.faults
.iter()
.any(|fault| fault.prompt.is_some() || fault.path.is_some())
{
CatalogErrorKind::Broken
} else {
CatalogErrorKind::Configuration
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum CatalogErrorKind {
Broken,
Configuration,
}
impl fmt::Display for CatalogError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let plural = if self.faults.len() == 1 { "" } else { "s" };
write!(f, "catalog has {} fault{plural}", self.faults.len())?;
for fault in &self.faults {
write!(f, "\n {fault}")?;
}
Ok(())
}
}
impl std::error::Error for CatalogError {}