use super::ErrorCode;
use core::fmt::{self, Debug, Formatter};
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, PartialEq, Eq)]
pub struct ErrorCategoryHandle {
type_id: usize,
name: &'static str,
}
impl ErrorCategoryHandle {
pub fn new<C: ErrorCategory>() -> ErrorCategoryHandle {
Self {
name: C::NAME,
type_id: C::chainable_category_formatters as *const () as usize,
}
}
pub fn name(&self) -> &'static str {
self.name
}
pub fn is_same_category(&self, other: &ErrorCategoryHandle) -> bool {
self.type_id == other.type_id
}
}
#[inline(always)]
pub(crate) fn get_next_formatter<C: ErrorCategory>(
next_formatter_index: Option<u8>,
) -> Option<ErrorCodeFormatter> {
if let Some(idx) = next_formatter_index {
let formatters = C::chainable_category_formatters();
let idx = idx as usize;
if idx < formatters.len() {
return Some(formatters[idx]);
}
}
None
}
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();
writeln!(f, "- {}({}): {:?}", C::NAME, error_code, err)
} else {
Ok(())
};
(
ErrorCategoryHandle::new::<C>(),
fmt_res.map(|_| {
let func = get_next_formatter::<C>(next_formatter);
func.map(ErrorCodeFormatterVal::new)
}),
)
}
#[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 {}
}
}