use std::borrow::Cow;
use std::error::Error;
use std::fmt::{Debug, Display};
use std::panic::Location;
use crate::ext::ErrorExt;
pub struct Many {
pub message: Cow<'static, str>,
pub causes: Vec<Box<dyn Error + Send + Sync + 'static>>,
pub location: Option<&'static Location<'static>>,
}
impl Many {
#[track_caller]
pub fn new(msg: impl Into<Cow<'static, str>>) -> Self {
Self {
message: msg.into(),
causes: Vec::new(),
location: Some(Location::caller()),
}
}
pub fn with_cause<E>(mut self, cause: E) -> Self
where
E: Into<Box<dyn Error + Send + Sync + 'static>>,
{
self.causes.push(cause.into());
self
}
pub fn causes(&self) -> &[Box<dyn Error + Send + Sync + 'static>] {
&self.causes
}
#[track_caller]
pub fn from_results<T, E>(
msg: impl Into<Cow<'static, str>>,
results: impl IntoIterator<Item = Result<T, E>>,
) -> Result<Vec<T>, Self>
where
E: Into<Box<dyn Error + Send + Sync + 'static>>,
{
let mut oks = Vec::new();
let mut errs = Vec::new();
for result in results {
match result {
Ok(v) => oks.push(v),
Err(e) => errs.push(e.into()),
}
}
if errs.is_empty() {
Ok(oks)
} else {
Err(Self {
message: msg.into(),
causes: errs,
location: Some(Location::caller()),
})
}
}
#[track_caller]
pub fn from_errors<E>(
msg: impl Into<Cow<'static, str>>,
errors: impl IntoIterator<Item = E>,
) -> Self
where
E: Into<Box<dyn Error + Send + Sync + 'static>>,
{
Self {
message: msg.into(),
causes: errors.into_iter().map(Into::into).collect(),
location: Some(Location::caller()),
}
}
pub fn is_empty(&self) -> bool {
self.causes.is_empty()
}
pub fn len(&self) -> usize {
self.causes.len()
}
}
impl Debug for Many {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.report().fmt(f)
}
}
impl Display for Many {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if let Some(loc) = self.location {
write!(f, "{}, at {}", self.message, loc)?;
} else {
write!(f, "{}", self.message)?;
}
if !self.causes.is_empty() {
write!(
f,
" ({} {})",
self.causes.len(),
if self.causes.len() == 1 {
"cause"
} else {
"causes"
}
)?;
}
Ok(())
}
}
impl Error for Many {
fn source(&self) -> Option<&(dyn Error + 'static)> {
self.causes
.first()
.map(|e| e.as_ref() as &(dyn Error + 'static))
}
}
impl<E> From<Vec<E>> for Many
where
E: Into<Box<dyn Error + Send + Sync + 'static>>,
{
#[track_caller]
fn from(errors: Vec<E>) -> Self {
Self {
message: Cow::Borrowed("Multiple errors occurred"),
causes: errors.into_iter().map(Into::into).collect(),
location: Some(Location::caller()),
}
}
}
impl Many {
pub fn with_message(mut self, msg: impl Into<Cow<'static, str>>) -> Self {
self.message = msg.into();
self
}
}