fizzyx 0.1.0

Safe, ergonomic Rust bindings for the Fizzy WebAssembly interpreter.
Documentation
//! Error and result types.

use core::fmt;
use fizzyx_sys as sys;

/// A specialized [`Result`](core::result::Result) type for `fizzyx` operations.
pub type Result<T> = core::result::Result<T, Error>;

/// An error returned by the `fizzyx` API.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Error {
    /// The Wasm binary is malformed and could not be parsed.
    Malformed(String),
    /// The Wasm module is well-formed but failed validation.
    Invalid(String),
    /// Instantiation of the module failed (e.g. an unresolved or mistyped import).
    Instantiation(String),
    /// A function or export with the given name was not found.
    ExportNotFound(String),
    /// Execution of a function trapped.
    Trap,
    /// The number of provided arguments or results did not match the signature.
    ArityMismatch {
        /// The number of values the signature expects.
        expected: usize,
        /// The number of values that were provided.
        provided: usize,
    },
    /// A provided value's type did not match the expected type.
    TypeMismatch {
        /// The position of the mismatched value.
        index: usize,
        /// A human-readable description of the expected type.
        expected: &'static str,
        /// A human-readable description of the provided type.
        found: &'static str,
    },
    /// A value or global used a type outside the WebAssembly 1.0 numeric types.
    UnsupportedType,
    /// An attempt was made to write to an immutable global.
    GlobalImmutable,
    /// A memory access was out of bounds.
    MemoryOutOfBounds {
        /// The byte offset of the attempted access.
        offset: usize,
        /// The length in bytes of the attempted access.
        length: usize,
        /// The current size of the memory in bytes.
        size: usize,
    },
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::Malformed(msg) => write!(f, "malformed Wasm module: {msg}"),
            Self::Invalid(msg) => write!(f, "invalid Wasm module: {msg}"),
            Self::Instantiation(msg) => write!(f, "failed to instantiate module: {msg}"),
            Self::ExportNotFound(name) => write!(f, "export `{name}` not found"),
            Self::Trap => write!(f, "execution trapped"),
            Self::ArityMismatch { expected, provided } => write!(
                f,
                "arity mismatch: expected {expected} values, got {provided}"
            ),
            Self::TypeMismatch {
                index,
                expected,
                found,
            } => write!(
                f,
                "type mismatch at index {index}: expected {expected}, got {found}"
            ),
            Self::UnsupportedType => {
                write!(f, "unsupported value type (only i32, i64, f32, f64 exist)")
            }
            Self::GlobalImmutable => write!(f, "cannot write to an immutable global"),
            Self::MemoryOutOfBounds {
                offset,
                length,
                size,
            } => write!(
                f,
                "memory access out of bounds: [{offset}, {offset}+{length}) exceeds size {size}"
            ),
        }
    }
}

impl std::error::Error for Error {}

/// Reads the message out of a [`sys::FizzyError`] as a Rust [`String`].
pub(crate) fn error_message(error: &sys::FizzyError) -> String {
    // SAFETY: Fizzy always writes a NUL-terminated string into `message`.
    let cstr = unsafe { core::ffi::CStr::from_ptr(error.message.as_ptr()) };
    cstr.to_string_lossy().into_owned()
}