use crate::Parser;
use core::{
fmt::{self, Display},
marker::PhantomData,
};
#[derive(PartialEq, Eq, Clone)]
pub struct ParseError<'a> {
start_offset: u32,
end_offset: u32,
direction: ParseDirection,
kind: ErrorKind,
extra_message: &'static &'static str,
_lifetime: PhantomData<&'a [u8]>,
}
impl<'a> ParseError<'a> {
#[inline(always)]
pub const fn new(parser: &Parser<'a>, kind: ErrorKind) -> Self {
Self {
start_offset: parser.start_offset,
end_offset: parser.start_offset + parser.str.len() as u32,
direction: parser.parse_direction,
kind,
extra_message: &"",
_lifetime: PhantomData,
}
}
pub const fn other_error(parser: &Parser<'a>, extra_message: &'static &'static str) -> Self {
Self {
start_offset: parser.start_offset,
end_offset: parser.start_offset + parser.str.len() as u32,
direction: parser.parse_direction,
kind: ErrorKind::Other,
extra_message,
_lifetime: PhantomData,
}
}
pub const fn copy(&self) -> Self {
Self {
start_offset: self.start_offset,
end_offset: self.end_offset,
direction: self.direction,
kind: self.kind,
extra_message: self.extra_message,
_lifetime: PhantomData,
}
}
#[inline(always)]
pub const fn offset(&self) -> usize {
(match self.direction {
ParseDirection::FromStart | ParseDirection::FromBoth => self.start_offset,
ParseDirection::FromEnd => self.end_offset,
}) as usize
}
pub const fn error_direction(&self) -> ParseDirection {
self.direction
}
pub const fn kind(&self) -> ErrorKind {
self.kind
}
const fn extra_message(&self) -> &'static str {
self.extra_message
}
const fn error_for_direction(&self) -> &'static str {
match self.direction {
ParseDirection::FromStart => "error from the start at the ",
ParseDirection::FromEnd => "error from the end at the ",
ParseDirection::FromBoth => "error from the start and end at the ",
}
}
const fn error_suffix(&self) -> &'static str {
match self.kind {
ErrorKind::ParseInteger => " while parsing an integer",
ErrorKind::ParseBool => " while parsing a bool",
ErrorKind::Find => " while trying to find and skip a pattern",
ErrorKind::Strip => " while trying to strip a pattern",
ErrorKind::SplitExhausted => ": called split on empty parser",
ErrorKind::DelimiterNotFound => ": delimiter (for splitting) could not be found",
ErrorKind::Other => {
if self.extra_message.is_empty() {
" other error"
} else {
": "
}
}
}
}
}
impl<'a> Display for ParseError<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.error_for_direction())?;
Display::fmt(&self.offset(), f)?;
f.write_str(" byte offset")?;
f.write_str(self.error_suffix())?;
f.write_str(self.extra_message())?;
Ok(())
}
}
const _: () = {
use const_panic::{
PanicFmt, PanicVal, flatten_panicvals,
fmt::{self as cfmt, ComputePvCount, FmtArg, FmtKind},
};
impl PanicFmt for ParseError<'_> {
type This = Self;
type Kind = const_panic::IsCustomType;
const PV_COUNT: usize = ComputePvCount {
field_amount: 5,
summed_pv_count: {
<u32>::PV_COUNT
+ <u32>::PV_COUNT
+ <ParseDirection>::PV_COUNT
+ <ErrorKind>::PV_COUNT
+ <&'static &'static str>::PV_COUNT
},
delimiter: cfmt::TypeDelim::Braced,
}
.call();
}
impl<'a> ParseError<'a> {
pub const fn to_panicvals(&self, fmtarg: FmtArg) -> [PanicVal<'a>; ParseError::PV_COUNT] {
match fmtarg.fmt_kind {
FmtKind::Debug => {
flatten_panicvals! {fmtarg;
"ParseError",
open: cfmt::OpenBrace,
"start_offset: ", u32 => self.start_offset, cfmt::COMMA_SEP,
"end_offset: ", u32 => self.end_offset, cfmt::COMMA_SEP,
"direction: ", ParseDirection => self.direction, cfmt::COMMA_SEP,
"kind: ", ErrorKind => self.kind, cfmt::COMMA_SEP,
"extra_message: ", &'static &'static str =>
self.extra_message, cfmt::COMMA_TERM,
close: cfmt::CloseBrace,
}
}
_ => const_panic::utils::flatten_panicvals(&[&[
PanicVal::write_str(self.error_for_direction()),
PanicVal::from_usize(self.offset(), FmtArg::DEBUG),
PanicVal::write_str(" byte offset"),
PanicVal::write_str(self.error_suffix()),
PanicVal::write_str(self.extra_message()),
]]),
}
}
}
impl fmt::Debug for ParseError<'_> {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_struct("ParseError")
.field("start_offset", &self.start_offset)
.field("end_offset", &self.end_offset)
.field("direction", &self.direction)
.field("kind", &self.kind)
.field("extra_message", &self.extra_message)
.finish()
}
}
macro_rules! fieldless_enum_fmt {
($self:expr, [$($variant:ident)*]) => (
match $self {
$(Self::$variant => PanicVal::write_str(stringify!($variant)),)*
}
)
}
impl PanicFmt for ParseDirection {
type This = Self;
type Kind = const_panic::IsCustomType;
const PV_COUNT: usize = 1;
}
impl ParseDirection {
pub const fn to_panicval<'a>(&self, _fmtarg: FmtArg) -> PanicVal<'a> {
fieldless_enum_fmt! {self, [FromStart FromEnd FromBoth]}
}
pub const fn to_panicvals<'a>(
&self,
fmtarg: FmtArg,
) -> [PanicVal<'a>; ParseDirection::PV_COUNT] {
[self.to_panicval(fmtarg)]
}
}
impl PanicFmt for ErrorKind {
type This = Self;
type Kind = const_panic::IsCustomType;
const PV_COUNT: usize = 1;
}
impl ErrorKind {
pub const fn to_panicval<'a>(&self, _fmtarg: FmtArg) -> PanicVal<'a> {
fieldless_enum_fmt! {self, [
ParseInteger
ParseBool
Find
Strip
SplitExhausted
DelimiterNotFound
Other
]}
}
pub const fn to_panicvals<'a>(
&self,
fmtarg: FmtArg,
) -> [PanicVal<'a>; ErrorKind::PV_COUNT] {
[self.to_panicval(fmtarg)]
}
}
};
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum ParseDirection {
FromStart = 0,
FromEnd = 1,
FromBoth = 2,
}
#[non_exhaustive]
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum ErrorKind {
ParseInteger,
ParseBool,
Find,
Strip,
SplitExhausted,
DelimiterNotFound,
Other,
}