Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ This repository also makes use of mdBook plugins. To install mdBook and the plug
```console
cargo install --version 0.4.21 mdbook
cargo install --version 0.6.7 mdbook-alerts
cargo install --version 0.7.7 mdbook-linkcheck
```

[cargo]: https://doc.rust-lang.org/cargo
Expand Down
2 changes: 2 additions & 0 deletions component-model/book.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,5 @@ edit-url-template = "https://github.com/bytecodealliance/component-docs/tree/mai
additional-css = ["theme/head.hbs"]

[preprocessor.alerts]

[output.linkcheck]
5 changes: 4 additions & 1 deletion component-model/examples/example-host/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@
name = "example-host"
version = "0.1.0"
edition = "2021"
description = """
Example Rust-based WebAssembly host that executes WebAssembly components
"""

[dependencies]
anyhow = "1.0.72"
async-std = { version = "1.13", features = ["attributes"] }
clap = { version = "4", features = ["derive"] }
wasmtime = "27.0"
wasmtime-wasi = "27.0"
anyhow = "1.0.72"
46 changes: 34 additions & 12 deletions component-model/examples/example-host/README.md
Original file line number Diff line number Diff line change
@@ -1,25 +1,47 @@
# Rust Host Application for Example Components

This is a native Rust CLI application that can run components of the following
`example` world, which simply defines an add function that takes in two
parameters.
This is a native Rust CLI application that can run [WebAssembly Components][wasm-components] of the `adder` world,
defined in [`examples/tutorial/wit/adder/world.wit`][adder-wit], using [WebAssembly Interface Types ("WIT")][wit].

The `adder` world exports an interface called `add` which defines an function that takes two unsigned and adds them:

```wit
package example:component;
package docs:adder@0.1.0;

interface add {
add: func(x: u32, y: u32) -> u32;
}

world example {
export add: func(x: s32, y: s32) -> s32;
world adder {
export add;
}
```

