#![warn(missing_docs)]
use crate::sections::SectionOrderError;
use thiserror::Error;
pub use wasmbin_derive::Wasmbin;
#[derive(Error, Debug)]
pub enum DecodeErrorKind {
#[error(transparent)]
Io(#[from] std::io::Error),
#[error(transparent)]
Leb128(#[from] leb128::read::Error),
#[error(transparent)]
Utf8(#[from] std::string::FromUtf8Error),
#[error("Could not recognise discriminant 0x{discriminant:X} for type {ty}")]
UnsupportedDiscriminant {
ty: &'static str,
discriminant: i128,
},
#[error("Invalid module magic signature [{actual:02X?}]")]
InvalidMagic {
actual: [u8; 8],
},
#[error("Unrecognized data")]
UnrecognizedData,
#[error(transparent)]
SectionOutOfOrder(#[from] SectionOrderError),
}
#[derive(Debug)]
pub(crate) enum PathItem {
Name(&'static str),
Index(usize),
Variant(&'static str),
}
#[expect(clippy::partial_pub_fields)]
#[derive(Error, Debug)]
pub struct DecodeError {
path: Vec<PathItem>,
#[source]
pub kind: DecodeErrorKind,
}
impl DecodeError {
pub(crate) fn unsupported_discriminant<T: Decode>(discriminant: impl Into<i128>) -> Self {
DecodeErrorKind::UnsupportedDiscriminant {
ty: std::any::type_name::<T>(),
discriminant: discriminant.into(),
}
.into()
}
}
impl DecodeError {
pub(crate) fn in_path(mut self, item: PathItem) -> Self {
self.path.push(item);
self
}
}
impl<E: Into<DecodeErrorKind>> From<E> for DecodeError {
fn from(err: E) -> DecodeError {
DecodeError {
path: vec![],
kind: err.into(),
}
}
}
impl std::fmt::Display for DecodeError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
f.write_str("(root)")?;
for item in self.path.iter().rev() {
match *item {
PathItem::Name(name) => write!(f, ".{name}"),
PathItem::Index(index) => write!(f, "[{index}]"),
PathItem::Variant(variant) => write!(f, ":<{variant}>"),
}?;
}
write!(f, ": {}", self.kind)
}
}
impl From<std::num::TryFromIntError> for DecodeErrorKind {
fn from(_err: std::num::TryFromIntError) -> Self {
DecodeErrorKind::Leb128(leb128::read::Error::Overflow)
}
}
impl From<std::convert::Infallible> for DecodeErrorKind {
fn from(err: std::convert::Infallible) -> Self {
match err {}
}
}
pub trait Encode {
fn encode(&self, w: &mut impl std::io::Write) -> std::io::Result<()>;
}
pub trait Decode: Sized {
fn decode(r: &mut impl std::io::Read) -> Result<Self, DecodeError>;
}
macro_rules! encode_decode_as {
($ty:ty, {
$($lhs:tt <=> $rhs:tt,)*
} $(, |$other:pat_param| $other_handler:expr)?) => {
impl $crate::io::Encode for $ty {
#[allow(unused_parens)]
fn encode(&self, w: &mut impl std::io::Write) -> std::io::Result<()> {
match *self {
$($lhs => $rhs,)*
}.encode(w)
}
}
impl $crate::io::Decode for $ty {
#[allow(unused_parens)]
fn decode(r: &mut impl std::io::Read) -> Result<Self, $crate::io::DecodeError> {
Ok(match $crate::io::Decode::decode(r)? {
$($rhs => $lhs,)*
$($other => return $other_handler)?
})
}
}
};
}
pub(crate) use encode_decode_as;
pub trait DecodeWithDiscriminant: Decode {
type Discriminant: Decode + Copy + Into<i128>;
fn maybe_decode_with_discriminant(
discriminant: Self::Discriminant,
r: &mut impl std::io::Read,
) -> Result<Option<Self>, DecodeError>;
fn decode_with_discriminant(
discriminant: Self::Discriminant,
r: &mut impl std::io::Read,
) -> Result<Self, DecodeError> {
Self::maybe_decode_with_discriminant(discriminant, r)?
.ok_or_else(|| DecodeError::unsupported_discriminant::<Self>(discriminant))
}
fn decode_without_discriminant(r: &mut impl std::io::Read) -> Result<Self, DecodeError> {
Self::decode_with_discriminant(Self::Discriminant::decode(r)?, r)
}
}