use core::{
convert::Infallible,
error::Error,
ffi::CStr,
fmt::{Debug, Display},
};
use crate::Read;
pub type ParseResult<T> = Result<T, ParseError>;
pub type PacketParseResult<T> = Result<T, PacketParseError>;
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
pub enum ParseError {
Unwanted,
NeedsHint,
TooSmall,
StraddledHeader,
NoRemainingChunks,
CannotAccept,
Reject,
IllegalValue,
}
impl ParseError {
#[inline]
pub fn as_cstr(&self) -> &'static CStr {
match self {
ParseError::Unwanted => c"Unwanted",
ParseError::NeedsHint => c"NeedsHint",
ParseError::TooSmall => c"TooSmall",
ParseError::StraddledHeader => c"StraddledHeader",
ParseError::NoRemainingChunks => c"NoRemainingChunks",
ParseError::CannotAccept => c"CannotAccept",
ParseError::Reject => c"Reject",
ParseError::IllegalValue => c"IllegalValue",
}
}
#[doc(hidden)]
pub fn convert_read_parse(self, reader: &mut impl Read) -> Self {
match self {
Self::TooSmall if reader.next_chunk().is_ok() => {
Self::StraddledHeader
}
a => a,
}
}
}
impl From<Infallible> for ParseError {
#[inline]
fn from(_: Infallible) -> Self {
unreachable!()
}
}
impl Display for ParseError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
ParseError::Unwanted => {
write!(f, "encountered header not permitted by the parser")
}
ParseError::NeedsHint => write!(
f,
"header/choice requires a hint to parse and none was provided"
),
ParseError::TooSmall => {
write!(f, "insufficient bytes in buffer to read current header")
}
ParseError::StraddledHeader => {
write!(f, "header is split across more than one buffer")
}
ParseError::NoRemainingChunks => write!(
f,
"packet contains no more chunks for parsing outstanding headers"
),
ParseError::CannotAccept => write!(
f,
"tried to accept packet with unfilled mandatory headers"
),
ParseError::Reject => write!(f, "packet was explicitly rejected"),
ParseError::IllegalValue => {
write!(f, "encountered field value not permitted by the parser")
}
}
}
}
impl Error for ParseError {}
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
pub struct PacketParseError {
label: &'static CRStr,
inner: ParseError,
}
impl PacketParseError {
#[inline]
pub const fn new(err: ParseError, label: &'static CRStr) -> Self {
Self { label, inner: err }
}
#[inline]
pub fn error(&self) -> &ParseError {
&self.inner
}
#[inline]
pub fn header(&self) -> &CRStr {
self.label
}
}
impl From<Infallible> for PacketParseError {
#[inline]
fn from(_: Infallible) -> Self {
unreachable!()
}
}
impl From<PacketParseError> for ParseError {
#[inline]
fn from(value: PacketParseError) -> Self {
value.inner
}
}
impl Display for PacketParseError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "error while parsing {}: {}", self.label, self.inner)
}
}
impl Error for PacketParseError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
Some(&self.inner)
}
}
#[derive(Clone, Copy, Eq, PartialEq, Hash)]
pub struct CRStr(&'static str, &'static CStr);
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
pub struct CRStrError;
impl Display for CRStrError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "input string was not null-terminated")
}
}
impl Error for CRStrError {}
impl CRStr {
#[inline]
pub const fn new(data: &'static str) -> Result<Self, CRStrError> {
if let Ok(cs) = CStr::from_bytes_with_nul(data.as_bytes()) {
if let Some((_nul, actual_str)) = data.as_bytes().split_last() {
Ok(Self(
unsafe { core::str::from_utf8_unchecked(actual_str) },
cs,
))
} else {
Err(CRStrError)
}
} else {
Err(CRStrError)
}
}
#[inline]
pub const fn new_unchecked(data: &'static str) -> Self {
match Self::new(data) {
Ok(v) => v,
Err(_) => panic!(),
}
}
#[inline]
pub fn as_str(&self) -> &'static str {
self.0
}
#[inline]
pub fn as_cstr(&self) -> &'static CStr {
self.1
}
}
impl AsRef<str> for CRStr {
#[inline]
fn as_ref(&self) -> &'static str {
self.as_str()
}
}
impl AsRef<CStr> for CRStr {
#[inline]
fn as_ref(&self) -> &'static CStr {
self.as_cstr()
}
}
impl Error for CRStr {}
impl Debug for CRStr {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{}", self.0)
}
}
impl Display for CRStr {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{}", self.0)
}
}