use crate::source::Stack;
use crate::{BitWidth, Endianness, NumEncoding, Opaque, StrEncoding, StrLen};
use core::fmt;
use core::fmt::Formatter;
use embedded_io::{Error, ErrorKind, ReadExactError};
use parse_display::Display;
macro_rules! impl_error {
($name:ident) => {
#[cfg(feature = "unstable")]
impl core::error::Error for $name {}
#[cfg(all(not(feature = "unstable"), feature = "std"))]
impl std::error::Error for $name {}
};
}
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Display)]
pub enum Signedness {
Signed,
Unsigned,
}
#[derive(Debug, Display)]
#[non_exhaustive]
pub enum EncodingError {
#[display("IO Error occurred: {:0?}")]
IOError(ErrorKind),
#[display("Unexpected end of file/buffer")]
UnexpectedEnd,
#[display("Seek error: {0}")]
SeekError(SeekError),
#[display("Malformed var-int encoding")]
VarIntError,
#[display("Invalid bool value")]
InvalidBool,
#[display("String error: {0}")]
StringError(StringError),
#[display("A size of {requested} exceeded the max allowed value of {max}")]
MaxSizeExceeded { max: usize, requested: usize },
#[display("Unrecognized enum variant ({0})")]
InvalidVariant(Opaque),
#[display(r#"A value of "{value}" is too large to fit in {requested_width}"#)]
TooLarge {
value: Opaque,
requested_width: BitWidth,
},
#[display("Expected {expected} value, got {got} value instead")]
SignMismatch {
expected: Signedness,
got: Signedness,
},
#[display("Flatten error: {0}")]
FlattenError(FlattenError),
#[display("Lock error: couldn't lock a RefCell/Mutex/RwLock or similar")]
LockError,
#[display("Borrow error: {0}")]
BorrowError(BorrowError),
#[display("Validation error: {0}")]
ValidationError(
#[cfg(feature = "alloc")] alloc::string::String,
#[cfg(not(feature = "alloc"))] &'static str,
),
#[cfg(all(feature = "serde", feature = "alloc"))]
#[cfg_attr(feature = "unstable", doc(cfg(feature = "serde")))]
#[display("Serde error: {0}")]
SerdeError(alloc::string::String),
#[cfg(all(feature = "serde", not(feature = "alloc")))]
#[cfg_attr(feature = "unstable", doc(cfg(feature = "serde")))]
#[display("Serde error")]
SerdeError,
}
#[macro_export]
macro_rules! val_error {
($($tt:tt)*) => {{
$crate::EncodingError::validation_error(::core::format_args!($($tt)*))
}};
}
impl EncodingError {
pub fn validation_error<'a>(fmt: fmt::Arguments<'a>) -> Self {
#[cfg(feature = "alloc")]
#[allow(unused_imports)]
{
use alloc::string::ToString;
Self::ValidationError(fmt.to_string())
}
#[cfg(not(feature = "alloc"))]
{
if let Some(str) = fmt.as_str() {
Self::ValidationError(str)
} else {
Self::ValidationError("Unknown")
}
}
}
pub fn invalid_variant<V>(v: V) -> Self
where
Opaque: From<V>,
{
Self::InvalidVariant(Opaque::from(v))
}
}
impl Error for EncodingError {
fn kind(&self) -> ErrorKind {
match self {
EncodingError::IOError(io_error) => io_error.kind().into(),
EncodingError::UnexpectedEnd => ErrorKind::Other,
EncodingError::FlattenError(_) => ErrorKind::InvalidInput,
EncodingError::LockError => ErrorKind::Other,
EncodingError::BorrowError(_) => ErrorKind::Other,
#[cfg(all(feature = "serde", feature = "alloc"))]
EncodingError::SerdeError(_) => ErrorKind::Other,
#[cfg(all(feature = "serde", not(feature = "alloc")))]
EncodingError::SerdeError => ErrorKind::Other,
_ => ErrorKind::InvalidData,
}
}
}
impl_error!(EncodingError);
#[cfg(feature = "std")]
impl From<std::io::Error> for EncodingError {
fn from(value: std::io::Error) -> Self {
match value.kind() {
std::io::ErrorKind::UnexpectedEof => Self::UnexpectedEnd,
kind @ _ => Self::IOError(kind.into()),
}
}
}
impl From<ErrorKind> for EncodingError {
fn from(value: ErrorKind) -> Self {
Self::IOError(value)
}
}
impl<T: Error> From<ReadExactError<T>> for EncodingError {
fn from(value: ReadExactError<T>) -> Self {
match value {
ReadExactError::UnexpectedEof => Self::UnexpectedEnd,
ReadExactError::Other(io_error) => Self::IOError(io_error.kind().into()),
}
}
}
impl From<StringError> for EncodingError {
fn from(value: StringError) -> Self {
Self::StringError(value)
}
}
impl From<FlattenError> for EncodingError {
fn from(value: FlattenError) -> Self {
Self::FlattenError(value)
}
}
impl From<BorrowError> for EncodingError {
fn from(value: BorrowError) -> Self {
Self::BorrowError(value)
}
}
impl From<SeekError> for EncodingError {
fn from(value: SeekError) -> Self {
Self::SeekError(value)
}
}
#[derive(Debug, Display)]
#[non_exhaustive]
pub enum StringError {
#[display("String conversion error")]
ConversionError,
#[display("Invalid characters in string data")]
InvalidChar,
#[display("String is longer than expected")]
TooLong,
#[display("Null byte expected")]
MissingNull,
}
impl_error!(StringError);
#[derive(Debug, Display)]
#[non_exhaustive]
pub enum FlattenError {
#[display("Enum discriminant mismatch: expected {expected}, got {got}")]
VariantMismatch { expected: Opaque, got: Opaque },
#[display("Boolean mismatch: expected {expected}, got {got}")]
BoolMismatch { expected: bool, got: bool },
#[display("Length mismatch: expected {expected}, got {got}")]
LenMismatch { expected: usize, got: usize },
}
impl_error!(FlattenError);
#[derive(Debug, Display)]
#[non_exhaustive]
pub enum BorrowError {
#[display(
"String encoding mismatch: expected {found} while decoding a {while_decoding} string"
)]
StrEncodingMismatch {
found: StrEncoding,
while_decoding: StrEncoding,
},
#[display(
"String length encoding mismatch: expected {found} while decoding a {while_decoding} string"
)]
StrLenEncodingMismatch {
found: StrLen,
while_decoding: StrLen,
},
#[display("Endianness mismatch: stream contains {found} data, but system uses {system}")]
EndiannessMismatch {
found: Endianness,
system: Endianness,
},
#[display("Bit width mismatch: stream contains {found} data, but system uses {system}")]
BitWidthMismatch { found: BitWidth, system: BitWidth },
#[display("Non-borrowable numerical encoding: {num_encoding} can't be directly borrowed")]
NonBorrowableNumEncoding { num_encoding: NumEncoding },
#[display(
"Alignment mismatch: borrowing this data requires its alignment to match the system's"
)]
AlignmentMismatch,
}
impl_error!(BorrowError);
#[derive(Debug, Display)]
#[non_exhaustive]
pub enum SeekError {
#[display("Tried to seek to a negative offset: {0}")]
BeforeBeginning(isize),
#[display("Tried to seek to an offset beyond the end: {0}")]
AfterEnd(usize),
#[display("Tried to seek to the beginning/end but they are unknown")]
UnknownRange,
}
impl_error!(SeekError);
#[derive(Debug)]
pub struct TaggedError {
err: EncodingError,
stack: Stack,
}
impl core::fmt::Display for TaggedError {
#[inline]
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
#[cfg(feature = "alloc")]
{
if self.stack.frames.is_empty() {
return write!(f, "{}", self.err);
}
write!(f, "[{}] {}", self.stack, self.err)?;
}
#[cfg(not(feature = "alloc"))]
{
if let crate::source::Frame::Item(x) = self.stack.last_frame {
if x == "" {
return write!(f, "{}", self.err);
}
}
write!(f, "[{}] {}", self.stack, self.err)?;
}
Ok(())
}
}
impl TaggedError {
#[inline]
pub const fn from_stack(err: EncodingError, stack: Stack) -> Self {
Self { err, stack }
}
}
pub type EncodingResult<T> = Result<T, EncodingError>;