use crate::ErrorCode;
use core::{
fmt::{self, Debug, Formatter},
ptr,
};
pub type ErrorCodeFormatter = fn(
error_code: ErrorCode,
next_formatter: Option<u8>,
f: Option<&mut Formatter<'_>>,
) -> (
ErrorCategoryHandle,
Result<Option<ErrorCodeFormatterVal>, fmt::Error>,
);
#[repr(transparent)]
pub struct ErrorCodeFormatterVal(ErrorCodeFormatter);
impl ErrorCodeFormatterVal {
pub fn new(func: ErrorCodeFormatter) -> ErrorCodeFormatterVal {
ErrorCodeFormatterVal(func)
}
pub fn into(self) -> ErrorCodeFormatter {
self.0
}
}
pub trait ErrorCategory: Copy + Into<ErrorCode> + From<ErrorCode> + Debug {
const NAME: &'static str;
type L0: ErrorCategory;
type L1: ErrorCategory;
type L2: ErrorCategory;
type L3: ErrorCategory;
type L4: ErrorCategory;
type L5: ErrorCategory;
fn chainable_category_formatters() -> &'static [ErrorCodeFormatter] {
&[
format_chained::<Self::L0>,
format_chained::<Self::L1>,
format_chained::<Self::L2>,
format_chained::<Self::L3>,
format_chained::<Self::L4>,
format_chained::<Self::L5>,
]
}
}
#[derive(Debug)]
pub struct ErrorCategoryHandle {
name: &'static str,
chainable_category_formatters: fn() -> &'static [ErrorCodeFormatter],
}
impl ErrorCategoryHandle {
pub fn new<C: ErrorCategory>() -> ErrorCategoryHandle {
Self {
name: C::NAME,
chainable_category_formatters: C::chainable_category_formatters,
}
}
pub fn name(&self) -> &'static str {
self.name
}
#[inline]
pub fn is_handle_of<C: ErrorCategory>(&self) -> bool {
ptr::eq(self.name.as_ptr(), C::NAME.as_ptr())
&& ptr::eq(
self.chainable_category_formatters as *const (),
C::chainable_category_formatters as *const (),
)
}
}
impl PartialEq for ErrorCategoryHandle {
fn eq(&self, other: &ErrorCategoryHandle) -> bool {
ptr::eq(self.name.as_ptr(), other.name.as_ptr())
&& ptr::eq(
self.chainable_category_formatters as *const (),
other.chainable_category_formatters as *const (),
)
}
}
impl Eq for ErrorCategoryHandle {}
pub fn format_chained<C: ErrorCategory>(
error_code: ErrorCode,
next_formatter: Option<u8>,
f: Option<&mut Formatter<'_>>,
) -> (
ErrorCategoryHandle,
Result<Option<ErrorCodeFormatterVal>, fmt::Error>,
) {
let fmt_res = if let Some(f) = f {
let err: C = error_code.into();
write!(f, "{}({}): {:?}", C::NAME, error_code, err)
} else {
Ok(())
};
(
ErrorCategoryHandle::new::<C>(),
fmt_res.map(|_| {
next_formatter.and_then(|idx| {
let idx = idx as usize;
let formatters = C::chainable_category_formatters();
if idx < formatters.len() {
Some(ErrorCodeFormatterVal::new(formatters[idx]))
} else {
None
}
})
}),
)
}
#[derive(Debug, Clone, Copy)]
pub enum Unused {}
impl ErrorCategory for Unused {
const NAME: &'static str = "";
type L0 = Unused;
type L1 = Unused;
type L2 = Unused;
type L3 = Unused;
type L4 = Unused;
type L5 = Unused;
fn chainable_category_formatters() -> &'static [ErrorCodeFormatter] {
&[]
}
}
impl From<ErrorCode> for Unused {
fn from(_: ErrorCode) -> Self {
unreachable!()
}
}
impl Into<ErrorCode> for Unused {
fn into(self) -> ErrorCode {
match self {}
}
}