Macro wiggle::from_witx

source ·
from_witx!() { /* proc-macro */ }
Expand description

This macro expands to a set of pub Rust modules:

  • The types module contains definitions for each typename declared in the witx document. Type names are translated to the Rust-idiomatic CamelCase.

  • For each module defined in the witx document, a Rust module is defined containing definitions for that module. Module names are translated to the Rust-idiomatic snake_case.

    • For each @interface func defined in a witx module, an abi-level function is generated which takes ABI-level arguments, along with a ref that impls the module trait, and a GuestMemory implementation. Users typically won’t use these abi-level functions: Either the wasmtime_integration macro or the lucet-wiggle crates adapt these to work with a particular WebAssembly engine.

    • A public “module trait” is defined (called the module name, in SnakeCase) which has a &self method for each function in the module. These methods takes idiomatic Rust types for each argument and return Result<($return_types),$error_type>

    • When the wiggle crate is built with the wasmtime_integration feature, each module contains an add_to_linker function to add it to a wasmtime::Linker.

Arguments are provided using Rust struct value syntax.

  • witx takes a list of string literal paths. Paths are relative to the CARGO_MANIFEST_DIR of the crate where the macro is invoked. Alternatively, witx_literal takes a string containing a complete witx document.
  • Optional: errors takes a mapping of witx identifiers to types, e.g { errno => YourErrnoType }. This allows you to use the UserErrorConversion trait to map these rich errors into the flat witx type, or to terminate WebAssembly execution by trapping.
    • Instead of requiring the user to define an error type, wiggle can generate an error type for the user which has conversions to/from the base type, and permits trapping, using the syntax errno => trappable AnErrorType.
  • Optional: async takes a set of witx modules and functions which are made Rust async functions in the module trait.

§Example

use wiggle::GuestPtr;
wiggle::from_witx!({
    witx_literal: "
        (typename $errno
          (enum (@witx tag u32)
            $ok
            $invalid_arg
            $io
            $overflow))
         (typename $alias_to_float f32)
         (module $example
           (@interface func (export \"int_float_args\")
             (param $an_int u32)
             (param $some_floats (list f32))
             (result $r (expected (error $errno))))
           (@interface func (export \"double_int_return_float\")
             (param $an_int u32)
             (result $r (expected $alias_to_float (error $errno)))))
    ",
    errors: { errno => YourRichError },
    async: { example::double_int_return_float },
});

/// Witx generates a set of traits, which the user must impl on a
/// type they define. We call this the ctx type. It stores any context
/// these functions need to execute.
pub struct YourCtxType {}

/// Witx provides a hook to translate "rich" (arbitrary Rust type) errors
/// into the flat error enums used at the WebAssembly interface. You will
/// need to impl the `types::UserErrorConversion` trait to provide a translation
/// from this rich type.
#[derive(Debug)]
pub enum YourRichError {
    InvalidArg(String),
    Io(std::io::Error),
    Overflow,
    Trap(String),
}

/// The above witx text contains one module called `$example`. So, we must
/// implement this one method trait for our ctx type.
#[wiggle::async_trait]
/// We specified in the `async_` field that `example::double_int_return_float`
/// is an asynchronous method. Therefore, we use the `async_trait` proc macro
/// to define this trait, so that `double_int_return_float` can be an `async fn`.
/// `wiggle::async_trait` is defined as `#[async_trait::async_trait(?Send)]` -
/// in wiggle, async methods do not have the Send constaint.
impl example::Example for YourCtxType {
    /// The arrays module has two methods, shown here.
    /// Note that the `GuestPtr` type comes from `wiggle`,
    /// whereas the witx-defined types like `Excuse` and `Errno` come
    /// from the `pub mod types` emitted by the `wiggle::from_witx!`
    /// invocation above.
    fn int_float_args(&mut self, _int: u32, _floats: &GuestPtr<[f32]>)
        -> Result<(), YourRichError> {
        unimplemented!()
    }
    async fn double_int_return_float(&mut self, int: u32)
        -> Result<f32, YourRichError> {
        Ok(int.checked_mul(2).ok_or(YourRichError::Overflow)? as f32)
    }
}

/// For all types used in the `error` an `expected` in the witx document,
/// you must implement `GuestErrorType` which tells wiggle-generated
/// code what value to return when the method returns Ok(...).
impl wiggle::GuestErrorType for types::Errno {
    fn success() -> Self {
        unimplemented!()
    }
}

/// If you specify a `error` mapping to the macro, you must implement the
/// `types::UserErrorConversion` for your ctx type as well. This trait gives
/// you an opportunity to store or log your rich error type, while returning
/// a basic witx enum to the WebAssembly caller. It also gives you the ability
/// to terminate WebAssembly execution by trapping.

impl types::UserErrorConversion for YourCtxType {
    fn errno_from_your_rich_error(&mut self, e: YourRichError)
        -> Result<types::Errno, wiggle::wasmtime_crate::Error>
    {
        println!("Rich error: {:?}", e);
        match e {
            YourRichError::InvalidArg{..} => Ok(types::Errno::InvalidArg),
            YourRichError::Io{..} => Ok(types::Errno::Io),
            YourRichError::Overflow => Ok(types::Errno::Overflow),
            YourRichError::Trap(s) => Err(wiggle::wasmtime_crate::Error::msg(s)),
        }
    }
}