bindgen!() { /* proc-macro */ }
runtime
and component-model
only.Expand description
Generate bindings for a WIT world.
This macro ingests a WIT world and will generate all the necessary
bindings for instantiating components that ascribe to the world
. This
provides a higher-level representation of working with a component than the
raw Instance
type which must be manually-type-checked and manually have
its imports provided via the Linker
type.
The most basic usage of this macro is:
wasmtime::component::bindgen!();
This will parse your projects WIT package in a wit
directory adjacent to
your crate’s Cargo.toml
. All of the *.wit
files in that directory are
parsed and then the single world
found will be used for bindings.
For example if your project contained:
// wit/my-component.wit
package my:project;
world hello-world {
import name: func() -> string;
export greet: func();
}
Then you can interact with the generated bindings like so:
use wasmtime::component::*;
use wasmtime::{Config, Engine, Store};
bindgen!();
struct MyState {
name: String,
}
// Imports into the world, like the `name` import for this world, are
// satisfied through traits.
impl HelloWorldImports for MyState {
// Note the `Result` return value here where `Ok` is returned back to
// the component and `Err` will raise a trap.
fn name(&mut self) -> wasmtime::Result<String> {
Ok(self.name.clone())
}
}
fn main() -> wasmtime::Result<()> {
// Configure an `Engine` and compile the `Component` that is being run for
// the application.
let mut config = Config::new();
config.wasm_component_model(true);
let engine = Engine::new(&config)?;
let component = Component::from_file(&engine, "./your-component.wasm")?;
// Instantiation of bindings always happens through a `Linker`.
// Configuration of the linker is done through a generated `add_to_linker`
// method on the bindings structure.
//
// Note that the closure provided here is a projection from `T` in
// `Store<T>` to `&mut U` where `U` implements the `HelloWorldImports`
// trait. In this case the `T`, `MyState`, is stored directly in the
// structure so no projection is necessary here.
let mut linker = Linker::new(&engine);
HelloWorld::add_to_linker(&mut linker, |state: &mut MyState| state)?;
// As with the core wasm API of Wasmtime instantiation occurs within a
// `Store`. The bindings structure contains an `instantiate` method which
// takes the store, component, and linker. This returns the `bindings`
// structure which is an instance of `HelloWorld` and supports typed access
// to the exports of the component.
let mut store = Store::new(
&engine,
MyState {
name: "me".to_string(),
},
);
let (bindings, _) = HelloWorld::instantiate(&mut store, &component, &linker)?;
// Here our `greet` function doesn't take any parameters for the component,
// but in the Wasmtime embedding API the first argument is always a `Store`.
bindings.call_greet(&mut store)?;
Ok(())
}
The function signatures within generated traits and on generated exports
match the component-model signatures as specified in the WIT world
item.
Note that WIT also has support for importing and exports interfaces within
worlds, which can be bound here as well:
For example this WIT input
// wit/my-component.wit
package my:project;
interface host {
gen-random-integer: func() -> u32;
sha256: func(bytes: list<u8>) -> string;
}
world hello-world {
import host;
export demo: interface {
run: func();
}
}
Then you can interact with the generated bindings like so:
use wasmtime::component::*;
use wasmtime::{Config, Engine, Store};
use my::project::host::Host;
bindgen!();
struct MyState {
// ...
}
// Note that the trait here is per-interface and within a submodule now.
impl Host for MyState {
fn gen_random_integer(&mut self) -> wasmtime::Result<u32> {
Ok(rand::thread_rng().gen())
}
fn sha256(&mut self, bytes: Vec<u8>) -> wasmtime::Result<String> {
// ...
}
}
fn main() -> wasmtime::Result<()> {
let mut config = Config::new();
config.wasm_component_model(true);
let engine = Engine::new(&config)?;
let component = Component::from_file(&engine, "./your-component.wasm")?;
let mut linker = Linker::new(&engine);
HelloWorld::add_to_linker(&mut linker, |state: &mut MyState| state)?;
let mut store = Store::new(
&engine,
MyState { /* ... */ },
);
let (bindings, _) = HelloWorld::instantiate(&mut store, &component, &linker)?;
// Note that the `demo` method returns a `&Demo` through which we can
// run the methods on that interface.
bindings.demo().call_run(&mut store)?;
Ok(())
}
The generated bindings can additionally be explored more fully with cargo doc
to see what types and traits and such are generated.
§Syntax
This procedural macro accepts a few different syntaxes. The primary purpose
of this macro is to locate a WIT package, parse it, and then extract a
world
from the parsed package. There are then codegen-specific options to
the bindings themselves which can additionally be specified.
Usage of this macro looks like:
// Parse the `wit/` folder adjacent to this crate's `Cargo.toml` and look
// for a single `world` in it. There must be exactly one for this to
// succeed.
bindgen!();
// Parse the `wit/` folder adjacent to this crate's `Cargo.toml` and look
// for the world `foo` contained in it.
bindgen!("foo");
// Parse the folder `other/wit/folder` adjacent to `Cargo.toml`.
bindgen!(in "other/wit/folder");
bindgen!("foo" in "other/wit/folder");
// Parse the file `foo.wit` as a single-file WIT package with no
// dependencies.
bindgen!("foo" in "foo.wit");
A more configured version of invoking this macro looks like:
bindgen!({
world: "foo", // not needed if `path` has one `world`
// same as in `bindgen!(in "other/wit/folder")
path: "other/wit/folder",
// Instead of `path` the WIT document can be provided inline if
// desired.
inline: "
package my:inline;
world foo {
// ...
}
",
// Add calls to `tracing::span!` before each import or export is called
// to log arguments and return values.
//
// This option defaults to `false`.
tracing: true,
// Imports will be async functions through #[async_trait] and exports
// are also invoked as async functions. Requires `Config::async_support`
// to be `true`.
//
// Note that this is only async for the host as the guest will still
// appear as if it's invoking blocking functions.
//
// This option defaults to `false`.
async: true,
// Alternative mode of async configuration where this still implies
// async instantiation happens, for example, but more control is
// provided over which imports are async and which aren't.
//
// Note that in this mode all exports are still async.
async: {
// All imports are async except for functions with these names
except_imports: ["foo", "bar"],
// All imports are synchronous except for functions with these names
//
// Note that this key cannot be specified with `except_imports`,
// only one or the other is accepted.
only_imports: ["foo", "bar"],
},
// This can be used to translate WIT return values of the form
// `result<T, error-type>` into `Result<T, RustErrorType>` in Rust.
// Users must define `RustErrorType` and the `Host` trait for the
// interface which defines `error-type` will have a method
// called `convert_error_type` which converts `RustErrorType`
// into `wasmtime::Result<ErrorType>`. This conversion can either
// return the raw WIT error (`ErrorType` here) or a trap.
//
// By default this option is not specified.
trappable_error_type: {
"wasi:io/streams/stream-error" => RustErrorType,
},
// All generated bindgen types are "owned" meaning types like `String`
// are used instead of `&str`, for example. This is the default and
// ensures that the same type used in both imports and exports uses the
// same generated type.
ownership: Owning,
// Alternative to `Owning` above where borrowed types attempt to be used
// instead. The `duplicate_if_necessary` configures whether duplicate
// Rust types will be generated for the same WIT type if necessary, for
// example when a type is used both as an import and an export.
ownership: Borrowing {
duplicate_if_necessary: true
},
// Restrict the code generated to what's needed for the interface
// imports in the inlined WIT document fragment.
interfaces: "
import wasi:cli/command;
",
// Remap imported interfaces or resources to types defined in Rust
// elsewhere. Using this option will prevent any code from being
// generated for interfaces mentioned here. Resources named here will
// not have a type generated to represent the resource.
//
// Interfaces mapped with this option should be previously generated
// with an invocation of this macro. Resources need to be mapped to a
// Rust type name.
with: {
// This can be used to indicate that entire interfaces have
// bindings generated elsewhere with a path pointing to the
// bindinges-generated module.
"wasi:random/random": wasmtime_wasi::bindings::random::random,
// Similarly entire packages can also be specified.
"wasi:cli": wasmtime_wasi::bindings::cli,
// Or, if applicable, entire namespaces can additionally be mapped.
"wasi": wasmtime_wasi::bindings,
// Versions are supported if multiple versions are in play:
"wasi:http/types@0.2.0": wasmtime_wasi_http::bindings::http::types,
"wasi:http@0.2.0": wasmtime_wasi_http::bindings::http,
// The `with` key can also be used to specify the `T` used in
// import bindings of `Resource<T>`. This can be done to configure
// which typed resource shows up in generated bindings and can be
// useful when working with the typed methods of `ResourceTable`.
"wasi:filesystem/types/descriptor": MyDescriptorType,
},
});