use crate::{
error_category::{self, ErrorCodeFormatter},
error_data::ErrorDataChainIter,
marker, DynError, ErrorCategory, ErrorCategoryHandle, ErrorCode, ErrorData, ERROR_CHAIN_LEN,
};
use core::marker::PhantomData;
use core::{
fmt::{self, Debug, Formatter},
iter::FusedIterator,
};
#[repr(transparent)]
pub struct Error<C>(ErrorData, PhantomData<C>);
impl<C> Error<C> {
#[inline(always)]
pub const fn new_raw(error_code: ErrorCode) -> Error<C> {
Error(ErrorData::new(error_code), PhantomData)
}
pub const fn from_raw(error_data: ErrorData) -> Error<C> {
Error(error_data, PhantomData)
}
}
impl<C> Error<C> {
pub const fn chain_capacity(&self) -> usize {
ERROR_CHAIN_LEN
}
}
impl<C: ErrorCategory> Error<C> {
#[inline(always)]
pub fn new(error_code: C) -> Error<C> {
Error(ErrorData::new(error_code.into()), PhantomData)
}
#[inline]
pub fn code(&self) -> C {
self.0.code().into()
}
pub fn chain_len(&self) -> usize {
self.0.chain_len()
}
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
}
})
}
pub fn iter(&self) -> ErrorIter {
ErrorIter {
formatter_func: Some(error_category::format_chained::<C>),
curr_error_code: self.0.code(),
next_formatter_index: self.0.first_formatter_index(),
chain_iter: self.0.iter_chain(),
}
}
}
pub struct ErrorIter {
pub(crate) formatter_func: Option<ErrorCodeFormatter>,
pub(crate) curr_error_code: ErrorCode,
pub(crate) next_formatter_index: Option<u8>,
pub(crate) chain_iter: ErrorDataChainIter,
}
impl Iterator for ErrorIter {
type Item = (ErrorCode, ErrorCategoryHandle);
fn next(&mut self) -> Option<Self::Item> {
if let Some(formatter_func) = self.formatter_func {
let (err_cat_handle, next_formatter_res) =
formatter_func(0, self.next_formatter_index.take(), None);
let error_code = self.curr_error_code;
if let (Some((next_error_code, next_next_formatter_index)), Ok(Some(next_formatter))) =
(self.chain_iter.next(), next_formatter_res)
{
self.curr_error_code = next_error_code;
self.next_formatter_index = next_next_formatter_index;
self.formatter_func = Some(next_formatter.into());
} else {
self.formatter_func = None;
}
Some((error_code, err_cat_handle))
} else {
None
}
}
}
impl FusedIterator for ErrorIter {}
impl<C: ErrorCategory> Debug for Error<C> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
DynError::from(*self).fmt(f)
}
}
pub trait ChainError<O: ErrorCategory, Tag> {
fn chain(self, error_code: O) -> Error<O>;
}
pub trait ResultChainError<T, O: ErrorCategory, Tag> {
fn chain_err(self, error_code: O) -> Result<T, Error<O>>;
}
macro_rules! impl_chain_error {
($([$t:ident, $idx:literal]),*) => {
$(
impl<C: ErrorCategory> ChainError<C, (marker::$t, marker::Error_t)> for Error<C::$t> {
#[inline(always)]
fn chain(self, error_code: C) -> Error<C> {
let mut data: ErrorData = self.0;
ErrorData::chain(&mut data, error_code.into(), $idx);
Error(data, PhantomData)
}
}
impl<C: ErrorCategory> ChainError<C, (marker::$t, marker::Concrete_t)> for C::$t {
#[inline(always)]
fn chain(self, error_code: C) -> Error<C> {
Error::new(self).chain(error_code)
}
}
)+
};
}
impl_chain_error!([L0, 0], [L1, 1], [L2, 2], [L3, 3], [L4, 4], [L5, 5]);
impl<OK, ERR, O, TAG> ResultChainError<OK, O, TAG> for Result<OK, ERR>
where
O: ErrorCategory,
ERR: ChainError<O, TAG>,
{
#[inline]
fn chain_err(self, error_code: O) -> Result<OK, Error<O>> {
match self {
Err(err) => Err(err.chain(error_code)),
Ok(val) => Ok(val),
}
}
}
impl<C: ErrorCategory> PartialEq for Error<C> {
fn eq(&self, other: &Error<C>) -> bool {
self.0 == other.0
}
}
impl<C: ErrorCategory> Eq for Error<C> {}
impl<C: ErrorCategory> Clone for Error<C> {
#[inline(always)]
fn clone(&self) -> Self {
Error(self.0, PhantomData)
}
#[inline(always)]
fn clone_from(&mut self, source: &Self) {
self.0 = source.0;
}
}
impl<C: ErrorCategory> Copy for Error<C> {}
impl<C: ErrorCategory> From<C> for Error<C> {
#[inline(always)]
fn from(error_code: C) -> Self {
Error::new(error_code)
}
}
impl<C: ErrorCategory> From<Error<C>> for ErrorData {
#[inline(always)]
fn from(error: Error<C>) -> Self {
error.0
}
}