#[allow(unused_imports)]
use crate::ErrorCategory;
use crate::ErrorCode;
pub const ERROR_CHAIN_LEN: usize = 4;
#[derive(Clone, Copy, PartialEq, Eq)]
#[repr(transparent)]
pub struct ErrorData {
data: u32,
}
mod consts {
pub const CODE_MASK: [u32; 5] = [
0x0000_000f,
0x0000_00f0,
0x0000_0f00,
0x0000_f000,
0x000f_0000,
];
pub const ALL_CODE_MASK: u32 = 0x000f_ffff;
pub const CODE_WIDTH: u32 = 4;
#[inline(always)]
pub const fn make_code(value: super::ErrorCode) -> u32 {
(value & 0b1111) as u32
}
pub const FORMATTER_MASK: [u32; 4] = [0x0070_0000, 0x0380_0000, 0x1c00_0000, 0xe000_0000];
pub const ALL_FORMATTER_MASK: u32 = 0xfff0_0000;
pub const FORMATTER_BITOFFSET: u32 = 20;
pub const FORMATTER_IDX_WIDTH: u32 = 3;
#[inline(always)]
pub const fn make_formatter_idx(value: u8) -> u32 {
(value & 0b0111) as u32
}
}
impl ErrorData {
pub const fn new(error_code: ErrorCode) -> ErrorData {
ErrorData {
data: error_code as u32 & consts::CODE_MASK[0],
}
}
pub fn set_code(&mut self, code: ErrorCode) -> ErrorCode {
let old_ec = consts::make_code(self.data as u8) as ErrorCode;
self.data = (self.data & !(consts::CODE_MASK[0])) | consts::make_code(code);
old_ec
}
#[inline]
pub fn code(&self) -> ErrorCode {
(self.data & consts::CODE_MASK[0]) as ErrorCode
}
pub fn first_formatter_index(&self) -> Option<u8> {
let fmt_index =
((self.data & consts::FORMATTER_MASK[0]) >> consts::FORMATTER_BITOFFSET) as u8;
if fmt_index > 0 {
Some(fmt_index - 1)
} else {
None
}
}
pub fn chain_len(&self) -> usize {
let mut mask = consts::FORMATTER_MASK[0];
for fmt_index in 0..ERROR_CHAIN_LEN {
if (self.data & mask) == 0 {
return fmt_index;
}
mask <<= consts::FORMATTER_IDX_WIDTH;
}
ERROR_CHAIN_LEN
}
#[inline]
pub fn chain_full(&self) -> bool {
self.chain_len() == ERROR_CHAIN_LEN
}
pub fn push_front(
&mut self,
error_code: ErrorCode,
category_index: u8,
) -> Option<(ErrorCode, u8)> {
let fmt_index_back = self.data & consts::FORMATTER_MASK[ERROR_CHAIN_LEN - 1];
let result = if fmt_index_back > 0 {
let ec_back = (self.data & consts::CODE_MASK[ERROR_CHAIN_LEN])
>> (ERROR_CHAIN_LEN as u32 * consts::CODE_WIDTH);
let fmt_index_back = fmt_index_back
>> ((ERROR_CHAIN_LEN as u32 - 1) * consts::FORMATTER_IDX_WIDTH
+ consts::FORMATTER_BITOFFSET);
Some((ec_back as ErrorCode, (fmt_index_back - 1) as u8))
} else {
None
};
let fmt_indices = ((self.data & consts::ALL_FORMATTER_MASK) << consts::FORMATTER_IDX_WIDTH)
| (consts::make_formatter_idx(category_index + 1) << consts::FORMATTER_BITOFFSET);
let err_codes = ((self.data << consts::CODE_WIDTH) & consts::ALL_CODE_MASK)
| consts::make_code(error_code);
self.data = fmt_indices | err_codes;
result
}
pub fn chain(&mut self, error_code: ErrorCode, category_index: u8) {
let overflow = self.push_front(error_code, category_index);
#[cfg(feature = "panic-on-overflow")]
debug_assert!(
overflow.is_none(),
"chaining two errors overflowed; error chain is full"
);
}
pub(crate) fn iter_chain(&self) -> ErrorDataChainIter {
ErrorDataChainIter {
error_codes: (self.data & consts::ALL_CODE_MASK) >> consts::CODE_WIDTH,
formatters: (self.data & consts::ALL_FORMATTER_MASK) >> consts::FORMATTER_BITOFFSET,
}
}
}
pub(crate) struct ErrorDataChainIter {
error_codes: u32,
formatters: u32,
}
impl Iterator for ErrorDataChainIter {
type Item = (ErrorCode, Option<u8>);
fn next(&mut self) -> Option<Self::Item> {
if self.formatters > 0 {
let ec = self.error_codes & consts::CODE_MASK[0];
self.error_codes >>= consts::CODE_WIDTH;
self.formatters >>= consts::FORMATTER_IDX_WIDTH;
let next_fmt_index = {
let next_fmt_index = consts::make_formatter_idx(self.formatters as u8);
if next_fmt_index > 0 {
Some(next_fmt_index as u8 - 1)
} else {
None
}
};
Some((ec as ErrorCode, next_fmt_index))
} else {
None
}
}
}