use core::{
any::Any,
fmt::{Debug, Display},
panic::Location,
slice::{Iter, IterMut},
};
use smallbox::{smallbox, SmallBox};
use thin_vec::{thin_vec, ThinVec};
use crate::{ProbablyNotRootCauseError, TimeoutError, UnitError};
pub trait StackableErrorTrait: Display + Any + Send + Sync + 'static {
#[doc(hidden)]
fn _as_any(&self) -> &(dyn Any + Send + Sync);
#[doc(hidden)]
fn _as_any_mut(&mut self) -> &mut (dyn Any + Send + Sync);
#[doc(hidden)]
fn _as_display(&self) -> &(dyn Display + Send + Sync);
}
impl<T: Display + Send + Sync + 'static> StackableErrorTrait for T {
fn _as_any(&self) -> &(dyn Any + Send + Sync) {
self
}
fn _as_any_mut(&mut self) -> &mut (dyn Any + Send + Sync) {
self
}
fn _as_display(&self) -> &(dyn Display + Send + Sync) {
self
}
}
pub trait StackedErrorDowncast: StackableErrorTrait + Sized {
fn get_err(&self) -> &(impl Display + Send + Sync + 'static);
fn get_location(&self) -> Option<&'static Location<'static>>;
fn downcast_ref<E>(&self) -> Option<&E>
where
E: Display + Send + Sync + 'static;
fn downcast_mut<E>(&mut self) -> Option<&mut E>
where
E: Display + Send + Sync + 'static;
}
pub struct ErrorItem {
b: SmallBox<dyn StackableErrorTrait, smallbox::space::S4>,
l: Option<&'static Location<'static>>,
}
#[cfg(target_pointer_width = "64")]
#[test]
fn error_kind_size() {
assert_eq!(core::mem::size_of::<ErrorItem>(), 56);
}
impl ErrorItem {
pub fn new<E: Display + Send + Sync + 'static>(
e: E,
l: Option<&'static Location<'static>>,
) -> Self {
Self { b: smallbox!(e), l }
}
}
impl Debug for ErrorItem {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_fmt(format_args!("{}", self.get_err()))?;
if let Some(location) = self.get_location() {
f.write_fmt(format_args!(" {location:?}"))?;
}
Ok(())
}
}
impl Display for ErrorItem {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
core::fmt::Debug::fmt(self, f)
}
}
impl StackedErrorDowncast for ErrorItem {
fn get_err(&self) -> &(impl Display + Send + Sync + 'static) {
&self.b
}
fn get_location(&self) -> Option<&'static Location<'static>> {
self.l
}
#[allow(clippy::needless_borrow)]
fn downcast_ref<E>(&self) -> Option<&E>
where
E: Display + Send + Sync + 'static,
{
(&*self.b)._as_any().downcast_ref()
}
#[allow(clippy::needless_borrow)]
fn downcast_mut<E>(&mut self) -> Option<&mut E>
where
E: Display + Send + Sync + 'static,
{
(&mut self.b)._as_any_mut().downcast_mut()
}
}
pub struct StackedError {
stack: ThinVec<ErrorItem>,
}
pub type Error = StackedError;
impl Error {
pub fn empty() -> Self {
Self {
stack: ThinVec::new(),
}
}
#[track_caller]
pub fn new() -> Self {
Self::from_err(UnitError {})
}
#[track_caller]
pub fn from_err<E: Display + Send + Sync + 'static>(e: E) -> Self {
Self {
stack: thin_vec![ErrorItem::new(e, Some(Location::caller()))],
}
}
pub fn from_err_locationless<E: Display + Send + Sync + 'static>(e: E) -> Self {
Self {
stack: thin_vec![ErrorItem::new(e, None)],
}
}
#[track_caller]
pub fn push(&mut self) {
self.push_err(UnitError {})
}
#[track_caller]
pub fn add(self) -> Self {
self.add_err(UnitError {})
}
#[track_caller]
pub fn push_err<E: Display + Send + Sync + 'static>(&mut self, e: E) {
self.stack.push(ErrorItem::new(e, Some(Location::caller())));
}
#[track_caller]
pub fn add_err<E: Display + Send + Sync + 'static>(mut self, e: E) -> Self {
self.push_err(e);
self
}
pub fn push_err_locationless<E: Display + Send + Sync + 'static>(&mut self, e: E) {
self.stack.push(ErrorItem::new(e, None));
}
pub fn add_err_locationless<E: Display + Send + Sync + 'static>(mut self, e: E) -> Self {
self.push_err_locationless(e);
self
}
pub fn chain_errors(mut self, mut other: Self) -> Self {
self.stack.append(&mut other.stack);
self
}
#[track_caller]
pub fn timeout() -> Self {
Self::from_err(TimeoutError {})
}
#[track_caller]
pub fn probably_not_root_cause() -> Self {
Self::from_err(ProbablyNotRootCauseError {})
}
pub fn is_timeout(&self) -> bool {
for e in &self.stack {
if e.downcast_ref::<TimeoutError>().is_some() {
return true
}
}
false
}
pub fn is_probably_not_root_cause(&self) -> bool {
for e in &self.stack {
if e.downcast_ref::<ProbablyNotRootCauseError>().is_some() {
return true
}
}
false
}
pub fn iter(&self) -> Iter<ErrorItem> {
self.stack.iter()
}
pub fn iter_mut(&mut self) -> IterMut<ErrorItem> {
self.stack.iter_mut()
}
}
impl<'a> IntoIterator for &'a Error {
type IntoIter = Iter<'a, ErrorItem>;
type Item = &'a ErrorItem;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl<'a> IntoIterator for &'a mut Error {
type IntoIter = IterMut<'a, ErrorItem>;
type Item = &'a mut ErrorItem;
fn into_iter(self) -> Self::IntoIter {
self.iter_mut()
}
}
impl Default for Error {
#[track_caller]
fn default() -> Self {
Error::new()
}
}
impl core::error::Error for Error {}