use crate::{
format_chained, ChainError, Error, ErrorCategory, ErrorCategoryHandle, ErrorCode,
ErrorCodeFormatter, ErrorData, ErrorIter, ERROR_CHAIN_LEN,
};
use core::{fmt, ptr};
#[derive(Clone)]
pub struct DynError {
error: ErrorData,
category_formatter: ErrorCodeFormatter,
}
impl PartialEq for DynError {
fn eq(&self, other: &DynError) -> bool {
self.error == other.error
&& ptr::eq(
self.category_formatter as *const (),
other.category_formatter as *const (),
)
}
}
impl Eq for DynError {}
impl DynError {
#[inline]
pub fn new<C: ErrorCategory>(error_code: C) -> DynError {
DynError {
error: ErrorData::new(error_code.into()),
category_formatter: format_chained::<C>,
}
}
#[inline]
pub fn from_raw_parts(
error_data: ErrorData,
category_formatter: ErrorCodeFormatter,
) -> DynError {
DynError {
error: error_data,
category_formatter,
}
}
pub fn into_raw_parts(self) -> (ErrorData, ErrorCodeFormatter) {
(self.error, self.category_formatter)
}
#[inline(always)]
pub fn code(&self) -> ErrorCode {
self.error.code()
}
#[inline(always)]
pub fn chain_len(&self) -> usize {
self.error.chain_len()
}
pub const fn chain_capacity(&self) -> usize {
ERROR_CHAIN_LEN
}
#[inline(always)]
pub fn category_handle(&self) -> ErrorCategoryHandle {
(self.category_formatter)(0, None, None).0
}
#[inline(always)]
pub fn formatter(&self) -> ErrorCodeFormatter {
self.category_formatter
}
pub fn is<C: ErrorCategory>(&self) -> bool {
self.category_handle().is_handle_of::<C>()
}
pub fn try_into<C: ErrorCategory>(self) -> Result<crate::Error<C>, Self> {
if self.is::<C>() {
Ok(crate::Error::from_raw(self.error))
} else {
Err(self)
}
}
pub fn caused_by<T: ErrorCategory>(&self, error_code: T) -> bool {
let error_code: ErrorCode = error_code.into();
let category_handle = ErrorCategoryHandle::new::<T>();
self.iter()
.any(|(ec, handle)| handle == category_handle && ec == error_code)
}
pub fn code_of_category<T: ErrorCategory>(&self) -> Option<T> {
let category_handle = ErrorCategoryHandle::new::<T>();
self.iter().find_map(|(ec, handle)| {
if handle == category_handle {
Some(ec.into())
} else {
None
}
})
}
#[inline]
pub fn iter(&self) -> ErrorIter {
ErrorIter {
formatter_func: Some(self.category_formatter),
curr_error_code: self.error.code(),
next_formatter_index: self.error.first_formatter_index(),
chain_iter: self.error.iter_chain(),
}
}
pub fn try_chain<C: ErrorCategory>(self, error_code: C) -> Result<Error<C>, Self> {
C::chainable_category_formatters()
.iter()
.enumerate()
.find_map(|(i, formatter)| {
if ptr::eq(
*formatter as *const (),
self.category_formatter as *const (),
) {
let mut data: ErrorData = self.error;
ErrorData::chain(&mut data, error_code.into(), i as u8);
Some(Error::from_raw(data))
} else {
None
}
})
.ok_or(self)
}
}
impl<O: ErrorCategory> ChainError<O, DynError> for DynError {
fn chain(self, error_code: O) -> Error<O> {
self.try_chain(error_code)
.expect("cannot chain unlinked error categories")
}
}
impl fmt::Debug for DynError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let (_, fmt_result) =
(self.category_formatter)(self.code(), self.error.first_formatter_index(), Some(f));
let mut formatter_func = fmt_result?;
for (ec, next_fmt_index) in self.error.iter_chain() {
formatter_func = if let Some(formatter_func) = formatter_func {
write!(f, "\n- ")?;
let (_, next_formatter) = formatter_func.into()(ec, next_fmt_index, Some(f));
next_formatter?
} else {
break;
};
}
Ok(())
}
}
impl<C: ErrorCategory> From<Error<C>> for DynError {
#[inline]
fn from(error: crate::Error<C>) -> Self {
DynError::from_raw_parts(error.into(), format_chained::<C>)
}
}
impl<C: ErrorCategory> From<C> for DynError {
#[inline]
fn from(error: C) -> Self {
DynError::from_raw_parts(ErrorData::new(error.into()), format_chained::<C>)
}
}