The application uses [`wasmtime`](https://github.com/bytecodealliance/wasmtime)
crates to generate Rust bindings, bring in WASI worlds, and executes the `add`
function of the component.
The application uses WebAssembly ecosystem crates (e.g. [`wasmtime`][wasmtime]) to generate Rust bindings, instantiate WASI worlds, and
executes the exported `add` function (`docs:adder/add.add`) of a provided component.

This host binary takes in two unsigned 32bit integers (`u32`) operands and a path to a component. This host then:

It takes in two operands and a path to a component. It passes the operands to
the component and prints the result generated by the component.
1. Loads the component from the given path
2. Instantiates it as an implementer of the `adder` world
3. Executes the `add` function exported by the component
4. Prints the result

```sh
If running with [`cargo`][cargo] (part of the [Rust toolchain][rust-toolchain]), then you should see output like the following:

```
$ cargo run --release -- 1 2 add.wasm
1 + 2 = 3
```

> [!NOTE]
> `add.wasm` is available in thsi folder, but can be replaced with your own built WebAssembly component
> at any time (written in any language that supports WebAssembly Components), given that it satisfies
> the `adder` world described above.

[wasmtime]: https://github.com/bytecodealliance/wasmtime
[wasm-components]: https://component-model.bytecodealliance.org/design/components.html
[adder-wit]: https://github.com/bytecodealliance/component-docs/tree/main/component-model/examples/tutorial/wit/adder/world.wit
[wit]: https://component-model.bytecodealliance.org/design/wit.html
[cargo]: https://doc.rust-lang.org/cargo/
[rust-toolchain]: https://www.rust-lang.org/tools/install
5 changes: 0 additions & 5 deletions component-model/examples/example-host/add.wit

This file was deleted.

52 changes: 39 additions & 13 deletions component-model/examples/example-host/src/async_add.rs
Original file line number Diff line number Diff line change
@@ -1,35 +1,61 @@
use anyhow::Context;
use std::path::PathBuf;
use wasmtime::component::*;

use anyhow::Context;
use wasmtime::component::{Component, Linker};
use wasmtime::{Config, Engine, Store};

use crate::state::States;

bindgen!({
path: "add.wit",
world: "example",
async: true
});
mod bindings {
//! Generated code for the
wasmtime::component::bindgen!({
path: "../tutorial/wit/adder/world.wit",
world: "adder",
async: true
});
}

pub async fn add(path: PathBuf, x: i32, y: i32) -> wasmtime::Result<i32> {
/// Perform the add operation for a given WebAssembly component
///
/// This operation asynchronously (as opposed to synchronously
/// without an async runtime like `tokio` or `async-std`).
///
/// # Arguments
///
/// * `path` - Path to the Wasm component bytes
/// * `x` - The left hand side of the addition
/// * `y` - The right hand side of the addition
///
pub async fn add(path: PathBuf, x: u32, y: u32) -> wasmtime::Result<u32> {
// Construct engine
let mut config = Config::default();
config.async_support(true);
let engine = Engine::new(&config)?;

// Construct component
let component = Component::from_file(&engine, path).context("Component file not found")?;

// Construct store for storing running states of the component
let wasi_view = States::new();
let mut store = Store::new(&engine, wasi_view);

// Construct linker for linking interfaces.
// For this simple adder component, no need to manually link additional interfaces.
let mut linker = Linker::new(&engine);
// Add wasi exports to linker to support io interfaces

// Add wasi exports to linker to support I/O (as in `wasi:io`) interfaces
// see: https://github.com/WebAssembly/wasi-io
wasmtime_wasi::add_to_linker_async(&mut linker)?;
let instance = Example::instantiate_async(&mut store, &component, &linker)

// Instantiate the component as an instance of the `adder` world,
// with the generated bindings
let instance = bindings::Adder::instantiate_async(&mut store, &component, &linker)
.await
.context("Failed to instantiate the example world")?;

// Call the add function on instance
instance
.docs_adder_add()
.call_add(&mut store, x, y)
.await
.context("Failed to call add function")
}
.context("calling add function")
}
12 changes: 6 additions & 6 deletions component-model/examples/example-host/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
mod async_add;
mod sync_add;
mod state;

use clap::Parser;
use std::path::PathBuf;

mod async_add;
mod state;
mod sync_add;

/// A CLI for executing WebAssembly components that
/// implement the `example` world.
#[derive(Parser)]
#[clap(name = "add-host", version = env!("CARGO_PKG_VERSION"))]
struct AddApp {
/// The first operand
x: i32,
x: u32,
/// The second operand
y: i32,
y: u32,
/// The path to the component.
#[clap(value_name = "COMPONENT_PATH")]
component: PathBuf,
Expand Down
2 changes: 1 addition & 1 deletion component-model/examples/example-host/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,4 @@ impl WasiView for States {
fn ctx(&mut self) -> &mut WasiCtx {
&mut self.ctx
}
}
}
53 changes: 39 additions & 14 deletions component-model/examples/example-host/src/sync_add.rs
Original file line number Diff line number Diff line change
@@ -1,32 +1,57 @@
use crate::state::States;
use anyhow::Context;
use std::path::PathBuf;
use wasmtime::component::{bindgen, Component, Linker};

use anyhow::Context;
use wasmtime::component::{Component, Linker};
use wasmtime::{Engine, Store};
use wasmtime_wasi;

bindgen!({
path: "add.wit",
world: "example",
async: false
});
use crate::state::States;

mod bindings {
wasmtime::component::bindgen!({
path: "../tutorial/wit/adder/world.wit",
world: "adder",
async: false
});
}

pub fn add(path: PathBuf, x: i32, y: i32) -> wasmtime::Result<i32> {
/// Perform a add operation for a given WebAssembly component
///
/// This operation happens synchronously (as opposed to asynchronously
/// powered by an async runtime like `tokio` or `async-std`).
///
/// # Arguments
///
/// * `path` - Path to the Wasm component bytes
/// * `x` - The left hand side of the addition
/// * `y` - The right hand side of the addition
///
pub fn add(path: PathBuf, x: u32, y: u32) -> wasmtime::Result<u32> {
// Construct engine
let engine = Engine::default();

// Construct component
let component = Component::from_file(&engine, path).context("Component file not found")?;

// Construct store for storing running states of the component
let wasi_view = States::new();
let mut store = Store::new(&engine, wasi_view);

// Construct linker for linking interfaces.
// For this simple adder component, no need to manually link additional interfaces.
let mut linker = Linker::new(&engine);
// Add wasi exports to linker to support io interfaces

// Add wasi exports to linker to support I/O (as in `wasi:io`) interfaces
// see: https://github.com/WebAssembly/wasi-io
wasmtime_wasi::add_to_linker_sync(&mut linker).expect("Could not add wasi to linker");
let instance = Example::instantiate(&mut store, &component, &linker)

// Instantiate the component as an instance of the `adder` world,
// with the generated bindings
let instance = bindings::Adder::instantiate(&mut store, &component, &linker)
.context("Failed to instantiate the example world")?;

// Call the add function on instance
instance
.docs_adder_add()
.call_add(&mut store, x, y)
.context("Failed to call add function")
}
.context("calling add function")
}
2 changes: 1 addition & 1 deletion component-model/examples/tutorial/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ package docs:adder@0.1.0;


interface add {
add: func(a: u32, b: u32) -> u32;
add: func(x: u32, y: u32) -> u32;
}

world adder {
Expand Down
6 changes: 3 additions & 3 deletions component-model/examples/tutorial/adder/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion component-model/examples/tutorial/adder/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ version = "0.1.0"
edition = "2021"

[dependencies]
wit-bindgen-rt = { version = "0.24.0", features = ["bitflags"] }
wit-bindgen-rt = { version = "0.37.0", features = ["bitflags"] }

[lib]
crate-type = ["cdylib"]
Expand Down
Loading