From 7df9182163d160d630e3923d2787f04d2999d314 Mon Sep 17 00:00:00 2001 From: Victor Adossi Date: Fri, 29 Aug 2025 20:36:13 +0900 Subject: [PATCH 01/35] refactor(lang/rust): rust language guide This commit heavily refactors the rust language guide, accomplishing a few goals: - Moving from `cargo component` to native rust toolchain - Removing sections with advanced funtionality --- .gitattributes | 5 - .github/workflows/rust.yml | 14 +- component-model/examples/tutorial/README.md | 8 +- .../tutorial/adder/.cargo/config.toml | 2 + .../examples/tutorial/adder/Cargo.lock | 410 +++++++++++- .../examples/tutorial/adder/Cargo.toml | 14 +- .../examples/tutorial/adder/src/lib.rs | 37 +- component-model/examples/tutorial/adder/wit | 1 + .../tutorial/calculator/.cargo/config.toml | 2 + .../tutorial/calculator/src/bindings.rs | 4 +- .../examples/tutorial/jco/README.md | 2 +- component-model/src/language-support.md | 2 +- component-model/src/language-support/c.md | 3 +- component-model/src/language-support/rust.md | 626 +++++------------- component-model/src/tutorial.md | 59 +- 15 files changed, 650 insertions(+), 539 deletions(-) create mode 100644 component-model/examples/tutorial/adder/.cargo/config.toml create mode 120000 component-model/examples/tutorial/adder/wit create mode 100644 component-model/examples/tutorial/calculator/.cargo/config.toml diff --git a/.gitattributes b/.gitattributes index a0596a9f..138c0689 100644 --- a/.gitattributes +++ b/.gitattributes @@ -4,11 +4,6 @@ # Ensure binary files aren't considered as text *.wasm binary -# Genrated code from cargo-component -component-model/examples/tutorial/*/src/bindings.rs linguist-generated - # Generated code from JS tutorial component-model/examples/tutorial/jco/package-lock.json linguist-generated -Cargo-component.lock linguist-language=toml - diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 3fbc60a9..d188de05 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -35,22 +35,20 @@ jobs: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Install Rust - run: rustup update stable --no-self-update - - - uses: taiki-e/cache-cargo-install-action@5c9abe9a3f79d831011df7c47177debbeb320405 # v2.1.2 - with: - tool: cargo-component + run: | + rustup update stable --no-self-update + rustup target add wasm32-wasip2 - uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2.7.8 with: shared-key: ${{ hashFiles('${{ matrix.projects.folder }}/Cargo.lock') }} - - name: Cargo bulid the package + - name: Cargo build the package working-directory: ${{ matrix.projects.folder }} if: ${{ matrix.projects.type != 'component' }} run: cargo build - - name: Cargo component build + - name: Cargo build working-directory: ${{ matrix.projects.folder }} if: ${{ matrix.projects.type == 'component' }} - run: cargo component build + run: cargo build diff --git a/component-model/examples/tutorial/README.md b/component-model/examples/tutorial/README.md index 54c37f2e..314603d1 100644 --- a/component-model/examples/tutorial/README.md +++ b/component-model/examples/tutorial/README.md @@ -42,12 +42,12 @@ To expand the exercise to add more components, add another operator world, expan ## Building and running the example -Use [`cargo-component`](https://github.com/bytecodealliance/cargo-component) and [`wac`](https://github.com/bytecodealliance/wac) to build and compose the calculator component. +Use [`wac`](https://github.com/bytecodealliance/wac) to build and compose the calculator component. ```sh -(cd calculator && cargo component build --release) -(cd adder && cargo component build --release) -(cd command && cargo component build --release) +(cd calculator && cargo build --release) +(cd adder && cargo build --release) +(cd command && cargo build --release) wac plug calculator/target/wasm32-wasip1/release/calculator.wasm --plug adder/target/wasm32-wasip1/release/adder.wasm -o composed.wasm wac plug command/target/wasm32-wasip1/release/command.wasm --plug composed.wasm -o final.wasm ``` diff --git a/component-model/examples/tutorial/adder/.cargo/config.toml b/component-model/examples/tutorial/adder/.cargo/config.toml new file mode 100644 index 00000000..dac24597 --- /dev/null +++ b/component-model/examples/tutorial/adder/.cargo/config.toml @@ -0,0 +1,2 @@ +[build] +target = "wasm32-wasip2" \ No newline at end of file diff --git a/component-model/examples/tutorial/adder/Cargo.lock b/component-model/examples/tutorial/adder/Cargo.lock index bff0abf6..696e0cfc 100644 --- a/component-model/examples/tutorial/adder/Cargo.lock +++ b/component-model/examples/tutorial/adder/Cargo.lock @@ -6,20 +6,418 @@ version = 4 name = "adder" version = "0.1.0" dependencies = [ - "wit-bindgen-rt", + "wit-bindgen", ] +[[package]] +name = "anyhow" +version = "1.0.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0674a1ddeecb70197781e945de4b3b8ffb61fa939a5597bcf48503737663100" + [[package]] name = "bitflags" -version = "2.4.0" +version = "2.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394" + +[[package]] +name = "equivalent" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] -name = "wit-bindgen-rt" -version = "0.37.0" +name = "foldhash" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc801b991c56492f87ab3086e786468f75c285a4d73017ab0ebc2fa1aed5d82c" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "futures" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-executor" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash", +] + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "id-arena" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005" + +[[package]] +name = "indexmap" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2481980430f9f78649238835720ddccc57e52df14ffce1c6f37391d61b563e9" +dependencies = [ + "equivalent", + "hashbrown", + "serde", +] + +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + +[[package]] +name = "log" +version = "0.4.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" + +[[package]] +name = "memchr" +version = "2.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + +[[package]] +name = "semver" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" + +[[package]] +name = "serde" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.143" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d401abef1d108fbd9cbaebc3e46611f4b1021f714a0597a71f41ee463f5f4a5a" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "slab" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" + +[[package]] +name = "syn" +version = "2.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "wasm-encoder" +version = "0.238.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50143b010bdc3adbd16275710f9085cc80d9c12cb869309a51a98ce2ff96558e" +dependencies = [ + "leb128fmt", + "wasmparser", +] + +[[package]] +name = "wasm-metadata" +version = "0.238.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a587a83ac49c2feb922b7ec5d504419320d5da41cf0726f44b2968c78fa2ee2a" +dependencies = [ + "anyhow", + "indexmap", + "wasm-encoder", + "wasmparser", +] + +[[package]] +name = "wasmparser" +version = "0.238.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ad4ca2ecb86b79ea410cd970985665de1d05774b7107b214bc5852b1bcbad7" +dependencies = [ + "bitflags", + "hashbrown", + "indexmap", + "semver", +] + +[[package]] +name = "wit-bindgen" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "052283831dbae3d879dc7f51f3d92703a316ca49f91540417d38591826127814" +dependencies = [ + "bitflags", + "futures", + "once_cell", + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen-core" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1df0102d506f94732b6b517015e7e951293ed51e98f1615f15056e5e1a7a4d67" +dependencies = [ + "anyhow", + "heck", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e128967ed7895a41c5541bd8cd2875472caad6434e6699c1b269302a0d60b39" +dependencies = [ + "anyhow", + "heck", + "indexmap", + "prettyplease", + "syn", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea08d8273179bf25a7aa87fc498380f7a7f56a2df93cd1135d0ca36af1eae962" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.238.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d577b6b6ca3d05cf2a0367e85b1cdfb269155022ba272ae5a0e14c1e1cb59e4d" +dependencies = [ + "anyhow", "bitflags", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.238.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d28fd1ea7579c62574b01b413d80293a0a3f3076d387752cd823a3b0e43e96f0" +dependencies = [ + "anyhow", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", ] diff --git a/component-model/examples/tutorial/adder/Cargo.toml b/component-model/examples/tutorial/adder/Cargo.toml index 7578216a..4df437f7 100644 --- a/component-model/examples/tutorial/adder/Cargo.toml +++ b/component-model/examples/tutorial/adder/Cargo.toml @@ -1,19 +1,11 @@ [package] name = "adder" version = "0.1.0" -edition = "2021" - -[dependencies] -wit-bindgen-rt = { version = "0.37.0", features = ["bitflags"] } +edition = "2024" [lib] crate-type = ["cdylib"] -[package.metadata.component] -package = "docs:adder" - -[package.metadata.component.dependencies] +[dependencies] +wit-bindgen = "0.45.0" -[package.metadata.component.target] -path = "../wit/adder" -world = "adder" diff --git a/component-model/examples/tutorial/adder/src/lib.rs b/component-model/examples/tutorial/adder/src/lib.rs index 20f59a38..fd025cc2 100644 --- a/component-model/examples/tutorial/adder/src/lib.rs +++ b/component-model/examples/tutorial/adder/src/lib.rs @@ -1,21 +1,30 @@ -#[allow(warnings)] -mod bindings; +mod bindings { + //! This module contains generated code for implementing + //! the `adder` world in `wit/world.wit`. + //! + //! The `path` option is actually not required, + //! as by default `wit_bindgen::generate` will look + //! for a top-level `wit` directory and use the files + //! (and interfaces/worlds) there-in. -// The comments that follow the `use` declaration below -// correlate the rust module path segments with their -// `world.wit` counterparts: -use bindings::exports::docs::adder::add::Guest; -// <- items bundled with `export` keyword -// <- package namespace -// <- package -// <- interface name + // The line below will be expanded as Rust code containing + wit_bindgen::generate!({ + path: "wit/adder/world.wit", + }); -struct Component; + // In the lines below we use the generated `export!()` macro re-use and + use super::AdderComponent; + export!(AdderComponent); +} + +/// Struct off of which the implementation will hang +/// +/// The name of this struct is not significant. +#[allow(dead_code)] +struct AdderComponent; -impl Guest for Component { +impl bindings::exports::docs::adder::add::Guest for AdderComponent { fn add(x: u32, y: u32) -> u32 { x + y } } - -bindings::export!(Component with_types_in bindings); diff --git a/component-model/examples/tutorial/adder/wit b/component-model/examples/tutorial/adder/wit new file mode 120000 index 00000000..8c60207d --- /dev/null +++ b/component-model/examples/tutorial/adder/wit @@ -0,0 +1 @@ +../wit \ No newline at end of file diff --git a/component-model/examples/tutorial/calculator/.cargo/config.toml b/component-model/examples/tutorial/calculator/.cargo/config.toml new file mode 100644 index 00000000..dac24597 --- /dev/null +++ b/component-model/examples/tutorial/calculator/.cargo/config.toml @@ -0,0 +1,2 @@ +[build] +target = "wasm32-wasip2" \ No newline at end of file diff --git a/component-model/examples/tutorial/calculator/src/bindings.rs b/component-model/examples/tutorial/calculator/src/bindings.rs index ec0a2ec4..5f5c2dcb 100644 --- a/component-model/examples/tutorial/calculator/src/bindings.rs +++ b/component-model/examples/tutorial/calculator/src/bindings.rs @@ -201,9 +201,7 @@ macro_rules! __export_calculator_impl { #[doc(inline)] pub(crate) use __export_calculator_impl as export; #[cfg(target_arch = "wasm32")] -#[unsafe( - link_section = "component-type:wit-bindgen:0.41.0:docs:calculator@0.1.0:calculator:encoded world" -)] +#[unsafe(link_section = "component-type:wit-bindgen:0.41.0:docs:calculator@0.1.0:calculator:encoded world")] #[doc(hidden)] #[allow(clippy::octal_escapes)] pub static __WIT_BINDGEN_COMPONENT_TYPE: [u8; 308] = *b"\ diff --git a/component-model/examples/tutorial/jco/README.md b/component-model/examples/tutorial/jco/README.md index 37875b10..f774d3c8 100644 --- a/component-model/examples/tutorial/jco/README.md +++ b/component-model/examples/tutorial/jco/README.md @@ -3,7 +3,7 @@ This is a `node` CLI and browser based example implementation of running a component that exports the `calculate` interface from a JavaScript application. It uses [`jco`](https://bytecodealliance.github.io/jco/) to generate JavaScript bindings and shows how the same component can be executed in the browser or locally with Node. For another example of using `jco` with components in multiple environments, see the [`jco` example](https://github.com/bytecodealliance/jco/blob/main/docs/src/example.md). ```sh -# Wasm referenced here was generated by cargo component. +# Wasm referenced here was generated by the Rust WebAssembly toolchain. # See top-level README for commands to generate it. # # We want to *omit* wasm requiring Wasi, diff --git a/component-model/src/language-support.md b/component-model/src/language-support.md index 67e52941..927e78a6 100644 --- a/component-model/src/language-support.md +++ b/component-model/src/language-support.md @@ -35,7 +35,7 @@ without using a higher-level language front-end. - [Building a Component with `componentize-py`](./language-support/python.md#building-a-component-with-componentize-py) - [Running components from Python Applications](./language-support/python.md#running-components-from-python-applications) - [Rust Tooling](./language-support/rust.md) - - [Building a Component with `cargo component`](./language-support/rust.md#building-a-component-with-cargo-component) + - [Building a Component](./language-support/rust.md#building-a-component) - [Running a Component from Rust Applications](./language-support/rust.md#running-a-component-from-rust-appliacations) - [MoonBit Tooling](./language-support/moonbit.md) - [WebAssembly Text Format (WAT)](./language-support/wat.md#wat-webassembly-text-format) diff --git a/component-model/src/language-support/c.md b/component-model/src/language-support/c.md index 446cfb52..c2edd6eb 100644 --- a/component-model/src/language-support/c.md +++ b/component-model/src/language-support/c.md @@ -16,7 +16,7 @@ if you're developing your own `.wit` files. For WIT interfaces that are built in to WASI, the code is part of the WebAssembly runtime that you will be using. -C/C++ currently lacks an integrated toolchain (like Rust's [`cargo-component`][cargo-component]). +C/C++ currently lacks an integrated toolchain. However, `wit-bindgen` can generate source-level bindings for Rust, C, Java ([TeaVM](https://teavm.org/)), and [TinyGo](https://tinygo.org/), with the ability to add more language generators in the future. @@ -27,7 +27,6 @@ with the ability to add more language generators in the future. [clang-tgt-wasm32-wasi]: https://clang.llvm.org/docs/ClangCommandLineReference.html#webassembly [llvm]: https://llvm.org/ [wasi]: https://wasi.dev/ -[cargo-component]: https://crates.io/crates/cargo-component [rust]: https://www.rust-lang.org/learn/get-started [sample-wit]: https://github.com/bytecodealliance/component-docs/blob/main/component-model/examples/tutorial/wit/adder/world.wit [cargo-config]: https://github.com/bytecodealliance/component-docs/blob/main/component-model/examples/example-host/Cargo.toml diff --git a/component-model/src/language-support/rust.md b/component-model/src/language-support/rust.md index 165bd39d..d052c95c 100644 --- a/component-model/src/language-support/rust.md +++ b/component-model/src/language-support/rust.md @@ -1,563 +1,255 @@ # Components in Rust -Rust has first-class support for the component model via the [`cargo-component` tool][cargo-component]. -We will be using the `cargo component` subcommand to create WebAssembly components using Rust as -the component's implementation language. +[Rust][rust] has first-class support for WebAssembly core and WebAssembly components via the +available targets in the toolchain: -> [!NOTE] -> You can find more details about `cargo-component` on [crates.io](https://crates.io/crates/cargo-component). - -## 1. Setup +- `wasm32-unknown-unknown` ([WebAssembly core][wasm-core]) +- `wasm32-wasip1` ([WASI P1][wasip1]) +- `wasm32-wasip2` ([WASI P2][wasip2]) -Install [`cargo-component`][cargo-component-install]: -```sh -cargo install --locked cargo-component -``` -Install [`wasm-tools`](https://github.com/bytecodealliance/wasm-tools#installation): -```sh -cargo install --locked wasm-tools -``` -Install [`wasmtime`](https://github.com/bytecodealliance/wasmtime#installation): -```sh -curl https://wasmtime.dev/install.sh -sSf | bash -``` +[wasm-core]: https://webassembly.github.io/spec/core/ +[wasip1]: https://github.com/WebAssembly/WASI/tree/main/legacy +[wasip2]: https://github.com/WebAssembly/WASI/tree/main/wasip2 -## 2. Scaffolding a Component +> [!NOTE] +> To use the targets above, ensure that they are enabled via the Rust toolchain (e.g. `rustup`). +> +> For example, to add the `wasm32-wasip2` target (`rustup toolchain list` can be used to show all available toolchains): +> ``` +> rustup target add wasm32-wasip2 +> ``` -We will create a component in Rust that implements the `add` function exported -by the [`adder` world][docs-adder] world in the `docs:adder` -[package](https://github.com/WebAssembly/component-model/blob/main/design/mvp/WIT.md#package-names). +With built-in support, Rust code (and the standard library) can compile to WebAssembly with native +tooling: -First, we will create a new WebAssembly component package called `add`: ```sh -cargo component new add --lib && cd add -``` - -## 3. Adding the WIT world - -We now need to change our generated `wit/world.wit` to match `docs:adder`: -```wit -{{#include ../../examples/tutorial/wit/adder/world.wit}} +cargo build --target wasm32-wasip2 ``` -The `package.metadata.component` section of our `Cargo.toml` should be changed -to the following: +> [!WARNING] +> While in the past the use of [`cargo-component`][cargo-component] was recommended, +> the project is in the process of being deprecated as native tooling can be used directly. -```toml -[package.metadata.component] -package = "docs:adder" -``` +[rust]: https://rust-lang.org +[cargo-component]: https://crates.io/crates/cargo-component -## 4. Generating bindings +## 1. Setup -Now that we've updated our `world.wit` and `Cargo.toml`, we can re-generate -bindings with the command below: +Install [`wasm-tools`][wasm-tools] to enable introspection and manipulation of WebAssembly binaries: ```sh -cargo component bindings -``` - -`cargo-component` will generate bindings for our -world and create a `Guest` trait that a component should -implement. - -## 5. Implementing the `Guest` trait - -Implement the `Guest` trait in `src/lib.rs`, using the scaffolded code. Your code should look something like the following: - -```rs -{{#include ../../examples/tutorial/adder/src/lib.rs}} +cargo install --locked wasm-tools ``` -## 6. Building a Component - -Now, let's build our component, being sure to optimize with a release build: +Install [`wasmtime`][wasmtime], a fast and secure runtime for WebAssembly binaries: ```sh -cargo component build --release +curl https://wasmtime.dev/install.sh -sSf | bash ``` -> [!WARNING] -> Building with `--release` removes all debug-related information from the resulting `.wasm` file. -> -> When prototyping or testing locally, you might want to avoid `--release` to -> obtain useful backtraces in case of errors (for example, with -> [`wasmtime::WasmBacktraceDetails::Enable`](https://docs.rs/wasmtime/latest/wasmtime/enum.WasmBacktraceDetails.html#variant.Enable)). -> Note: the resulting `.wasm` file will be considerably larger (likely 4MB+). - -You can use `wasm-tools` to output the WIT package of the component: - -```sh -wasm-tools component wit target/wasm32-wasip1/release/add.wasm -``` +[wasm-tools]: https://github.com/bytecodealliance/wasm-tools#installation +[wasmtime]: https://github.com/bytecodealliance/wasmtime#installation -The command above should produce the output below: +## 2. Creating a WebAssembly project in Rust -```wit -package root:component; +Create a new project in Rust with `cargo new`: -world root { - export docs:adder/add@0.1.0; -} -package docs:adder@0.1.0 { - interface add { - add: func(x: u32, y: u32) -> u32; - } -} +```console +$ cargo new --lib adder + Creating library `adder` package +note: see more `Cargo.toml` keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html ``` +When building Rust WebAssembly projects, it is possible to create *either* a binary crate or +a library crate that can compile to WebAssembly (producing a "command" component or a "reactor" +component respectively), in this case opt for a reactor component. -### Running a Component - -To verify that our component works, lets run it from a Rust application that knows how to run a -component targeting the [`adder` world](#adding-the-wit-world). +> [!NOTE] +> The distinction between a command component and a reactor component isn't important yet, +> but they can be considered similar to the difference between a binary and a shared library. -The application uses [`wasmtime`](https://github.com/bytecodealliance/wasmtime) crates to generate -Rust bindings, bring in WASI worlds, and execute the component. +One thing that we *will* have to add to our Rust project is in `Cargo.toml`, setting the `crate-type`: -```console -$ cd examples/example-host -$ cargo run --release -- 1 2 ../add/target/wasm32-wasip1/release/adder.wasm -1 + 2 = 3 +```toml +[lib] +crate-type = ["cdylib"] ``` -## Importing an interface - -The world file (`wit/world.wit`) we generated doesn't specify any imports. -If your component consumes other components, you can edit the `world.wit` file to import their interfaces. +As we are building a reactor component (the "equivalent" of a library of functions), we must use +the [`cdylib`](https://doc.rust-lang.org/reference/linkage.html#r-link.cdylib) (stands for "c dynamic library") crate type that Rust provides. -> [!NOTE] -> This section is about importing custom WIT interfaces from library components. -> By default, `cargo-component` imports any required [WASI interfaces](https://wasi.dev/interfaces) -> for us without needing to explicitly declare them. +## 3. Adding the `add` Interface +We will create a component in Rust that implements the `add` interface exported by +the [`adder` world][docs-adder] world in the `docs:adder` [WebAssembly Interface types (WIT) package][wit-docs-packages]. -For example, suppose you have created and built an adder component as explained in the [exporting an interface section](#exporting-an-interface-with-cargo-component) and want to use that component in a calculator component. Here is a partial example world for a calculator that imports the add interface: +Create a file called `wit/world.wit` and fill it with the following content: ```wit -// in the 'calculator' project +{{#include ../../examples/tutorial/wit/adder/world.wit}} +``` -// wit/world.wit -package docs:calculator; +The (WIT) types in this file represent the interface of our component must satisfy (the `adder world`). +We say that our component "exports" the `add` interface (which itself contains a single function `add`). -interface calculate { - eval-expression: func(expr: string) -> u32; -} +Working with these types is similar to other Interface Definition Language (IDL) toolchains (e.g. protobuf), +in that we will need some language level bindings that make the interface easy to implement. -world calculator { - export calculate; - import docs:adder/add@0.1.0; -} -``` +[wit-docs-packages]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/WIT.md#package-names -### Referencing the package to import +## 4. Generating Bindings for the `adder` Interface -Because the `docs:adder` package is in a different project, we must first tell `cargo component` how to find it. To do this, add the following to the `Cargo.toml` file: +While the Rust toolchain can compile WebAssembly binaries natively, it cannot (yet) automatically +generate bindings that match our intended (WIT) interface types (`wit/world.wit`). -```toml -[package.metadata.component.target.dependencies] -"docs:adder" = { path = "../adder/wit" } # directory containing the WIT package +We can use [`wit-bindgen`][crates-wit-bindgen] to generate bindings: + +```sh +cargo add wit-bindgen ``` > [!NOTE] -> The path for `docs:adder` is relative to the `wit` _directory_, not to the `world.wit` file. +> The command above should be run from inside the `adder` directory that was created by +> `cargo new` so as to be sure to add `wit-bindgen` to the dependencies of the right project. Alternatively, you can directly add `wit-bindgen` to the dependencies section of the `Cargo.toml`. > -> A WIT package may be spread across multiple files in the same directory; `cargo component` will search them all. +> It is also possible to use `wit-bindgen` as a binary via the [`wit-bindgen-cli`][crates-wit-bindgen-cli] +> crate, but here we will focus on a code-first binding build approach. -### Calling the import from Rust - -Now the declaration of `add` in the adder's WIT file is visible to the `calculator` project. To invoke the imported `add` interface from the `calculate` implementation: +Once you have `wit-bindgen` as a part of your Rust project (i.e. in `Cargo.toml`), we can use it to generate Rust code bindings for our WIT interface. Update your `src/lib.rs` file to look like the following: ```rust -// src/lib.rs -mod bindings; - -use bindings::exports::docs::calculator::calculate::Guest; - -// Bring the imported add function into scope -use bindings::docs::calculator::add::add; - -struct Component; - -impl Guest for Component { - fn eval_expression(expr: String) -> u32 { - // Cleverly parse `expr` into values and operations, and evaluate - // them meticulously. - add(123, 456) - } +mod bindings { + //! This module contains generated code for implementing + //! the `adder` world in `wit/world.wit`. + //! + //! The `path` option is actually not required, + //! as by default `wit_bindgen::generate` will look + //! for a top-level `wit` directory and use the files + //! (and interfaces/worlds) there-in. + wit_bindgen::generate!({ + path: "wit/world.wit", + }); } ``` -### Fulfilling the import +Here we create a module called `bindings` that contains the code output by the [`wit_bindgen::generate` macro][rustdoc-wit-bindgen-generate]. +Various `struct`s, `interface`s, `enum`s and more might be generated by `wit_bindgen`, so it's often desirable to sequester those new +types to a module that can be referred to later. -When you build this using `cargo component build`, the `add` interface remains imported. The calculator has taken a dependency on the `add` _interface_, but has not linked the `adder` implementation of that interface - this is not like referencing the `adder` crate. (Indeed, `calculator` could import the `add` interface even if there was no Rust project implementing the WIT file.) You can see this by running [`wasm-tools component wit`](https://github.com/bytecodealliance/wasm-tools/tree/main/crates/wit-component) to view the calculator's world: +At present, the code won't *do* much, but that's because we haven't added our implementation yet. -``` -# Do a release build to prune unused imports (e.g. WASI) -$ cargo component build --release +[crates-wit-bindgen]: https://crates.io/crates/wit-bindgen +[crates-wit-bindgen-cli]: https://crates.io/crates/wit-bindgen-cli +[rustdoc-wit-bindgen-generate]: https://docs.rs/wit-bindgen/latest/wit_bindgen/macro.generate.html -$ wasm-tools component wit ./target/wasm32-wasip1/release/calculator.wasm -package root:component; +## 5. Implementing the `adder` world via the generated `Guest` Trait -world root { - import docs:adder/add@0.1.0; +We can fill in functionality of the component by implementing `bindings::Guest` trait in `src/lib.rs`. +Your code should look something like the following: - export docs:calculator/calculate@0.1.0; -} +```rs +{{#include ../../examples/tutorial/adder/src/lib.rs}} ``` -As the import is unfulfilled, the `calculator.wasm` component could not run by itself in its current form. To fulfill the `add` import, so that -only `calculate` is exported, you would need to [compose the `calculator.wasm` with some `adder.wasm` into a single, self-contained component](../composing-and-distributing/composing.md). - -## Creating a command component with `cargo component` - -A _command_ is a component with a specific export that allows it to be executed directly by `wasmtime` (or other `wasi:cli` hosts). In Rust terms, it's the equivalent of an application (`bin`) package with a `main` function, instead of a library crate (`lib`) package. - -To create a command with cargo component, run: +There are a few points of note in the code listing above: -```sh -cargo component new -``` - -Unlike library components, this does _not_ have the `--lib` flag. You will see that the created project is different too: +1. The `AdderComponent` struct is introduced, but is only useful as an implementer of the `Guest` trait. +2. The `bindings::exports::docs::adder::add::Guest` trait mirrors the `docs:adder/add` interface that is exported. +3. Given (1) and (2), `AdderComponent` implements (in the WIT sense) the `adder` world, via the generated bindings. +4. The [`export!()` macro][export-macro] is generated by `wit_bindgen::generate!` macro, and does important setup. + - `export!` is easiest used from inside the `bindings` module, *but* we need to refer to the `super::AdderComponent` struct -- It doesn't contain a `.wit` file. `cargo component build` will automatically export the `wasi:cli/run` interface for Rust `bin` packages, and hook it up to `main`. -- Because there's no `.wit` file, `Cargo.toml` doesn't contain a `package.metadata.component.target` section. -- The Rust file is called `main.rs` instead of `lib.rs`, and contains a `main` function instead of an interface implementation. +> [!NOTE] +> To dive into the code generated by the `wit_bindgen::generate!` macro, you can use the [`cargo-expand` crate][crates-cargo-expand] -You can write Rust in this project, just as you normally would, including importing your own or third-party crates. +[export-macro]: https://docs.rs/wit-bindgen/latest/wit_bindgen/macro.generate.html#exports-the-export-macro +[crates-cargo-expand]: https://crates.io/crates/cargo-expand -> All the crates that make up your project are linked together at build time, and compiled to a _single_ Wasm component. In this case, all the linking is happening at the Rust level: no WITs or component composition is involved. Only if you import Wasm interfaces do WIT and composition come into play. +## 6. Building a Component -To run your command component: +Now, let's build our component, using the native Rust toolchain to build a WASI P2 component. ```sh -cargo component build -wasmtime run ./target/wasm32-wasip1/debug/.wasm +cargo build --target=wasm32-wasip2 ``` -> **WARNING:** If your program prints to standard out or error, you may not see the printed output! Some versions of `wasmtime` have a bug where they don't flush output streams before exiting. To work around this, add a `std::thread::sleep()` with a 10 millisecond delay before exiting `main`. - -### Importing an interface into a command component +This performs a debug build, which produces a WebAssembly component to `target/wasm32-wasip2/debug/adder.wasm`: -As mentioned above, `cargo component build` doesn't generate a WIT file for a command component. If you want to import a Wasm interface, though, you'll need to create a WIT file and a world, plus reference the packages containing your imports: - -1. Add a `wit/world.wit` to your project, and write a WIT world that imports the interface(s) you want to use. For example: - - ```wit - package docs:app; - - world app { - import docs:calculator/calculate@0.1.0; - } - ``` - - > `cargo component` sometimes fails to find packages if versions are not set explicitly. For example, if the calculator WIT declares `package docs:calculator` rather than `docs:calculator@0.1.0`, then you may get an error even though `cargo component build` automatically versions the binary export. - -2. Edit `Cargo.toml` to tell `cargo component` about the new WIT file: - - ```toml - [package.metadata.component.target] - path = "wit" - ``` - - (This entry is created automatically for library components but not for command components.) - -3. Edit `Cargo.toml` to tell `cargo component` where to find external package WITs: - - ```toml - [package.metadata.component.target.dependencies] - "docs:calculator" = { path = "../calculator/wit" } - "docs:adder" = { path = "../adder/wit" } - ``` - - > If the external package refers to other packages, you need to provide the paths to them as well. - -4. Use the imported interface in your Rust code: - - ```rust - use bindings::docs::calculator::calculate::eval_expression; - - fn main() { - let result = eval_expression("1 + 1"); - println!("1 + 1 = {result}"); - } - ``` +```console +du -hs target/wasm32-wasip2/debug/adder.wasm +3.3M target/wasm32-wasip2/debug/adder.wasm +``` -5. [Compose the command component with the `.wasm` components that implement the imports.](../composing-and-distributing/composing.md) +3 megabytes is *large* for a WebAssembly component for a compiled language like Rust. Let's compile in release mode, +performing more optimizations: -6. Run the composed component: +```sh +cargo build --target=wasm32-wasip2 --release +``` - ```console - $ wasmtime run ./my-composed-command.wasm - 1 + 1 = 579 # might need to go back and do some work on the calculator implementation - ``` +After compiling in release mode, we get a much smaller binary: -## Using user-defined types +```console +$ du -hs target/wasm32-wasip2/release/adder.wasm +16K target/wasm32-wasip2/release/adder.wasm +``` -[User-defined types](../design/wit.md#user-defined-types) map to Rust types as follows. +Note that you can use many of the optimization options normally available with the Rust toolchain to control binary output. -| WIT type | Rust binding | -|------------|--------------| -| `record` | `struct` with public fields corresponding to the record fields | -| `variant` | `enum` with cases corresponding to the variant cases | -| `enum` | `enum` with cases corresponding to the enum cases, with no data attached | -| `resource` | [See below](#using-resources) | -| `flags` | Opaque type supporting bit flag operations, with constants for flag values | +> [!WARNING] +> Building with `--release` removes all debug-related information from the resulting `.wasm` file. +> +> When prototyping or testing locally, you might want to avoid `--release` to +> obtain useful backtraces in case of errors (for example, with +> [`wasmtime::WasmBacktraceDetails::Enable`](https://docs.rs/wasmtime/latest/wasmtime/enum.WasmBacktraceDetails.html#variant.Enable)). +> Note: the resulting `.wasm` file will be considerably larger (likely 4MB+). -For example, consider the following WIT: +## 7. Inspecting the built component -```wit -interface types { - enum operation { - add, - sub, - mul, - div - } - - record expression { - left: u32, - operation: operation, - right: u32 - } - - eval: func(expr: expression) -> u32; -} -``` +Now that we have a WIT binary, we can introspect it using WebAssembly component tooling. -When exported from a component, this could be implemented as: +For example, we can `wasm-tools` to output the WIT package of the component, because WebAssembly +components are self-documenting, and contain this information: -```rust -impl Guest for Implementation { - fn eval(expr: Expression) -> u32 { - // Record fields become public fields on a struct - let (l, r) = (expr.left, expr.right); - match expr.operation { - // Enum becomes an enum with only unit cases - Operation::Add => l + r, - Operation::Sub => l - r, - Operation::Mul => l * r, - Operation::Div => l / r, - } - } -} +```sh +wasm-tools component wit target/wasm32-wasip2/release/adder.wasm ``` -## Using resources - -[Resources](../design/wit.md#resources) are handles to entities that live outside the component, for example in a host, or in a different component. - -### Example - -In this section, our example resource will be a [Reverse Polish Notation (RPN)](https://en.wikipedia.org/wiki/Reverse_Polish_notation) calculator. (Engineers of a certain vintage will remember this from handheld calculators of the 1970s.) A RPN calculator is a stateful entity: a consumer pushes operands and operations onto a stack maintained within the calculator, then evaluates the stack to produce a value. The resource in WIT looks like this: +The command above should produce the output below: ```wit -package docs:rpn@0.1.0; - -interface types { - enum operation { - add, - sub, - mul, - div - } - - resource engine { - constructor(); - push-operand: func(operand: u32); - push-operation: func(operation: operation); - execute: func() -> u32; - } -} +package root:component; -world calculator { - export types; +world root { + export docs:adder/add@0.1.0; +} +package docs:adder@0.1.0 { + interface add { + add: func(x: u32, y: u32) -> u32; + } } ``` -### Implementing and exporting a resource in a component - -To implement the calculator using `cargo component`: - -1. Create a library component as shown in previous sections, with the WIT given above. - -2. Define a Rust `struct` to represent the calculator state: +## 8. Running the `adder` Component - ```rust - use std::cell::RefCell; - - struct CalcEngine { - stack: RefCell>, - } - ``` - - > Why is the stack wrapped in a `RefCell`? As we will see, the generated Rust trait for the calculator engine has _immutable_ references to `self`. But our implementation of that trait will need to mutate the stack. So we need a type that allows for interior mutability, such as `RefCell` or `Arc>`. - -3. The generated bindings (`bindings.rs`) for an exported resource include a trait named `GuestX`, where `X` is the resource name. (You may need to run `cargo component build` to regenerate the bindings after updating the WIT.) For the calculator `engine` resource, the trait is `GuestEngine`. Implement this trait on the `struct` from step 2: - - ```rust - use bindings::exports::docs::rpn::types::{GuestEngine, Operation}; - - impl GuestEngine for CalcEngine { - fn new() -> Self { - CalcEngine { - stack: RefCell::new(vec![]) - } - } - - fn push_operand(&self, operand: u32) { - self.stack.borrow_mut().push(operand); - } - - fn push_operation(&self, operation: Operation) { - let mut stack = self.stack.borrow_mut(); - let right = stack.pop().unwrap(); // TODO: error handling! - let left = stack.pop().unwrap(); - let result = match operation { - Operation::Add => left + right, - Operation::Sub => left - right, - Operation::Mul => left * right, - Operation::Div => left / right, - }; - stack.push(result); - } - - fn execute(&self) -> u32 { - self.stack.borrow_mut().pop().unwrap() // TODO: error handling! - } - } - ``` - -4. We now have a working calculator type which implements the `engine` contract, but we must still connect that type to the `engine` resource type. This is done by implementing the generated `Guest` trait. For this WIT, the `Guest` trait contains nothing except an associated type. You can use an empty `struct` to implement the `Guest` trait on. Set the associated type for the resource - in our case, `Engine` - to the type which implements the resource trait - in our case, the `CalcEngine` `struct` which implements `GuestEngine`. Then use the `export!` macro to export the mapping: - - ```rust - struct Implementation; - impl Guest for Implementation { - type Engine = CalcEngine; - } - - bindings::export!(Implementation with_types_in bindings); - ``` - -This completes the implementation of the calculator `engine` resource. Run `cargo component build` to create a component `.wasm` file. - -### Importing and consuming a resource in a component - -To use the calculator engine in another component, that component must import the resource. - -1. Create a command component as shown in previous sections. - -2. Add a `wit/world.wit` to your project, and write a WIT world that imports the RPN calculator types: - - ```wit - package docs:rpn-cmd; - - world app { - import docs:rpn/types@0.1.0; - } - ``` - -3. Edit `Cargo.toml` to tell `cargo component` about the new WIT file and the external RPN package file: - - ```toml - [package.metadata.component] - package = "docs:rpn-cmd" - - [package.metadata.component.target] - path = "wit" - - [package.metadata.component.target.dependencies] - "docs:rpn" = { path = "../wit" } # or wherever your resource WIT is - ``` - -4. The resource now appears in the generated bindings as a `struct`, with appropriate associated functions. Use these to construct a test app: - - ```rust - #[allow(warnings)] - mod bindings; - use bindings::docs::rpn::types::{Engine, Operation}; - - fn main() { - let calc = Engine::new(); - calc.push_operand(1); - calc.push_operand(2); - calc.push_operation(Operation::Add); - let sum = calc.execute(); - println!("{sum}"); - } - ``` - -You can now build the command component and [compose it with the `.wasm` component that implements the resource.](../composing-and-distributing/composing.md). You can then run the composed command with `wasmtime run`. - -### Implementing and exporting a resource implementation in a host - -If you are hosting a Wasm runtime, you can export a resource from your host for guests to consume. Hosting a runtime is outside the scope of this book, so we will give only a broad outline here. This is specific to the Wasmtime runtime; other runtimes may express things differently. - -1. Use `wasmtime::component::bindgen!` to specify the WIT you are a host for: - - ```rust - wasmtime::component::bindgen!({ - path: "../wit" - }); - ``` - -2. Tell `bindgen!` how you will represent the resource in the host via the `with` field. This can be any Rust type. For example, the RPN engine could be represented by a `CalcEngine` struct: - - ```rust - wasmtime::component::bindgen!({ - path: "../wit", - with: { - "docs:rpn/types/engine": CalcEngine, - } - }); - ``` - - > If you don't specify the host representation for a resource, it defaults to an empty enum. This is rarely useful as resources are usually stateful. - -3. If the representation type isn't a built-in type, define it: - - ```rust - struct CalcEngine { /* ... */ } - ``` - -4. As a host, you will already be implementing a `Host` trait. You will now need to implement a `HostX` trait (where `X` is the resource name) _on the same type_ as the `Host` trait: - - ```rust - impl docs::rpn::types::HostEngine for MyHost { - fn new(&mut self) -> wasmtime::component::Resource { /* ... */ } - fn push_operand(&mut self, self_: wasmtime::component::Resource) { /* ... */ } - // etc. - } - ``` - - > **Important:** You implement this on the 'overall' host type, *not* on the resource representation! Therefore, the `self` reference in these functions is to the 'overall' host type. For instance methods of the resource, the instance is identified by a second parameter (`self_`), of type `wasmtime::component::Resource`. +To verify that our component works, lets run it from a Rust application that knows how to run a +component targeting the [`adder` world](#adding-the-wit-world). -5. Add a `wasmtime::component::ResourceTable` to the host: +The application uses [`wasmtime`][crates-wasmtime] to generate Rust "host"/"embedder" bindings, +bring in WASI worlds, and execute the component. - ```rust - struct MyHost { - calcs: wasmtime::component::ResourceTable, - } - ``` +With the [`component-docs` repository][repo-component-docs] cloned locally, run the following: -6. In your resource method implementations, use this table to store and access instances of the resource representation: +```console +$ cd examples/example-host +$ cargo run --release -- 1 2 ../add/target/wasm32-wasip1/release/adder.wasm +1 + 2 = 3 +``` - ```rust - impl docs::rpn::types::HostEngine for MyHost { - fn new(&mut self) -> wasmtime::component::Resource { - self.calcs.push(CalcEngine::new()).unwrap() // TODO: error handling - } - fn push_operand(&mut self, self_: wasmtime::component::Resource) { - let calc_engine = self.calcs.get(&self_).unwrap(); - // calc_engine is a CalcEngine - call its functions - } - // etc. - } - ``` +With this, we have successfully built and run a basic WebAssembly component with Rust 🎉 -[cargo-component]: https://github.com/bytecodealliance/cargo-component -[cargo-component-install]: https://github.com/bytecodealliance/cargo-component#install +[crates-wasmtime]: https://crates.io/crates/wasmtime +[repo-component-docs]: https://github.com/bytecodealliance/component-docs [docs-adder]: https://github.com/bytecodealliance/component-docs/tree/main/component-model/examples/tutorial/wit/adder/world.wit [!NOTE]: # [!WARNING]: # diff --git a/component-model/src/tutorial.md b/component-model/src/tutorial.md index 60b4244a..6936f8dc 100644 --- a/component-model/src/tutorial.md +++ b/component-model/src/tutorial.md @@ -104,51 +104,74 @@ The WebAssembly host expects it to export the [`wasi:cli/run` interface](https://github.com/WebAssembly/wasi-cli/blob/main/wit/run.wit), which is the equivalent of the [`main` function][wiki-entrypoint] to WASI. -[`cargo-component`][cargo-component] will automatically resolve a Rust `bin` package -with a `main` function to a component with `wasi:cli/run` exported. Scaffold a new Wasm application -with a `command` component: +To build a command component, [`cargo`][cargo] should be configured to build a `--lib` crate which +adheres to the `wasi:cli` interface. Crucially the component must export `wasi:cli/run` + +To scaffold a new runnable Wasm application: ```console -cargo component new command --command +cargo new --lib example +cd example ``` -This component will implement the [`app`](https://github.com/bytecodealliance/component-docs/tree/main/component-model/examples/tutorial/wit/calculator.wit) world, which +This component will implement the [`app`](https://github.com/bytecodealliance/component-docs/tree/main/component-model/examples/tutorial/wit/calculator/world.wit) world, which imports the `calculate` interface. -In `Cargo.toml`, point `cargo-component` to the WIT file and specify that it should pull in bindings -for the `app` world from the path to `calculator.wit`: +In `Cargo.toml`, configure this crate as a `cdylib`: ```toml -[package.metadata.component.target] -path = "../wit/calculator/world.wit" -world = "app" +[lib] +crate-type = "cdylib" +``` +In addition, we'll need to modify the WIT interface that *imports* the calculation function and makes use of it to be runnable: + +```diff +world app { + import calculate; ++ export wasi:cli/run@0.2.7; +} ``` + Since the calculator world imports the `add` interface, the command component needs to pull in the `adder` WIT as a dependency, as well. +To do this, we can [`wkg`][wkg] from [`wasm-pkg-tools`][wasm-pkg-tools]. We must first configure wkg to find the `docs:adder` repository: + ```toml -[package.metadata.component.target.dependencies] -"docs:adder" = { path = "../wit/adder" } +# $XDG_CONFIG_HOME/wasm-pkg/config.toml +default_registry = "ghcr.io" + +[namespace_registries] +# Tell wkg that the component-book WITs can be found at ghcr.io/bytecodealliance/docs +docs = { registry = "docs", metadata = { preferredProtocol = "oci", "oci" = {registry = "ghcr.io", namespacePrefix = "bytecodealliance/" } } } ``` -Now, implement a command line application that: + +> [!NOTE] +> We have published the [`docs:adder` WIT package](https://github.com/orgs/bytecodealliance/packages/container/package/docs%2Fadder) to GHCR ahead of time, so it is easy to find/access via the configuration above. +> +> To make your own custom WITs available, please use the [`wkg publish`](./composing-and-distributing/distributing.md#distributing-wit-and-components-by-package-name-with-wkg-publish) command. + +With the project scaffolded, now we must actually *implement* a command line application that: 1. Takes in three arguments: two operands and the name of an operator ("1 2 add") 2. Parses the operator name and ensures it is supported in the `op` enum 3. Calls the `calculate` interface's `eval_expression`, passing in the arguments. -For reference, see a completed [example](https://github.com/bytecodealliance/component-docs/tree/main/component-model/examples/tutorial/command/). +For reference, see the [completed code listing](https://github.com/bytecodealliance/component-docs/tree/main/component-model/examples/tutorial/command/). +[wkg]: https://github.com/bytecodealliance/wasm-pkg-tools/tree/main/crates/wkg +[wasm-pkg-tools]: https://github.com/bytecodealliance/wasm-pkg-tools/tree/main [wiki-entrypoint]: https://en.wikipedia.org/wiki/Entry_point -[cargo-component]: https://crates.io/crates/cargo-component +[cargo]: https://doc.rust-lang.org/cargo ## Composing the calculator Now, we are ready to bring our components together into one runnable calculator component, using -`wac`. +`wac`. We will: 1. Compose the calculator component with the add component to satisfy the calculator component's `adder` import -2. Compose that resolved calculator component once more with the command component to satisfy the command component's `calculate` import. +2. Compose that resolved calculator component once more with the command component to satisfy the command component's `calculate` import. The result is a fully-formed command component that has all its imports satisfied and has a single export (the `wasi:cli/run` interface), which can be executed by [`wasmtime`][wasmtime]. @@ -182,3 +205,5 @@ expanded enum. Another extension of this tutorial could be to remove the `op` enum and instead modify `eval-expression` to take in a string that can then be parsed to determine which operator component to call. Maybe this parser is a component of its own?! + +[!NOTE]: # From d3290a60e58e3ff7ce7ad569cd628c005bbeb897 Mon Sep 17 00:00:00 2001 From: Victor Adossi Date: Mon, 6 Oct 2025 22:01:50 +0900 Subject: [PATCH 02/35] refactor: add new sections to contain reworked rust sections --- component-model/book.toml | 4 + component-model/src/SUMMARY.md | 25 +++-- .../src/building-a-simple-component.md | 26 ++++++ .../composing-and-distributing/composing.md | 2 + .../src/creating-runnable-components.md | 21 +++++ .../src/importing-and-reusing-components.md | 26 ++++++ component-model/src/introduction.md | 14 +-- component-model/src/language-support.md | 38 ++++---- .../{ => building-a-simple-component}/c.md | 12 +-- .../csharp.md | 12 +-- .../{ => building-a-simple-component}/go.md | 8 +- .../javascript.md | 38 ++++---- .../moonbit.md | 12 +-- .../other-languages.md | 0 .../python.md | 10 +- .../{ => building-a-simple-component}/rust.md | 4 +- .../{ => building-a-simple-component}/wat.md | 6 +- .../creating-runnable-components/rust.md | 2 + .../importing-and-reusing-components/rust.md | 92 +++++++++++++++++++ .../using-wit-resources/rust.md | 1 + component-model/src/using-wit-resources.md | 51 ++++++++++ 21 files changed, 318 insertions(+), 86 deletions(-) create mode 100644 component-model/src/building-a-simple-component.md create mode 100644 component-model/src/creating-runnable-components.md create mode 100644 component-model/src/importing-and-reusing-components.md rename component-model/src/language-support/{ => building-a-simple-component}/c.md (97%) rename component-model/src/language-support/{ => building-a-simple-component}/csharp.md (95%) rename component-model/src/language-support/{ => building-a-simple-component}/go.md (98%) rename component-model/src/language-support/{ => building-a-simple-component}/javascript.md (95%) rename component-model/src/language-support/{ => building-a-simple-component}/moonbit.md (95%) rename component-model/src/language-support/{ => building-a-simple-component}/other-languages.md (100%) rename component-model/src/language-support/{ => building-a-simple-component}/python.md (94%) rename component-model/src/language-support/{ => building-a-simple-component}/rust.md (98%) rename component-model/src/language-support/{ => building-a-simple-component}/wat.md (95%) create mode 100644 component-model/src/language-support/creating-runnable-components/rust.md create mode 100644 component-model/src/language-support/importing-and-reusing-components/rust.md create mode 100644 component-model/src/language-support/using-wit-resources/rust.md create mode 100644 component-model/src/using-wit-resources.md diff --git a/component-model/book.toml b/component-model/book.toml index d72758b8..52c90595 100644 --- a/component-model/book.toml +++ b/component-model/book.toml @@ -10,6 +10,10 @@ git-repository-url = "https://github.com/bytecodealliance/component-docs/tree/ma edit-url-template = "https://github.com/bytecodealliance/component-docs/tree/main/component-model/{path}" additional-css = ["theme/head.hbs"] +[output.html.fold] +enable = true +level = 1 + [output.html.redirect] "/creating-and-consuming/composing.html" = "/composing-and-distributing/composing.html" "/creating-and-consuming/distributing.html" = "/composing-and-distributing/distributing.html" diff --git a/component-model/src/SUMMARY.md b/component-model/src/SUMMARY.md index 4dc11ece..e7951b33 100644 --- a/component-model/src/SUMMARY.md +++ b/component-model/src/SUMMARY.md @@ -17,15 +17,22 @@ # Using WebAssembly Components - [Creating Components](./language-support.md) - - [C/C++](./language-support/c.md) - - [C#](./language-support/csharp.md) - - [Go](./language-support/go.md) - - [JavaScript](./language-support/javascript.md) - - [Python](./language-support/python.md) - - [Rust](./language-support/rust.md) - - [MoonBit](./language-support/moonbit.md) - - [WebAssembly Text Format (WAT)](./language-support/wat.md) - - [Other Languages](./language-support/other-languages.md) + - [Building a simple component](./building-a-simple-component.md) + - [C/C++](./language-support/building-a-simple-component/c.md) + - [C#](./language-support/building-a-simple-component/csharp.md) + - [Go](./language-support/building-a-simple-component/go.md) + - [JavaScript](./language-support/building-a-simple-component/javascript.md) + - [Python](./language-support/building-a-simple-component/python.md) + - [Rust](./language-support/building-a-simple-component/rust.md) + - [MoonBit](./language-support/building-a-simple-component/moonbit.md) + - [WebAssembly Text Format (WAT)](./language-support/building-a-simple-component/wat.md) + - [Other Languages](./language-support/building-a-simple-component/other-languages.md) + - [Importing and reusing components](./importing-and-reusing-components.md) + - [Rust](./language-support/importing-and-reusing-components/rust.md) + - [Creating runnable components](./creating-runnable-components.md) + - [Rust](./language-support/creating-runnable-components/rust.md) + - [Using WIT resources](./using-wit-resources.md) + - [Rust](./language-support/using-wit-resources/rust.md) - [Running Components](./running-components.md) - [Wasmtime](./running-components/wasmtime.md) - [jco](./running-components/jco.md) diff --git a/component-model/src/building-a-simple-component.md b/component-model/src/building-a-simple-component.md new file mode 100644 index 00000000..b1b75197 --- /dev/null +++ b/component-model/src/building-a-simple-component.md @@ -0,0 +1,26 @@ +# Building a simple component + +This section contains guides on how to build a simple WebAssembly component that implements an adder, +with the following [WIT][docs-wit] interface: + +```wit +{{#include ../examples/tutorial/wit/adder/world.wit}} +``` + +## Languages + +This guide is implemented for various languages: + +| Language | +|----------------------------------------------------------------------------------------| +| [C/C++](./language-support/building-a-simple-component/c.md) | +| [C#](./language-support/building-a-simple-component/csharp.md) | +| [Go](./language-support/building-a-simple-component/go.md) | +| [JavaScript](./language-support/building-a-simple-component/javascript.md) | +| [Python](./language-support/building-a-simple-component/python.md) | +| [Rust](./language-support/building-a-simple-component/rust.md) | +| [MoonBit](./language-support/building-a-simple-component/moonbit.md) | +| [WebAssembly Text Format (WAT)](./language-support/building-a-simple-component/wat.md) | +| [Other Languages](./language-support/building-a-simple-component/other-languages.md) | + +[docs-wit]: ./design/wit.md diff --git a/component-model/src/composing-and-distributing/composing.md b/component-model/src/composing-and-distributing/composing.md index dad522e6..e7db1005 100644 --- a/component-model/src/composing-and-distributing/composing.md +++ b/component-model/src/composing-and-distributing/composing.md @@ -155,3 +155,5 @@ You can compose components visually using the builder app at [wasmbuilder.app](h 5. When you have connected all the imports and exports that you want, click the Download Component button to download the composed component as a `.wasm` file. + +[!NOTE]: # diff --git a/component-model/src/creating-runnable-components.md b/component-model/src/creating-runnable-components.md new file mode 100644 index 00000000..351437d8 --- /dev/null +++ b/component-model/src/creating-runnable-components.md @@ -0,0 +1,21 @@ +# Creating Runnable Components + +This section contains language-specific guides on how to create runnable components. + +At a high level there are at least two ways to create components that are more like binaries than libraries +(i.e. that are easy to run from a tool like `wasmtime`): + +- Creating a "command" component +- Exporting the `wasi:cli/run` interface + +This section explores how to do the above in relevant languages. + +## Languages + +This guide is implemented for various languages: + +| Language | +|-----------------------------------------------------------------| +| [Rust](./language-support/creating-runnable-components/rust.md) | + +[docs-wit]: ./design/wit.md diff --git a/component-model/src/importing-and-reusing-components.md b/component-model/src/importing-and-reusing-components.md new file mode 100644 index 00000000..794110be --- /dev/null +++ b/component-model/src/importing-and-reusing-components.md @@ -0,0 +1,26 @@ +# Importing and reusing components + +This section contains language-specific guides on how to reuse existing WebAssembly +components, in particular using an `adder` component to complete a `calculator` component. + +The `adder` component has the following [WIT][docs-wit] interface: + +```wit +{{#include ../examples/tutorial/wit/adder/world.wit}} +``` + +The `calculator` component has the following interface: + +```wit +{{#include ../examples/tutorial/wit/calculator/world.wit}} +``` + +## Languages + +This guide is implemented for various languages: + +| Language | +|---------------------------------------------------------------------| +| [Rust](./language-support/importing-and-reusing-components/rust.md) | + +[docs-wit]: ./design/wit.md diff --git a/component-model/src/introduction.md b/component-model/src/introduction.md index 992e4019..3dad3d73 100644 --- a/component-model/src/introduction.md +++ b/component-model/src/introduction.md @@ -26,13 +26,13 @@ This documentation is aimed at _users_ of the component model: developers of lib [Interfaces]: ./design/interfaces.md [Worlds]: ./design/worlds.md -[C/C++]: ./language-support/c.md -[C#]: ./language-support/csharp.md -[Go]: ./language-support/go.md -[JavaScript]: ./language-support/javascript.md -[Python]: ./language-support/python.md -[Rust]: ./language-support/rust.md -[MoonBit]: ./language-support/moonbit.md +[C/C++]: ./language-support/building-a-simple-component/c.md +[C#]: ./language-support/building-a-simple-component/csharp.md +[Go]: ./language-support/building-a-simple-component/go.md +[JavaScript]: ./language-support/building-a-simple-component/javascript.md +[Python]: ./language-support/building-a-simple-component/python.md +[Rust]: ./language-support/building-a-simple-component/rust.md +[MoonBit]: ./language-support/building-a-simple-component/moonbit.md [Composing]: ./composing-and-distributing/composing.md [Running]: ./running-components.md diff --git a/component-model/src/language-support.md b/component-model/src/language-support.md index 927e78a6..8b2573f5 100644 --- a/component-model/src/language-support.md +++ b/component-model/src/language-support.md @@ -23,22 +23,22 @@ The last section, on WebAssembly Text Format (WAT), details how to write WebAssembly components by hand, without using a higher-level language front-end. - - [C/C++ Tooling](./language-support/c.md) - - [Building a Component with `wit-bindgen` and `wasm-tools`](./language-support/c.md#building-a-component-with-wit-bindgen-and-wasm-tools) - - [Running a Component from C/C++ Applications](./language-support/c.md#running-a-component-from-cc-applications) - - [C# Tooling](./language-support/csharp.md) - - [Go Tooling](./language-support/go.md) - - [JavaScript Tooling](./language-support/javascript.md) - - [Building a Component with `jco`](./language-support/javascript.md#building-a-component-with-jco) - - [Running a Component from JavaScript Applications](./language-support/javascript.md#running-a-component-from-javascript-applications) - - [Python Tooling](./language-support/python.md) - - [Building a Component with `componentize-py`](./language-support/python.md#building-a-component-with-componentize-py) - - [Running components from Python Applications](./language-support/python.md#running-components-from-python-applications) - - [Rust Tooling](./language-support/rust.md) - - [Building a Component](./language-support/rust.md#building-a-component) - - [Running a Component from Rust Applications](./language-support/rust.md#running-a-component-from-rust-appliacations) - - [MoonBit Tooling](./language-support/moonbit.md) - - [WebAssembly Text Format (WAT)](./language-support/wat.md#wat-webassembly-text-format) - - [Building a Component from WAT with `wasm-tools`](./language-support/wat.md#building-a-component-with-wasm-tools) - - [Running a Component with Wasmtime](./language-support/wat.md#running-a-component-with-wasmtime) - - [Other Languages with Component Model Support](./language-support/other-languages.md) + - [C/C++ Tooling](./language-support/building-a-simple-component/c.md) + - [Building a Component with `wit-bindgen` and `wasm-tools`](./language-support/building-a-simple-component/c.md#building-a-component-with-wit-bindgen-and-wasm-tools) + - [Running a Component from C/C++ Applications](./language-support/building-a-simple-component/c.md#running-a-component-from-cc-applications) + - [C# Tooling](./language-support/building-a-simple-component/csharp.md) + - [Go Tooling](./language-support/building-a-simple-component/go.md) + - [JavaScript Tooling](./language-support/building-a-simple-component/javascript.md) + - [Building a Component with `jco`](./language-support/building-a-simple-component/javascript.md#building-a-component-with-jco) + - [Running a Component from JavaScript Applications](./language-support/building-a-simple-component/javascript.md#running-a-component-from-javascript-applications) + - [Python Tooling](./language-support/building-a-simple-component/python.md) + - [Building a Component with `componentize-py`](./language-support/building-a-simple-component/python.md#building-a-component-with-componentize-py) + - [Running components from Python Applications](./language-support/building-a-simple-component/python.md#running-components-from-python-applications) + - [Rust Tooling](./language-support/building-a-simple-component/rust.md) + - [Building a Component](./language-support/building-a-simple-component/rust.md#building-a-component) + - [Running a Component from Rust Applications](./language-support/building-a-simple-component/rust.md#running-a-component-from-rust-appliacations) + - [MoonBit Tooling](./language-support/building-a-simple-component/moonbit.md) + - [WebAssembly Text Format (WAT)](./language-support/building-a-simple-component/wat.md#wat-webassembly-text-format) + - [Building a Component from WAT with `wasm-tools`](./language-support/building-a-simple-component/wat.md#building-a-component-with-wasm-tools) + - [Running a Component with Wasmtime](./language-support/building-a-simple-component/wat.md#running-a-component-with-wasmtime) + - [Other Languages with Component Model Support](./language-support/building-a-simple-component/other-languages.md) diff --git a/component-model/src/language-support/c.md b/component-model/src/language-support/building-a-simple-component/c.md similarity index 97% rename from component-model/src/language-support/c.md rename to component-model/src/language-support/building-a-simple-component/c.md index c2edd6eb..3dbc38db 100644 --- a/component-model/src/language-support/c.md +++ b/component-model/src/language-support/building-a-simple-component/c.md @@ -40,7 +40,7 @@ First, install the following dependencies: as well as converting between preview1 modules and preview2 components in the optional manual workflow. 3. The [`WASI SDK`](https://github.com/webassembly/wasi-sdk) - * WASI SDK is a WASI enabled C/C++ toolchain which includes a version of the C standard + * WASI SDK is a WASI enabled C/C++ toolchain which includes a version of the C standard library (`libc`) implemented with WASI interfaces, among other artifacts necessary to compile C/C++ to WebAssembly. * On a Linux system, you can skip to the ["Install"](https://github.com/webassembly/wasi-sdk?tab=readme-ov-file#install) section. @@ -91,7 +91,7 @@ Next, create a file named `component.c` with code that implements the `adder` wo that is, code which fulfills the definition of the interface function declared in `adder.h`. ```c -{{#include ../../examples/tutorial/c/adder/component.c}} +{{#include ../../../examples/tutorial/c/adder/component.c}} ``` ## 4. Compile a WebAssembly Preview 2 component with `wasi-sdk`'s `wasm32-wasip2-clang` @@ -231,7 +231,7 @@ For example, if we modify the above code to reference `printf()`, it would compile to a P1 component: ```c -{{#include ../../examples/tutorial/c/adder/component_with_printf.c}} +{{#include ../../../examples/tutorial/c/adder/component_with_printf.c}} ``` However, the module would fail to transform to a P2 component: @@ -314,14 +314,14 @@ The following section requires you to have [a Rust toolchain][rust] installed. > (The `wasmtime` version is specified in [the Cargo configuration file][cargo-config] > for the example host.) -{{#include example-host-part1.md}} +{{#include ../example-host-part1.md}} A successful run should show the following output (of course, the paths to your example host and adder component will vary, and you should substitute `adder.wasm` with `adder.component.wasm` if you followed the manual instructions above): -{{#include example-host-part2.md}} +{{#include ../example-host-part2.md}} ## 7. Run the component from C/C++ Applications @@ -334,7 +334,7 @@ and run by their toolchains, or even composed with a C language command component and run via the `wasmtime` CLI or any other host. -See the [Rust Tooling guide](../language-support/rust.md#running-a-component-from-rust-applications) +See the [Rust Tooling guide](./rust.md#running-a-component-from-rust-applications) for instructions on how to run this component from the Rust `example-host` (replacing the path to `add.wasm` with your `adder.wasm` or `adder.component.wasm` above). diff --git a/component-model/src/language-support/csharp.md b/component-model/src/language-support/building-a-simple-component/csharp.md similarity index 95% rename from component-model/src/language-support/csharp.md rename to component-model/src/language-support/building-a-simple-component/csharp.md index 175be144..afcc258c 100644 --- a/component-model/src/language-support/csharp.md +++ b/component-model/src/language-support/building-a-simple-component/csharp.md @@ -43,7 +43,7 @@ For this example we will use a WIT file containing two worlds Copy and paste the following into a new file called "`wit/component.wit`". ```wit -{{#include ../../examples/tutorial/csharp/adder/world-hostapp.wit}} +{{#include ../../../examples/tutorial/csharp/adder/world-hostapp.wit}} ``` In the `adder.csproj` project file, add a new `` @@ -88,7 +88,7 @@ This is because we've promised an implementation, but haven't yet written one fo To fix this, add the following code in a file called `Component.cs`: ```csharp -{{#include ../../examples/tutorial/csharp/adder/Component.cs}} +{{#include ../../../examples/tutorial/csharp/adder/Component.cs}} ``` Then, we can build our component: @@ -103,12 +103,12 @@ The component will be available at `bin/Debug/net10.0/wasi-wasm/native/adder.was The following section requires you to have [a Rust toolchain][rust] installed. -{{#include example-host-part1.md}} +{{#include ../example-host-part1.md}} A successful run should show the following output (of course, the paths to your example host and adder component will vary): -{{#include example-host-part2.md}} +{{#include ../example-host-part2.md}} [rust]: https://www.rust-lang.org/learn/get-started @@ -135,7 +135,7 @@ cd host-app Copy the following WIT file into a file called `wit/add.wit` in your project: ```wit -{{#include ../../examples/tutorial/csharp/adder/world-hostapp.wit}} +{{#include ../../../examples/tutorial/csharp/adder/world-hostapp.wit}} ``` Add it to your `host-app.csproj` project file as a new `ItemGroup` at the top level: @@ -154,7 +154,7 @@ Now we'll be focusing on the executable side of the application—the `hostapp` Modify `Program.cs` to look like this: ```csharp -{{#include ../../examples/tutorial/csharp/adder/Program.cs}} +{{#include ../../../examples/tutorial/csharp/adder/Program.cs}} ``` Once again, compile your component with `dotnet build`: diff --git a/component-model/src/language-support/go.md b/component-model/src/language-support/building-a-simple-component/go.md similarity index 98% rename from component-model/src/language-support/go.md rename to component-model/src/language-support/building-a-simple-component/go.md index 1f190458..791a69cd 100644 --- a/component-model/src/language-support/go.md +++ b/component-model/src/language-support/building-a-simple-component/go.md @@ -71,7 +71,7 @@ Create a subdirectory called `wit` and paste the following code into a file called `wit/component.wit`: ```wit -{{#include ../../examples/tutorial/go/adder/world2.wit}} +{{#include ../../../examples/tutorial/go/adder/world2.wit}} ``` The line `include wasi:cli/imports@0.2.0` is necessary because @@ -255,7 +255,7 @@ In your `add` directory, create a file called `main.go` and paste the following code into it: ```Go -{{#include ../../examples/tutorial/go/adder/main.go}} +{{#include ../../../examples/tutorial/go/adder/main.go}} ``` Go's `init` function is used to do initialization tasks @@ -315,12 +315,12 @@ The following section requires you to have [a Rust toolchain][rust] installed. To run our add component, we need to use a host program with a WASI runtime that understands the `example` world. -{{#include example-host-part1.md}} +{{#include ../example-host-part1.md}} A successful run should show the following output (of course, the paths to your example host and adder component will vary): -{{#include example-host-part2.md}} +{{#include ../example-host-part2.md}} [example-host]: https://github.com/bytecodealliance/component-docs/tree/main/component-model/examples/example-host [rust]: https://www.rust-lang.org/learn/get-started diff --git a/component-model/src/language-support/javascript.md b/component-model/src/language-support/building-a-simple-component/javascript.md similarity index 95% rename from component-model/src/language-support/javascript.md rename to component-model/src/language-support/building-a-simple-component/javascript.md index c54dae8d..be7c15fb 100644 --- a/component-model/src/language-support/javascript.md +++ b/component-model/src/language-support/building-a-simple-component/javascript.md @@ -44,13 +44,13 @@ npm install -g @bytecodealliance/jco Building a WebAssembly component with JavaScript often consists of: -1. Determining which interface our component will target (i.e. given a [WebAssembly Interface Types ("WIT")](../design/wit.md) world) +1. Determining which interface our component will target (i.e. given a [WebAssembly Interface Types ("WIT")][docs-wit] world) 2. Creating the component by writing JavaScript that satisfies the interface 3. Compiling the interface-compliant JavaScript to WebAssembly ### What is WIT? -[WebAssembly Interface Types ("WIT")](../design/wit.md) is a featureful Interface Definition Language ("IDL") +[WebAssembly Interface Types ("WIT")][docs-wit] is a featureful Interface Definition Language ("IDL") for defining functionality, but most of the time, you shouldn't need to write WIT from scratch. Often, it's sufficient to download a pre-existing interface that defines what your component should do. @@ -60,7 +60,7 @@ Create a new directory called `adder` and paste the following WIT code into a file called `world.wit`. ```wit -{{#include ../../examples/tutorial/wit/adder/world.wit}} +{{#include ../../../examples/tutorial/wit/adder/world.wit}} ``` The `export add;` declaration inside the `adder` world means that @@ -73,7 +73,7 @@ The parts of this name are: * `add` also happens to be the name of the function itself. * `@0.1.0` is a version number that must match the declared version number of the package. -> To learn more about the WIT syntax, check out the [WIT explainer](../design/wit.md). +> To learn more about the WIT syntax, check out the [WIT explainer][docs-wit]. [adder-world]: https://github.com/bytecodealliance/component-docs/blob/main/component-model/examples/tutorial/wit/adder/world.wit [wit-example-world]: https://github.com/bytecodealliance/component-docs/tree/main/component-model/examples/example-host/add.wit @@ -84,7 +84,7 @@ To implement the [`adder` world][adder-world], we can write a [JavaScript ES mod Paste the following code into a file called `adder.js` in your `adder` directory: ```js -{{#include ../../examples/tutorial/js/adder/adder.js}} +{{#include ../../../examples/tutorial/js/adder/adder.js}} ``` > [!WARNING] @@ -161,11 +161,11 @@ $ wasm-tools print adder.wasm | head -1 To run the component we've built, we can use the [`example-host` project][example-host]: -{{#include example-host-part1.md}} +{{#include ../example-host-part1.md}} The output looks like: -{{#include example-host-part2.md}} +{{#include ../example-host-part2.md}} While the output isn't exciting, the code contained in `example-host` does a lot to make it happen: @@ -240,7 +240,7 @@ using a runtime that supports the [core WebAssembly specification][core-wasm] as To use this component from [Node.js][nodejs], you can write code like the following: ```mjs -{{#include ../../examples/tutorial/js/adder/run.js}} +{{#include ../../../examples/tutorial/js/adder/run.js}} ``` Pasting this code into a file called `run.js` in your `adder` directory, @@ -249,7 +249,7 @@ First, you will need to create a `package.json` file in the same directory: ```json -{{#include ../../examples/tutorial/js/adder/package.json}} +{{#include ../../../examples/tutorial/js/adder/package.json}} ``` > [!NOTE] @@ -291,8 +291,8 @@ Components expose their interfaces via [WebAssembly Interface Types][docs-wit], hand-in-hand with the [Component Model][docs-component-model] which enables components to use higher-level types interchangeably. -[docs-wit]: ../design/wit.md -[docs-component-model]: ../design/why-component-model.md +[docs-wit]: ../../design/wit.md +[docs-component-model]: ../../design/why-component-model.md ### Exporting WIT Interfaces with `jco` @@ -310,10 +310,10 @@ To build a project like `string-reverse` from the ground up, first we'd start wi In a new directory called `string-reverse`, paste this code into a file called `wit/component.wit`: ```wit -{{#include ../../examples/tutorial/js/string-reverse/component.wit}} +{{#include ../../../examples/tutorial/js/string-reverse/component.wit}} ``` -As a slightly deeper crash course on [WIT](../design/wit.md), here's what the above code describes: +As a slightly deeper crash course on [WIT][docs-wit], here's what the above code describes: - We've defined a namespace called `example`. - We've defined a package called `string-reverse` inside the `example` namespace. @@ -337,7 +337,7 @@ OK now let's see what the JS code looks like to *implement* the `component` worl Paste the following code into a file called `string-reverse.mjs`: ```mjs -{{#include ../../examples/tutorial/js/string-reverse/string-reverse.mjs}} +{{#include ../../../examples/tutorial/js/string-reverse/string-reverse.mjs}} ``` > This code uses `split()` to convert the string into an array of characters, @@ -402,7 +402,7 @@ For Node.js, we can use code like this. Paste the following code into a file called `run.js` in your `string-reverse` directory: ```mjs -{{#include ../../examples/tutorial/js/string-reverse/run.js}} +{{#include ../../../examples/tutorial/js/string-reverse/run.js}} ``` > [!NOTE] @@ -411,7 +411,7 @@ Paste the following code into a file called `run.js` in your `string-reverse` di As before, we also need a `package.json` file: ```json -{{#include ../../examples/tutorial/js/string-reverse/package.json}} +{{#include ../../../examples/tutorial/js/string-reverse/package.json}} ``` Then run: @@ -458,7 +458,7 @@ functionality for reversing *and* upper-casing a string. Here's the WIT one might write to enable this functionality: ```wit -{{#include ../../examples/tutorial/js/string-reverse-upper/component.wit}} +{{#include ../../../examples/tutorial/js/string-reverse-upper/component.wit}} ``` This time, the `world` named `revup` that we are building *relies* on the interface `reverse` @@ -478,7 +478,7 @@ The JavaScript to make this work ([`string-reverse-upper.mjs` in `jco/examples`] looks like this: ```js -{{#include ../../examples/tutorial/js/string-reverse-upper/string-reverse-upper.mjs}} +{{#include ../../../examples/tutorial/js/string-reverse-upper/string-reverse-upper.mjs}} ``` If we place the above WIT file in the `wit` subdirectory, we also need to create a @@ -589,7 +589,7 @@ You should see output like the following: To run the transpiled component, we can write code like the following: ```js -{{#include ../../examples/tutorial/js/string-reverse-upper/run.js}} +{{#include ../../../examples/tutorial/js/string-reverse-upper/run.js}} ``` > [!NOTE] diff --git a/component-model/src/language-support/moonbit.md b/component-model/src/language-support/building-a-simple-component/moonbit.md similarity index 95% rename from component-model/src/language-support/moonbit.md rename to component-model/src/language-support/building-a-simple-component/moonbit.md index 80481558..25ea431b 100644 --- a/component-model/src/language-support/moonbit.md +++ b/component-model/src/language-support/building-a-simple-component/moonbit.md @@ -76,7 +76,7 @@ mkdir wit Create `wit/world.wit` with the following content: ```wit -{{#include ../../examples/tutorial/wit/adder/world.wit}} +{{#include ../../../examples/tutorial/wit/adder/world.wit}} ``` This WIT definition: @@ -153,7 +153,7 @@ code, which is what we need to complete. Implement the `add` function in `gen/interface/docs/adder/add/stub.mbt`: ```rust -{{#include ../../examples/tutorial/moonbit/adder/stub.mbt}} +{{#include ../../../examples/tutorial/moonbit/adder/stub.mbt}} ``` ## 5. Configure the Build @@ -161,7 +161,7 @@ Implement the `add` function in `gen/interface/docs/adder/add/stub.mbt`: Ensure your `gen/moon.pkg.json` is properly configured for WebAssembly target: ```json -{{#include ../../examples/tutorial/moonbit/adder/moon.pkg.json}} +{{#include ../../../examples/tutorial/moonbit/adder/moon.pkg.json}} ``` ## 5. Build the WebAssembly Component @@ -249,19 +249,19 @@ This allows you to integrate the MoonBit's error handling easier. For example, for the following interface: ```wit -{{#include ../../examples/tutorial/moonbit/adder-with-error/world.wit}} +{{#include ../../../examples/tutorial/moonbit/adder-with-error/world.wit}} ``` Will generate the following type and function ```rust -{{#include ../../examples/tutorial/moonbit/adder-with-error/top.mbt}} +{{#include ../../../examples/tutorial/moonbit/adder-with-error/top.mbt}} ``` which you may use it as: ```rust -{{#include ../../examples/tutorial/moonbit/adder-with-error/stub.mbt}} +{{#include ../../../examples/tutorial/moonbit/adder-with-error/stub.mbt}} ``` ### --ignore-stub diff --git a/component-model/src/language-support/other-languages.md b/component-model/src/language-support/building-a-simple-component/other-languages.md similarity index 100% rename from component-model/src/language-support/other-languages.md rename to component-model/src/language-support/building-a-simple-component/other-languages.md diff --git a/component-model/src/language-support/python.md b/component-model/src/language-support/building-a-simple-component/python.md similarity index 94% rename from component-model/src/language-support/python.md rename to component-model/src/language-support/building-a-simple-component/python.md index 2b75ddfa..d6f4e45d 100644 --- a/component-model/src/language-support/python.md +++ b/component-model/src/language-support/building-a-simple-component/python.md @@ -17,7 +17,7 @@ Next, create or download the WIT world you would like to target. For this example we will use an [`adder` world][adder-wit] with an `add` function. ```wit -{{#include ../../examples/tutorial/wit/adder/world.wit}} +{{#include ../../../examples/tutorial/wit/adder/world.wit}} ``` Create a new directory for your project and create a subdirectory in it called `wit`. @@ -58,7 +58,7 @@ To implement the `adder` world (in particular the `add` interface that is export put the following code in a file called `app.py`: ```py -{{#include ../../examples/tutorial/python/app.py}} +{{#include ../../../examples/tutorial/python/app.py}} ``` We now can compile our application to a WebAssembly component using the `componentize` subcommand: @@ -77,12 +77,12 @@ Component built successfully The following section requires you to have [a Rust toolchain][rust] installed. -{{#include example-host-part1.md}} +{{#include ../example-host-part1.md}} A successful run should show the following output (of course, the paths to your example host and adder component will vary): -{{#include example-host-part2.md}} +{{#include ../example-host-part2.md}} [rust]: https://www.rust-lang.org/learn/get-started @@ -118,7 +118,7 @@ We can now write a Python program that calls `add`. Copy/paste the following code into a file called `host.py`: ```py -{{#include ../../examples/tutorial/python/host.py}} +{{#include ../../../examples/tutorial/python/host.py}} ``` Run the Python host program: diff --git a/component-model/src/language-support/rust.md b/component-model/src/language-support/building-a-simple-component/rust.md similarity index 98% rename from component-model/src/language-support/rust.md rename to component-model/src/language-support/building-a-simple-component/rust.md index d052c95c..a1d79475 100644 --- a/component-model/src/language-support/rust.md +++ b/component-model/src/language-support/building-a-simple-component/rust.md @@ -86,7 +86,7 @@ the [`adder` world][docs-adder] world in the `docs:adder` [WebAssembly Interface Create a file called `wit/world.wit` and fill it with the following content: ```wit -{{#include ../../examples/tutorial/wit/adder/world.wit}} +{{#include ../../../examples/tutorial/wit/adder/world.wit}} ``` The (WIT) types in this file represent the interface of our component must satisfy (the `adder world`). @@ -148,7 +148,7 @@ We can fill in functionality of the component by implementing `bindings::Guest` Your code should look something like the following: ```rs -{{#include ../../examples/tutorial/adder/src/lib.rs}} +{{#include ../../../examples/tutorial/adder/src/lib.rs}} ``` There are a few points of note in the code listing above: diff --git a/component-model/src/language-support/wat.md b/component-model/src/language-support/building-a-simple-component/wat.md similarity index 95% rename from component-model/src/language-support/wat.md rename to component-model/src/language-support/building-a-simple-component/wat.md index 039e175a..9cd38030 100644 --- a/component-model/src/language-support/wat.md +++ b/component-model/src/language-support/building-a-simple-component/wat.md @@ -12,7 +12,7 @@ It's useful for writing small examples for testing and experimentation. Here's an example of a module expressed in WAT: ```wat -{{#include ../../examples/tutorial/wat/adder/add.wat}} +{{#include ../../../examples/tutorial/wat/adder/add.wat}} ``` The module contains two top-level declarations, a function and an export. @@ -64,7 +64,7 @@ and simply adds two numbers. Create a file called `adder.wit` whose contents are as follows: ```wit - {{#include ../../examples/tutorial/wit/adder/world.wit}} + {{#include ../../../examples/tutorial/wit/adder/world.wit}} ``` 3. Define an `add` core module in WAT that exports an `add` function that adds two parameters. @@ -72,7 +72,7 @@ and simply adds two numbers. (the same as the example in the WAT section): ```wat -{{#include ../../examples/tutorial/wat/adder/add.wat}} +{{#include ../../../examples/tutorial/wat/adder/add.wat}} ``` 4. Use `wasm-tools` to create a binary core module with component metadata embedded inside it: diff --git a/component-model/src/language-support/creating-runnable-components/rust.md b/component-model/src/language-support/creating-runnable-components/rust.md new file mode 100644 index 00000000..19ad259c --- /dev/null +++ b/component-model/src/language-support/creating-runnable-components/rust.md @@ -0,0 +1,2 @@ +# Creating Runnable Components (Rust) + diff --git a/component-model/src/language-support/importing-and-reusing-components/rust.md b/component-model/src/language-support/importing-and-reusing-components/rust.md new file mode 100644 index 00000000..02d36ca2 --- /dev/null +++ b/component-model/src/language-support/importing-and-reusing-components/rust.md @@ -0,0 +1,92 @@ +# Importing and Reusing components (Rust) + +## Importing an interface + +The world file (`wit/world.wit`) we generated doesn't specify any imports. +If your component consumes other components, you can edit the `world.wit` file to import their interfaces. + +> [!NOTE] +> This section is about importing custom WIT interfaces from library components. +> By default, `cargo-component` imports any required [WASI interfaces](https://wasi.dev/interfaces) +> for us without needing to explicitly declare them. + + +For example, suppose you have created and built an adder component as explained in the [exporting an interface section](#exporting-an-interface-with-cargo-component) and want to use that component in a calculator component. Here is a partial example world for a calculator that imports the add interface: + +```wit +// in the 'calculator' project + +// wit/world.wit +package docs:calculator; + +interface calculate { + eval-expression: func(expr: string) -> u32; +} + +world calculator { + export calculate; + import docs:adder/add@0.1.0; +} +``` + +### Referencing the package to import + +Because the `docs:adder` package is in a different project, we must first tell `cargo component` how to find it. To do this, add the following to the `Cargo.toml` file: + +```toml +[package.metadata.component.target.dependencies] +"docs:adder" = { path = "../adder/wit" } # directory containing the WIT package +``` + +> [!NOTE] +> The path for `docs:adder` is relative to the `wit` _directory_, not to the `world.wit` file. +> +> A WIT package may be spread across multiple files in the same directory; `cargo component` will search them all. + +### Calling the import from Rust + +Now the declaration of `add` in the adder's WIT file is visible to the `calculator` project. To invoke the imported `add` interface from the `calculate` implementation: + +```rust +// src/lib.rs +mod bindings; + +use bindings::exports::docs::calculator::calculate::Guest; + +// Bring the imported add function into scope +use bindings::docs::calculator::add::add; + +struct Component; + +impl Guest for Component { + fn eval_expression(expr: String) -> u32 { + // Cleverly parse `expr` into values and operations, and evaluate + // them meticulously. + add(123, 456) + } +} +``` + +### Fulfilling the import + +When you build this using `cargo component build`, the `add` interface remains imported. The calculator has taken a dependency on the `add` _interface_, but has not linked the `adder` implementation of that interface - this is not like referencing the `adder` crate. (Indeed, `calculator` could import the `add` interface even if there was no Rust project implementing the WIT file.) You can see this by running [`wasm-tools component wit`](https://github.com/bytecodealliance/wasm-tools/tree/main/crates/wit-component) to view the calculator's world: + +``` +# Do a release build to prune unused imports (e.g. WASI) +$ cargo component build --release + +$ wasm-tools component wit ./target/wasm32-wasip1/release/calculator.wasm +package root:component; + +world root { + import docs:adder/add@0.1.0; + + export docs:calculator/calculate@0.1.0; +} +``` + +As the import is unfulfilled, the `calculator.wasm` component could not run by itself in its current form. To fulfill the `add` import, so that +only `calculate` is exported, you would need to [compose the `calculator.wasm` with some `adder.wasm` into a single, self-contained component](../../composing-and-distributing/composing.md). + + +[!NOTE]: # diff --git a/component-model/src/language-support/using-wit-resources/rust.md b/component-model/src/language-support/using-wit-resources/rust.md new file mode 100644 index 00000000..c6a45950 --- /dev/null +++ b/component-model/src/language-support/using-wit-resources/rust.md @@ -0,0 +1 @@ +# Using WIT Resources (Rust) diff --git a/component-model/src/using-wit-resources.md b/component-model/src/using-wit-resources.md new file mode 100644 index 00000000..f857d726 --- /dev/null +++ b/component-model/src/using-wit-resources.md @@ -0,0 +1,51 @@ +# Using WIT resources + +This section contains language-specific guides on how to use [WIT][docs-wit] resources. + +Resources represent functionality that is implemented only on one side of a component boundary, +for example in another component or in the underlying platform/host. + +An example of a resource: + +```wit +package docs:calc-resource@0.1.0; + +interface types { + enum operation { + add, + sub, + mul, + div, + } + + variant execute-error { + divide-by-zero, + unexpected(string), + } + + resource stack-calculator { + constructor(); + push-operand: func(operand: u32); + push-operation: func(operation: operation); + execute: func() -> result; + } +} + +world calculator { + export types; +} +``` + +Hosts or Components implementing the stack-based calculator above do share the *resource* (the `stack-calculator` entity) +with components that `import` that functionality, but do *not* share the actual implementation. All calls to `stack-calculator` +resources resolve inside the component that `export`ed the functionality. + +## Languages + +This guide is implemented for various languages: + +| Language | +|--------------------------------------------------------| +| [Rust](./language-support/using-wit-resources/rust.md) | + +[docs-wit]: ./design/wit.md From 24b797d5c3aafedcddb5d548d94aa113d983e00c Mon Sep 17 00:00:00 2001 From: Victor Adossi Date: Mon, 6 Oct 2025 22:25:01 +0900 Subject: [PATCH 03/35] chore(lang/rust): fill out more of runnable component section --- .../src/creating-runnable-components.md | 4 +- .../creating-runnable-components/rust.md | 158 ++++++++++++++++++ 2 files changed, 160 insertions(+), 2 deletions(-) diff --git a/component-model/src/creating-runnable-components.md b/component-model/src/creating-runnable-components.md index 351437d8..43c0971f 100644 --- a/component-model/src/creating-runnable-components.md +++ b/component-model/src/creating-runnable-components.md @@ -5,8 +5,8 @@ This section contains language-specific guides on how to create runnable compone At a high level there are at least two ways to create components that are more like binaries than libraries (i.e. that are easy to run from a tool like `wasmtime`): -- Creating a "command" component -- Exporting the `wasi:cli/run` interface +1. Exporting the `wasi:cli/run` interface (recommended) +2. Creating a "command" component This section explores how to do the above in relevant languages. diff --git a/component-model/src/language-support/creating-runnable-components/rust.md b/component-model/src/language-support/creating-runnable-components/rust.md index 19ad259c..41f61442 100644 --- a/component-model/src/language-support/creating-runnable-components/rust.md +++ b/component-model/src/language-support/creating-runnable-components/rust.md @@ -1,2 +1,160 @@ # Creating Runnable Components (Rust) +## Exporting the `wasi:cli/run` interface + +Any reactor (library-like) component can *also* export the [`run` interface]wasi-cli-iface-run] inside [WASI CLI][wasi-cli], +and signal to ecosystem projects that it can be executed. + +> [!WARNING] +> Reactor components can be reused, and while most platforms may *not* choose to reuse a component after `wasi:cli/run` +> has been called, there is no guarantee that they will or will not. + +### 1. Create a new Rust library project + +To build a simple component that exports `wasi:cli/run`, first create a new Rust project: + +```sh +cargo new --lib runnable-example +``` + +After creating the new project, ensure it is a `cdylib` crate by updating `runnable-example/Cargo.toml` and adding +the following lines: + +```toml +[lib] +crate-type = ['cdylib'] +``` + +We'll also be generating Rust bindings from WIT interfaces, so add `wit-bindgen`: + +```sh +cargo add wit-bindgen +``` + +### 2. Add the appropriate WIT interfaces + +Then, add the appropriate WIT interfaces. For example a simple component that prints "Hello World", add the following +contents to `runnable-example/wit/component.wit`: + +```wit +package example:runnable; + +interface greet { + greet: func(name: string) -> string; +} + +world greeter { + export greet; + export wasi:cli/run@0.2.7; +} +``` + +Building a library component this way does two things: + +- Enables *other* components/hosts to use the `greet` interface +- Exposes an interface (`wasi:cli/run`) that indicates this component can be run like a CLI + - Note that no guarantees are made about what the component *does* when it runs + +While we created `greet`, `wasi:cli` is a well-known interface. We can resolve this interface to local WIT by +using `wkg`: + +```sh +wkg wit fetch +``` + +At this point, you should have a `wit` folder with a `deps` subfolder and yoru original `component.wit`. + +[!WARNING]: # + +### 3. Write the code for the component + +The following code can be inserted into `runnable-example/src/lib.rs`: + +```rust +mod bindings { + wit_bindgen::generate!() +} + + +package example:runnable; + +interface greet { + greet: func(name: string) -> string; +} + +world greeter { + export greet; + export wasi:cli/run@0.2.7; +} + +/// Component off of which implementation will hang (this can be named anything) +struct Component; + +impl Component { + fn greet(s: impl AsRef) -> String { + format!("hello {s}!"); + } +} + +export bindings::example::runnable::greet::Guest for Component { + fn greet(&self, s: String) -> String { + self.greet(s) + } +} + +export bindings::wasi::cli::run::Guest for Component { + fn run(&self) -> Result<(), ()> { + // NOTE: here, we would normally use more of the wasi:cli interface + // to grab arguments and other information from the execution environment. + eprintln!("CLI => {}", self.greet("CLI User")); + Ok(()) + } +} +``` + +### 4. Build the component + +To build the component, use `cargo`: + +```sh +cargo build --target=wasm32-wasip2 +``` + +### 5. Run the component with `wasmtime` + +You can run the component with `wasmtime`, and unlike a generic reactor component, you do not need to specify +the interface and function to run (`wasi:cli/run` is detected and used automatically): + +```console +$ wasmtime run target/wasm32-wasip2/runnable-example.wasm +CLI => hello CLI User! +``` + +## Creating a command component + +A _command_ is a component with a specific export that allows it to be executed directly by `wasmtime` (or other `wasi:cli` hosts). In Rust terms, it's the equivalent of an application (`bin`) package with a `main` function, instead of a library crate (`lib`) package. + +To create a command with cargo component, run: + +```sh +cargo component new +``` + +Unlike library components, this does _not_ have the `--lib` flag. You will see that the created project is different too: + +- It doesn't contain a `.wit` file. `cargo component build` will automatically export the `wasi:cli/run` interface for Rust `bin` packages, and hook it up to `main`. +- Because there's no `.wit` file, `Cargo.toml` doesn't contain a `package.metadata.component.target` section. +- The Rust file is called `main.rs` instead of `lib.rs`, and contains a `main` function instead of an interface implementation. + +You can write Rust in this project, just as you normally would, including importing your own or third-party crates. + +> All the crates that make up your project are linked together at build time, and compiled to a _single_ Wasm component. In this case, all the linking is happening at the Rust level: no WITs or component composition is involved. Only if you import Wasm interfaces do WIT and composition come into play. + +To run your command component: + +```sh +cargo component build +wasmtime run ./target/wasm32-wasip1/debug/.wasm +``` + +> **WARNING:** If your program prints to standard out or error, you may not see the printed output! Some versions of `wasmtime` have a bug where they don't flush output streams before exiting. To work around this, add a `std::thread::sleep()` with a 10 millisecond delay before exiting `main`. From fad04df3d787fc36e08ae28e6d37299259e29ad9 Mon Sep 17 00:00:00 2001 From: Victor Adossi Date: Mon, 6 Oct 2025 22:40:34 +0900 Subject: [PATCH 04/35] chore(lang/rust): add command component section --- .../creating-runnable-components/rust.md | 82 +++++++++---------- 1 file changed, 40 insertions(+), 42 deletions(-) diff --git a/component-model/src/language-support/creating-runnable-components/rust.md b/component-model/src/language-support/creating-runnable-components/rust.md index 41f61442..b724d86d 100644 --- a/component-model/src/language-support/creating-runnable-components/rust.md +++ b/component-model/src/language-support/creating-runnable-components/rust.md @@ -39,12 +39,7 @@ contents to `runnable-example/wit/component.wit`: ```wit package example:runnable; -interface greet { - greet: func(name: string) -> string; -} - world greeter { - export greet; export wasi:cli/run@0.2.7; } ``` @@ -72,41 +67,19 @@ The following code can be inserted into `runnable-example/src/lib.rs`: ```rust mod bindings { - wit_bindgen::generate!() -} - + use super::Component; -package example:runnable; - -interface greet { - greet: func(name: string) -> string; -} + wit_bindgen::generate!(); -world greeter { - export greet; - export wasi:cli/run@0.2.7; + export!(Component); } /// Component off of which implementation will hang (this can be named anything) struct Component; -impl Component { - fn greet(s: impl AsRef) -> String { - format!("hello {s}!"); - } -} - -export bindings::example::runnable::greet::Guest for Component { - fn greet(&self, s: String) -> String { - self.greet(s) - } -} - export bindings::wasi::cli::run::Guest for Component { fn run(&self) -> Result<(), ()> { - // NOTE: here, we would normally use more of the wasi:cli interface - // to grab arguments and other information from the execution environment. - eprintln!("CLI => {}", self.greet("CLI User")); + eprintln!("Hello World!"); Ok(()) } } @@ -127,34 +100,59 @@ the interface and function to run (`wasi:cli/run` is detected and used automatic ```console $ wasmtime run target/wasm32-wasip2/runnable-example.wasm -CLI => hello CLI User! +Hello World! ``` ## Creating a command component -A _command_ is a component with a specific export that allows it to be executed directly by `wasmtime` (or other `wasi:cli` hosts). In Rust terms, it's the equivalent of an application (`bin`) package with a `main` function, instead of a library crate (`lib`) package. +A _command_ is a component with a specific export that allows it to be executed directly by `wasmtime` +(or other `wasi:cli` hosts). In Rust terms, it's the equivalent of an application (`bin`) package with +a `main` function, instead of a library crate (`lib`) package. + +### 1. Create a new Rust binary project -To create a command with cargo component, run: +To create a command with cargo, run: ```sh -cargo component new +cargo new runnable-example ``` -Unlike library components, this does _not_ have the `--lib` flag. You will see that the created project is different too: +Unlike library components, this does _not_ have the `--lib` flag (`--bin` is the default for `cargo new`). -- It doesn't contain a `.wit` file. `cargo component build` will automatically export the `wasi:cli/run` interface for Rust `bin` packages, and hook it up to `main`. -- Because there's no `.wit` file, `Cargo.toml` doesn't contain a `package.metadata.component.target` section. -- The Rust file is called `main.rs` instead of `lib.rs`, and contains a `main` function instead of an interface implementation. +The created Rust source file is called `main.rs` instead of `lib.rs`, and contains a `main` function. You can write Rust in this project, just as you normally would, including importing your own or third-party crates. > All the crates that make up your project are linked together at build time, and compiled to a _single_ Wasm component. In this case, all the linking is happening at the Rust level: no WITs or component composition is involved. Only if you import Wasm interfaces do WIT and composition come into play. +### 2. Write the relevant Rust + +The following code can be inserted into `runnable-example/src/main.rs`: + +```rust +pub fn main() { + eprintln!("Hello World!"); +} +``` + +### 3. Build the component + +To build the component, use `cargo`: + +```sh +cargo build --target=wasm32-wasip2 +``` + +### 4. Run the component with `wasmtime` + To run your command component: ```sh -cargo component build -wasmtime run ./target/wasm32-wasip1/debug/.wasm +wasmtime run ./target/wasm32-wasip2/debug/runnable-example.wasm ``` -> **WARNING:** If your program prints to standard out or error, you may not see the printed output! Some versions of `wasmtime` have a bug where they don't flush output streams before exiting. To work around this, add a `std::thread::sleep()` with a 10 millisecond delay before exiting `main`. +> [!WARNING] +> If your program prints to standard out or error, you may not see the printed output! +> +> Some versions of `wasmtime` have a bug where they don't flush output streams before exiting. To work +> around this, add a `std::thread::sleep()` with a 10 millisecond delay before exiting `main`. From 196c580444f1a77d9d95efc0c9bf85d5000b6107 Mon Sep 17 00:00:00 2001 From: Victor Adossi Date: Mon, 6 Oct 2025 22:49:35 +0900 Subject: [PATCH 05/35] chore(lang/rust): add old cargo component implementations --- .../using-wit-resources/rust.md | 217 ++++++++++++++++++ 1 file changed, 217 insertions(+) diff --git a/component-model/src/language-support/using-wit-resources/rust.md b/component-model/src/language-support/using-wit-resources/rust.md index c6a45950..49fdba76 100644 --- a/component-model/src/language-support/using-wit-resources/rust.md +++ b/component-model/src/language-support/using-wit-resources/rust.md @@ -1 +1,218 @@ # Using WIT Resources (Rust) + +[Resources](../design/wit.md#resources) are handles to entities that live outside the component (i.e. in a host, or other component). + +## An example stack-based calculator + +In this section, our example resource will be a [Reverse Polish Notation (RPN)](https://en.wikipedia.org/wiki/Reverse_Polish_notation) calculator. (Engineers of a certain vintage will remember this from handheld calculators of the 1970s.) A RPN calculator is a stateful entity: a consumer pushes operands and operations onto a stack maintained within the calculator, then evaluates the stack to produce a value. The resource in WIT looks like this: + +```wit +package docs:rpn@0.1.0; + +interface types { + enum operation { + add, + sub, + mul, + div + } + + resource engine { + constructor(); + push-operand: func(operand: u32); + push-operation: func(operation: operation); + execute: func() -> u32; + } +} + +world calculator { + export types; +} +``` + +## Implementing and exporting a resource in a component + +To implement the calculator using `cargo component`: + +1. Create a library component as shown in previous sections, with the WIT given above. + +2. Define a Rust `struct` to represent the calculator state: + + ```rust + use std::cell::RefCell; + + struct CalcEngine { + stack: RefCell>, + } + ``` + + > Why is the stack wrapped in a `RefCell`? As we will see, the generated Rust trait for the calculator engine has _immutable_ references to `self`. But our implementation of that trait will need to mutate the stack. So we need a type that allows for interior mutability, such as `RefCell` or `Arc>`. + +3. The generated bindings (`bindings.rs`) for an exported resource include a trait named `GuestX`, where `X` is the resource name. (You may need to run `cargo component build` to regenerate the bindings after updating the WIT.) For the calculator `engine` resource, the trait is `GuestEngine`. Implement this trait on the `struct` from step 2: + + ```rust + use bindings::exports::docs::rpn::types::{GuestEngine, Operation}; + + impl GuestEngine for CalcEngine { + fn new() -> Self { + CalcEngine { + stack: RefCell::new(vec![]) + } + } + + fn push_operand(&self, operand: u32) { + self.stack.borrow_mut().push(operand); + } + + fn push_operation(&self, operation: Operation) { + let mut stack = self.stack.borrow_mut(); + let right = stack.pop().unwrap(); // TODO: error handling! + let left = stack.pop().unwrap(); + let result = match operation { + Operation::Add => left + right, + Operation::Sub => left - right, + Operation::Mul => left * right, + Operation::Div => left / right, + }; + stack.push(result); + } + + fn execute(&self) -> u32 { + self.stack.borrow_mut().pop().unwrap() // TODO: error handling! + } + } + ``` + +4. We now have a working calculator type which implements the `engine` contract, but we must still connect that type to the `engine` resource type. This is done by implementing the generated `Guest` trait. For this WIT, the `Guest` trait contains nothing except an associated type. You can use an empty `struct` to implement the `Guest` trait on. Set the associated type for the resource - in our case, `Engine` - to the type which implements the resource trait - in our case, the `CalcEngine` `struct` which implements `GuestEngine`. Then use the `export!` macro to export the mapping: + + ```rust + struct Implementation; + impl Guest for Implementation { + type Engine = CalcEngine; + } + + bindings::export!(Implementation with_types_in bindings); + ``` + +This completes the implementation of the calculator `engine` resource. Run `cargo component build` to create a component `.wasm` file. + +## Importing and consuming a resource in a component + +To use the calculator engine in another component, that component must import the resource. + +1. Create a command component as shown in previous sections. + +2. Add a `wit/world.wit` to your project, and write a WIT world that imports the RPN calculator types: + + ```wit + package docs:rpn-cmd; + + world app { + import docs:rpn/types@0.1.0; + } + ``` + +3. Edit `Cargo.toml` to tell `cargo component` about the new WIT file and the external RPN package file: + + ```toml + [package.metadata.component] + package = "docs:rpn-cmd" + + [package.metadata.component.target] + path = "wit" + + [package.metadata.component.target.dependencies] + "docs:rpn" = { path = "../wit" } # or wherever your resource WIT is + ``` + +4. The resource now appears in the generated bindings as a `struct`, with appropriate associated functions. Use these to construct a test app: + + ```rust + #[allow(warnings)] + mod bindings; + use bindings::docs::rpn::types::{Engine, Operation}; + + fn main() { + let calc = Engine::new(); + calc.push_operand(1); + calc.push_operand(2); + calc.push_operation(Operation::Add); + let sum = calc.execute(); + println!("{sum}"); + } + ``` + +You can now build the command component and [compose it with the `.wasm` component that implements the resource.](../composing-and-distributing/composing.md). You can then run the composed command with `wasmtime run`. + +## Implementing and exporting a resource implementation in a host + +If you are hosting a Wasm runtime, you can export a resource from your host for guests to consume. Hosting a runtime is outside the scope of this book, so we will give only a broad outline here. This is specific to the Wasmtime runtime; other runtimes may express things differently. + +1. Use `wasmtime::component::bindgen!` to specify the WIT you are a host for: + + ```rust + wasmtime::component::bindgen!({ + path: "../wit" + }); + ``` + +2. Tell `bindgen!` how you will represent the resource in the host via the `with` field. This can be any Rust type. For example, the RPN engine could be represented by a `CalcEngine` struct: + + ```rust + wasmtime::component::bindgen!({ + path: "../wit", + with: { + "docs:rpn/types/engine": CalcEngine, + } + }); + ``` + + > If you don't specify the host representation for a resource, it defaults to an empty enum. This is rarely useful as resources are usually stateful. + +3. If the representation type isn't a built-in type, define it: + + ```rust + struct CalcEngine { /* ... */ } + ``` + +4. As a host, you will already be implementing a `Host` trait. You will now need to implement a `HostX` trait (where `X` is the resource name) _on the same type_ as the `Host` trait: + + ```rust + impl docs::rpn::types::HostEngine for MyHost { + fn new(&mut self) -> wasmtime::component::Resource { /* ... */ } + fn push_operand(&mut self, self_: wasmtime::component::Resource) { /* ... */ } + // etc. + } + ``` + + > **Important:** You implement this on the 'overall' host type, *not* on the resource representation! Therefore, the `self` reference in these functions is to the 'overall' host type. For instance methods of the resource, the instance is identified by a second parameter (`self_`), of type `wasmtime::component::Resource`. + +5. Add a `wasmtime::component::ResourceTable` to the host: + + ```rust + struct MyHost { + calcs: wasmtime::component::ResourceTable, + } + ``` + +6. In your resource method implementations, use this table to store and access instances of the resource representation: + + ```rust + impl docs::rpn::types::HostEngine for MyHost { + fn new(&mut self) -> wasmtime::component::Resource { + self.calcs.push(CalcEngine::new()).unwrap() // TODO: error handling + } + fn push_operand(&mut self, self_: wasmtime::component::Resource) { + let calc_engine = self.calcs.get(&self_).unwrap(); + // calc_engine is a CalcEngine - call its functions + } + // etc. + } + ``` + +[cargo-component]: https://github.com/bytecodealliance/cargo-component +[cargo-component-install]: https://github.com/bytecodealliance/cargo-component#install +[docs-adder]: https://github.com/bytecodealliance/component-docs/tree/main/component-model/examples/tutorial/wit/adder/world.wit + +[!NOTE]: # +[!WARNING]: # From e23e45325958584c029ab25c45e0cf60e19d814e Mon Sep 17 00:00:00 2001 From: Victor Adossi Date: Mon, 6 Oct 2025 23:01:47 +0900 Subject: [PATCH 06/35] refactor(lang/rust): WIT resources guide for rust --- .../using-wit-resources/rust.md | 70 ++++++++++++------- 1 file changed, 45 insertions(+), 25 deletions(-) diff --git a/component-model/src/language-support/using-wit-resources/rust.md b/component-model/src/language-support/using-wit-resources/rust.md index 49fdba76..0a6215c0 100644 --- a/component-model/src/language-support/using-wit-resources/rust.md +++ b/component-model/src/language-support/using-wit-resources/rust.md @@ -2,9 +2,14 @@ [Resources](../design/wit.md#resources) are handles to entities that live outside the component (i.e. in a host, or other component). -## An example stack-based calculator +## An example stack-based Reverse Polish Notation (RPN) calculator -In this section, our example resource will be a [Reverse Polish Notation (RPN)](https://en.wikipedia.org/wiki/Reverse_Polish_notation) calculator. (Engineers of a certain vintage will remember this from handheld calculators of the 1970s.) A RPN calculator is a stateful entity: a consumer pushes operands and operations onto a stack maintained within the calculator, then evaluates the stack to produce a value. The resource in WIT looks like this: +In this section, our example resource will be a [Reverse Polish Notation (RPN)](https://en.wikipedia.org/wiki/Reverse_Polish_notation) calculator. (Engineers of a certain vintage will remember this from handheld calculators of the 1970s.) + +A RPN calculator is a stateful entity: a consumer pushes operands and operations onto a stack +maintained within the calculator, then evaluates the stack to produce a value. + +In WIT, the resource looks like the following: ```wit package docs:rpn@0.1.0; @@ -32,7 +37,7 @@ world calculator { ## Implementing and exporting a resource in a component -To implement the calculator using `cargo component`: +To implement the calculator in Rust: 1. Create a library component as shown in previous sections, with the WIT given above. @@ -48,9 +53,15 @@ To implement the calculator using `cargo component`: > Why is the stack wrapped in a `RefCell`? As we will see, the generated Rust trait for the calculator engine has _immutable_ references to `self`. But our implementation of that trait will need to mutate the stack. So we need a type that allows for interior mutability, such as `RefCell` or `Arc>`. -3. The generated bindings (`bindings.rs`) for an exported resource include a trait named `GuestX`, where `X` is the resource name. (You may need to run `cargo component build` to regenerate the bindings after updating the WIT.) For the calculator `engine` resource, the trait is `GuestEngine`. Implement this trait on the `struct` from step 2: +3. The generated bindings (`bindings.rs`) for an exported resource include a trait named `GuestX`, where `X` is the resource name. For the calculator `engine` resource, the trait is `GuestEngine`. Implement this trait on the `struct` from step 2: ```rust + mod bindings { + use super::Component; + wit_bindgen::generate!(); + export!(Component); + } + use bindings::exports::docs::rpn::types::{GuestEngine, Operation}; impl GuestEngine for CalcEngine { @@ -86,23 +97,26 @@ To implement the calculator using `cargo component`: 4. We now have a working calculator type which implements the `engine` contract, but we must still connect that type to the `engine` resource type. This is done by implementing the generated `Guest` trait. For this WIT, the `Guest` trait contains nothing except an associated type. You can use an empty `struct` to implement the `Guest` trait on. Set the associated type for the resource - in our case, `Engine` - to the type which implements the resource trait - in our case, the `CalcEngine` `struct` which implements `GuestEngine`. Then use the `export!` macro to export the mapping: ```rust - struct Implementation; - impl Guest for Implementation { + // ... bindings & CalcEngine impl code ... + + struct Component; + + impl bindings::Guest for Component { type Engine = CalcEngine; } - bindings::export!(Implementation with_types_in bindings); + bindings::export!(Component); ``` -This completes the implementation of the calculator `engine` resource. Run `cargo component build` to create a component `.wasm` file. +This completes the implementation of the calculator `engine` resource. Run `cargo build --target=wasm32-wasip2` to create a component `.wasm` file. ## Importing and consuming a resource in a component To use the calculator engine in another component, that component must import the resource. -1. Create a command component as shown in previous sections. +1. [Create a runnable component](../../creating-runnable-components/rust.md) as shown in previous sections. -2. Add a `wit/world.wit` to your project, and write a WIT world that imports the RPN calculator types: +2. Add a `wit/component.wit` to your project, and write a WIT world that imports the RPN calculator types: ```wit package docs:rpn-cmd; @@ -112,24 +126,24 @@ To use the calculator engine in another component, that component must import th } ``` -3. Edit `Cargo.toml` to tell `cargo component` about the new WIT file and the external RPN package file: +3. Create a `wkg.toml` file to enable retrieving the relevant WIT files for `docs:rpn` (which contains the `engine` resource): ```toml - [package.metadata.component] - package = "docs:rpn-cmd" - - [package.metadata.component.target] - path = "wit" - - [package.metadata.component.target.dependencies] - "docs:rpn" = { path = "../wit" } # or wherever your resource WIT is + [overrides] + "docs:rpn" = { path = "../path/to/docs-rpn/wit" } ``` + After doing this, you can run `wkg wit fetch` to ensure all WIT is available locally. + 4. The resource now appears in the generated bindings as a `struct`, with appropriate associated functions. Use these to construct a test app: ```rust - #[allow(warnings)] - mod bindings; + mod bindings { + use super::Component; + wit_bindgen::generate!(); + export!(Component); + } + use bindings::docs::rpn::types::{Engine, Operation}; fn main() { @@ -142,11 +156,19 @@ To use the calculator engine in another component, that component must import th } ``` -You can now build the command component and [compose it with the `.wasm` component that implements the resource.](../composing-and-distributing/composing.md). You can then run the composed command with `wasmtime run`. +Building the component as is creates a WebAssembly component with an *unsatisfied import* -- namely the `docs:rpn/types` import. + +After building the component, it must be [composed with a `.wasm` component that implements the resource.](../composing-and-distributing/composing.md). After composition creates a component with no unsatisfied imports, the composed command component can be run with `wasmtime run`. + +Alternatively, a host that can provide the `docs:rpn/types` import (and related resource) can also be used to run the component +in it's "incomplete" state (as the host will "complete" the componnt by providing the expected import). ## Implementing and exporting a resource implementation in a host -If you are hosting a Wasm runtime, you can export a resource from your host for guests to consume. Hosting a runtime is outside the scope of this book, so we will give only a broad outline here. This is specific to the Wasmtime runtime; other runtimes may express things differently. +If you are hosting a Wasm runtime, you can export a resource from your host for guests to consume. + +Hosting a runtime is outside the scope of this book, so we will give only a broad outline here. This is specific +to the Wasmtime runtime; other runtimes may express things differently. 1. Use `wasmtime::component::bindgen!` to specify the WIT you are a host for: @@ -210,8 +232,6 @@ If you are hosting a Wasm runtime, you can export a resource from your host for } ``` -[cargo-component]: https://github.com/bytecodealliance/cargo-component -[cargo-component-install]: https://github.com/bytecodealliance/cargo-component#install [docs-adder]: https://github.com/bytecodealliance/component-docs/tree/main/component-model/examples/tutorial/wit/adder/world.wit [!NOTE]: # From fdd79341ffe75bb368e9a88c02ce3298d84af44b Mon Sep 17 00:00:00 2001 From: Victor Adossi Date: Mon, 6 Oct 2025 23:06:04 +0900 Subject: [PATCH 07/35] refactor(lang/rust): importing guide, simple build guide --- .../building-a-simple-component/rust.md | 2 +- .../importing-and-reusing-components/rust.md | 26 ++++++++++++------- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/component-model/src/language-support/building-a-simple-component/rust.md b/component-model/src/language-support/building-a-simple-component/rust.md index a1d79475..1cbea41b 100644 --- a/component-model/src/language-support/building-a-simple-component/rust.md +++ b/component-model/src/language-support/building-a-simple-component/rust.md @@ -1,4 +1,4 @@ -# Components in Rust +# Building a simple component (Rust) [Rust][rust] has first-class support for WebAssembly core and WebAssembly components via the available targets in the toolchain: diff --git a/component-model/src/language-support/importing-and-reusing-components/rust.md b/component-model/src/language-support/importing-and-reusing-components/rust.md index 02d36ca2..e44e6fe9 100644 --- a/component-model/src/language-support/importing-and-reusing-components/rust.md +++ b/component-model/src/language-support/importing-and-reusing-components/rust.md @@ -31,17 +31,17 @@ world calculator { ### Referencing the package to import -Because the `docs:adder` package is in a different project, we must first tell `cargo component` how to find it. To do this, add the following to the `Cargo.toml` file: +Because the `docs:adder` package is in a different project, we must first tell `cargo` how to find it. To do this, we add a +custom `wkg.toml` to our project: ```toml -[package.metadata.component.target.dependencies] +[overrides] "docs:adder" = { path = "../adder/wit" } # directory containing the WIT package ``` -> [!NOTE] -> The path for `docs:adder` is relative to the `wit` _directory_, not to the `world.wit` file. -> -> A WIT package may be spread across multiple files in the same directory; `cargo component` will search them all. +After adding this configuration file, when we run `wkg wit fetch`, `wkg` will assume that the package `docs:adder` can be found +at the path that is given, and will pull it's contents into the local project under `wit/deps`. + ### Calling the import from Rust @@ -69,11 +69,16 @@ impl Guest for Component { ### Fulfilling the import -When you build this using `cargo component build`, the `add` interface remains imported. The calculator has taken a dependency on the `add` _interface_, but has not linked the `adder` implementation of that interface - this is not like referencing the `adder` crate. (Indeed, `calculator` could import the `add` interface even if there was no Rust project implementing the WIT file.) You can see this by running [`wasm-tools component wit`](https://github.com/bytecodealliance/wasm-tools/tree/main/crates/wit-component) to view the calculator's world: +When you build this using `cargo build`, the `add` interface remains unsatisfied (i.e. imported). + +The calculator has taken a dependency on the `add` _interface_, but has not linked the `adder` implementation of +that interface - this is not like referencing the `adder` crate (Indeed, `calculator` could import the `add` interface even if there was no Rust project implementing the WIT file) . + +You can see this by running [`wasm-tools component wit`](https://github.com/bytecodealliance/wasm-tools/tree/main/crates/wit-component) to view the calculator's world: ``` # Do a release build to prune unused imports (e.g. WASI) -$ cargo component build --release +$ cargo build --target=wasm32-wasip2 --release $ wasm-tools component wit ./target/wasm32-wasip1/release/calculator.wasm package root:component; @@ -85,8 +90,9 @@ world root { } ``` -As the import is unfulfilled, the `calculator.wasm` component could not run by itself in its current form. To fulfill the `add` import, so that -only `calculate` is exported, you would need to [compose the `calculator.wasm` with some `adder.wasm` into a single, self-contained component](../../composing-and-distributing/composing.md). +As the import is unfulfilled, the `calculator.wasm` component could not run by itself in its current form. +To fulfill the `add` import, so that only `calculate` is exported, you would +need to [compose the `calculator.wasm` with some `adder.wasm` into a single, self-contained component](../../composing-and-distributing/composing.md). [!NOTE]: # From bb60a35726a45fabcb62bb73316816c533f21125 Mon Sep 17 00:00:00 2001 From: Victor Adossi Date: Mon, 6 Oct 2025 23:07:36 +0900 Subject: [PATCH 08/35] fix(lang/rust): remove more references to cargo-component --- component-model/src/composing-and-distributing/composing.md | 2 -- .../language-support/importing-and-reusing-components/rust.md | 3 --- 2 files changed, 5 deletions(-) diff --git a/component-model/src/composing-and-distributing/composing.md b/component-model/src/composing-and-distributing/composing.md index e7db1005..c464b523 100644 --- a/component-model/src/composing-and-distributing/composing.md +++ b/component-model/src/composing-and-distributing/composing.md @@ -62,8 +62,6 @@ Here are some tips to avoid or diagnose errors: because `regex` doesn't have any dependencies, let alone ones that `validator` can satisfy. * Composition cares about interface versions, and current tools are inconsistent about when they infer or inject versions. - For example, if a Rust component exports `test:mypackage`, - `cargo component build` will decorate this with the crate version, e.g. `test:mypackage@0.1.0`. If another Rust component _imports_ an interface from `test:mypackage`, that won't match `test:mypackage@0.1.0`. You can use [`wasm-tools component wit`](https://github.com/bytecodealliance/wasm-tools/tree/main/crates/wit-component) to view the imports and exports embedded in the `.wasm` files and check whether they match up. diff --git a/component-model/src/language-support/importing-and-reusing-components/rust.md b/component-model/src/language-support/importing-and-reusing-components/rust.md index e44e6fe9..6acd35cf 100644 --- a/component-model/src/language-support/importing-and-reusing-components/rust.md +++ b/component-model/src/language-support/importing-and-reusing-components/rust.md @@ -7,9 +7,6 @@ If your component consumes other components, you can edit the `world.wit` file t > [!NOTE] > This section is about importing custom WIT interfaces from library components. -> By default, `cargo-component` imports any required [WASI interfaces](https://wasi.dev/interfaces) -> for us without needing to explicitly declare them. - For example, suppose you have created and built an adder component as explained in the [exporting an interface section](#exporting-an-interface-with-cargo-component) and want to use that component in a calculator component. Here is a partial example world for a calculator that imports the add interface: From 4334bd931a97d67c7396b298155426c3980471cb Mon Sep 17 00:00:00 2001 From: Victor Adossi Date: Mon, 6 Oct 2025 23:09:34 +0900 Subject: [PATCH 09/35] fix(lang/rust): links --- .../language-support/creating-runnable-components/rust.md | 5 ++++- .../src/language-support/using-wit-resources/rust.md | 6 +++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/component-model/src/language-support/creating-runnable-components/rust.md b/component-model/src/language-support/creating-runnable-components/rust.md index b724d86d..886c36df 100644 --- a/component-model/src/language-support/creating-runnable-components/rust.md +++ b/component-model/src/language-support/creating-runnable-components/rust.md @@ -2,13 +2,16 @@ ## Exporting the `wasi:cli/run` interface -Any reactor (library-like) component can *also* export the [`run` interface]wasi-cli-iface-run] inside [WASI CLI][wasi-cli], +Any reactor (library-like) component can *also* export the [`run` interface][wasi-cli-iface-run] inside [WASI CLI][wasi-cli], and signal to ecosystem projects that it can be executed. > [!WARNING] > Reactor components can be reused, and while most platforms may *not* choose to reuse a component after `wasi:cli/run` > has been called, there is no guarantee that they will or will not. +[wasi-cli-iface-run]: https://github.com/WebAssembly/wasi-cli/tree/main/wit/run.wit +[wasi-cli]: https://github.com/WebAssembly/wasi-cli + ### 1. Create a new Rust library project To build a simple component that exports `wasi:cli/run`, first create a new Rust project: diff --git a/component-model/src/language-support/using-wit-resources/rust.md b/component-model/src/language-support/using-wit-resources/rust.md index 0a6215c0..8288ee02 100644 --- a/component-model/src/language-support/using-wit-resources/rust.md +++ b/component-model/src/language-support/using-wit-resources/rust.md @@ -1,6 +1,6 @@ # Using WIT Resources (Rust) -[Resources](../design/wit.md#resources) are handles to entities that live outside the component (i.e. in a host, or other component). +[Resources](../../design/wit.md#resources) are handles to entities that live outside the component (i.e. in a host, or other component). ## An example stack-based Reverse Polish Notation (RPN) calculator @@ -114,7 +114,7 @@ This completes the implementation of the calculator `engine` resource. Run `carg To use the calculator engine in another component, that component must import the resource. -1. [Create a runnable component](../../creating-runnable-components/rust.md) as shown in previous sections. +1. [Create a runnable component](../creating-runnable-components/rust.md) as shown in previous sections. 2. Add a `wit/component.wit` to your project, and write a WIT world that imports the RPN calculator types: @@ -158,7 +158,7 @@ To use the calculator engine in another component, that component must import th Building the component as is creates a WebAssembly component with an *unsatisfied import* -- namely the `docs:rpn/types` import. -After building the component, it must be [composed with a `.wasm` component that implements the resource.](../composing-and-distributing/composing.md). After composition creates a component with no unsatisfied imports, the composed command component can be run with `wasmtime run`. +After building the component, it must be [composed with a `.wasm` component that implements the resource.](../../composing-and-distributing/composing.md). After composition creates a component with no unsatisfied imports, the composed command component can be run with `wasmtime run`. Alternatively, a host that can provide the `docs:rpn/types` import (and related resource) can also be used to run the component in it's "incomplete" state (as the host will "complete" the componnt by providing the expected import). From 59e0efbf2c51caa36d08b71757ff18af4fa62214 Mon Sep 17 00:00:00 2001 From: Victor Adossi Date: Wed, 15 Oct 2025 10:51:24 +0900 Subject: [PATCH 10/35] refactor(pl/rust): rework wasi run interface note --- .../src/language-support/creating-runnable-components/rust.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/component-model/src/language-support/creating-runnable-components/rust.md b/component-model/src/language-support/creating-runnable-components/rust.md index 886c36df..b1abc81d 100644 --- a/component-model/src/language-support/creating-runnable-components/rust.md +++ b/component-model/src/language-support/creating-runnable-components/rust.md @@ -3,7 +3,7 @@ ## Exporting the `wasi:cli/run` interface Any reactor (library-like) component can *also* export the [`run` interface][wasi-cli-iface-run] inside [WASI CLI][wasi-cli], -and signal to ecosystem projects that it can be executed. +and signal to consumers that the library can also be run similarly to a binary. > [!WARNING] > Reactor components can be reused, and while most platforms may *not* choose to reuse a component after `wasi:cli/run` From 1cba12f2a52f7f81cc210f7aaea5c3a43e7d2038 Mon Sep 17 00:00:00 2001 From: Victor Adossi Date: Wed, 15 Oct 2025 10:56:20 +0900 Subject: [PATCH 11/35] refactor(pl/rust): remove warning about reactor component reuse --- .../src/language-support/creating-runnable-components/rust.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/component-model/src/language-support/creating-runnable-components/rust.md b/component-model/src/language-support/creating-runnable-components/rust.md index b1abc81d..858a08b4 100644 --- a/component-model/src/language-support/creating-runnable-components/rust.md +++ b/component-model/src/language-support/creating-runnable-components/rust.md @@ -5,10 +5,6 @@ Any reactor (library-like) component can *also* export the [`run` interface][wasi-cli-iface-run] inside [WASI CLI][wasi-cli], and signal to consumers that the library can also be run similarly to a binary. -> [!WARNING] -> Reactor components can be reused, and while most platforms may *not* choose to reuse a component after `wasi:cli/run` -> has been called, there is no guarantee that they will or will not. - [wasi-cli-iface-run]: https://github.com/WebAssembly/wasi-cli/tree/main/wit/run.wit [wasi-cli]: https://github.com/WebAssembly/wasi-cli From e919fe25c82b5fdc3950f77a3cc406d78248d8d0 Mon Sep 17 00:00:00 2001 From: Victor Adossi Date: Wed, 15 Oct 2025 10:56:54 +0900 Subject: [PATCH 12/35] fix(pl/rust): typos --- .../src/language-support/building-a-simple-component/rust.md | 2 +- .../src/language-support/creating-runnable-components/rust.md | 2 +- .../language-support/importing-and-reusing-components/rust.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/component-model/src/language-support/building-a-simple-component/rust.md b/component-model/src/language-support/building-a-simple-component/rust.md index 1cbea41b..3bff6594 100644 --- a/component-model/src/language-support/building-a-simple-component/rust.md +++ b/component-model/src/language-support/building-a-simple-component/rust.md @@ -232,7 +232,7 @@ package docs:adder@0.1.0 { ## 8. Running the `adder` Component -To verify that our component works, lets run it from a Rust application that knows how to run a +To verify that our component works, let's run it from a Rust application that knows how to run a component targeting the [`adder` world](#adding-the-wit-world). The application uses [`wasmtime`][crates-wasmtime] to generate Rust "host"/"embedder" bindings, diff --git a/component-model/src/language-support/creating-runnable-components/rust.md b/component-model/src/language-support/creating-runnable-components/rust.md index 858a08b4..c9f40a5d 100644 --- a/component-model/src/language-support/creating-runnable-components/rust.md +++ b/component-model/src/language-support/creating-runnable-components/rust.md @@ -56,7 +56,7 @@ using `wkg`: wkg wit fetch ``` -At this point, you should have a `wit` folder with a `deps` subfolder and yoru original `component.wit`. +At this point, you should have a `wit` folder with a `deps` subfolder and your original `component.wit`. [!WARNING]: # diff --git a/component-model/src/language-support/importing-and-reusing-components/rust.md b/component-model/src/language-support/importing-and-reusing-components/rust.md index 6acd35cf..195a0d34 100644 --- a/component-model/src/language-support/importing-and-reusing-components/rust.md +++ b/component-model/src/language-support/importing-and-reusing-components/rust.md @@ -37,7 +37,7 @@ custom `wkg.toml` to our project: ``` After adding this configuration file, when we run `wkg wit fetch`, `wkg` will assume that the package `docs:adder` can be found -at the path that is given, and will pull it's contents into the local project under `wit/deps`. +at the path that is given, and will pull its contents into the local project under `wit/deps`. ### Calling the import from Rust From 2f2eb7076fa9555fe614901abd1bc12ca41da1d0 Mon Sep 17 00:00:00 2001 From: Victor Adossi Date: Wed, 15 Oct 2025 11:09:31 +0900 Subject: [PATCH 13/35] fix(pl/rust): add missing greet interface for runnable example --- .../creating-runnable-components/rust.md | 29 +++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/component-model/src/language-support/creating-runnable-components/rust.md b/component-model/src/language-support/creating-runnable-components/rust.md index c9f40a5d..96aec12f 100644 --- a/component-model/src/language-support/creating-runnable-components/rust.md +++ b/component-model/src/language-support/creating-runnable-components/rust.md @@ -38,7 +38,12 @@ contents to `runnable-example/wit/component.wit`: ```wit package example:runnable; +interface greet { + greet: func(name: string) -> string; +} + world greeter { + export greet; export wasi:cli/run@0.2.7; } ``` @@ -76,8 +81,16 @@ mod bindings { /// Component off of which implementation will hang (this can be named anything) struct Component; -export bindings::wasi::cli::run::Guest for Component { - fn run(&self) -> Result<(), ()> { +/// Implementation for the `greet` interface export +impl bindings::exports::example::runnable::greet::Guest for Component { + fn greet(name: String) -> String { + format!("Hello {name}!") + } +} + +/// Implementation for `wasi:cli/run` interface export +impl bindings::exports::wasi::cli::run::Guest for Component { + fn run() -> Result<(), ()> { eprintln!("Hello World!"); Ok(()) } @@ -92,6 +105,12 @@ To build the component, use `cargo`: cargo build --target=wasm32-wasip2 ``` +The component can also be built in release mode: + +```console +cargo build --target=wasm32-wasip2 --release +``` + ### 5. Run the component with `wasmtime` You can run the component with `wasmtime`, and unlike a generic reactor component, you do not need to specify @@ -142,6 +161,12 @@ To build the component, use `cargo`: cargo build --target=wasm32-wasip2 ``` +The component can also be built in release mode: + +```console +cargo build --target=wasm32-wasip2 --release +``` + ### 4. Run the component with `wasmtime` To run your command component: From 03e76d9a0ab6b145f83d8e10379a180c00decc15 Mon Sep 17 00:00:00 2001 From: Victor Adossi Date: Wed, 15 Oct 2025 11:15:12 +0900 Subject: [PATCH 14/35] refactor(pl/rust): add note about wasi:cli/run standard interop --- .../language-support/creating-runnable-components/rust.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/component-model/src/language-support/creating-runnable-components/rust.md b/component-model/src/language-support/creating-runnable-components/rust.md index 96aec12f..ca172159 100644 --- a/component-model/src/language-support/creating-runnable-components/rust.md +++ b/component-model/src/language-support/creating-runnable-components/rust.md @@ -63,6 +63,11 @@ wkg wit fetch At this point, you should have a `wit` folder with a `deps` subfolder and your original `component.wit`. +The component we will create to satisfy the WIT above can be used as a library, as other components +or platforms can use the `greet` interface export. More importantly, the component can *also* be +recognized as a generically runnable component thanks to `wasi:cli/run`, so it can work +with any tooling (ex. `wasmtime run`) that supports/recognizes the `wasi:cli` interface. + [!WARNING]: # ### 3. Write the code for the component From 76238d7b18bc12ec89dc670630d9e641f967747a Mon Sep 17 00:00:00 2001 From: Victor Adossi Date: Wed, 15 Oct 2025 11:20:11 +0900 Subject: [PATCH 15/35] refactor(pl/rust): discuss runnable command components first --- .../src/creating-runnable-components.md | 4 +- .../creating-runnable-components/rust.md | 128 +++++++++--------- 2 files changed, 69 insertions(+), 63 deletions(-) diff --git a/component-model/src/creating-runnable-components.md b/component-model/src/creating-runnable-components.md index 43c0971f..06e5c4e0 100644 --- a/component-model/src/creating-runnable-components.md +++ b/component-model/src/creating-runnable-components.md @@ -5,8 +5,8 @@ This section contains language-specific guides on how to create runnable compone At a high level there are at least two ways to create components that are more like binaries than libraries (i.e. that are easy to run from a tool like `wasmtime`): -1. Exporting the `wasi:cli/run` interface (recommended) -2. Creating a "command" component +1. Creating a "command" component +2. Exporting the `wasi:cli/run` interface This section explores how to do the above in relevant languages. diff --git a/component-model/src/language-support/creating-runnable-components/rust.md b/component-model/src/language-support/creating-runnable-components/rust.md index ca172159..3bcbdc3d 100644 --- a/component-model/src/language-support/creating-runnable-components/rust.md +++ b/component-model/src/language-support/creating-runnable-components/rust.md @@ -1,10 +1,76 @@ # Creating Runnable Components (Rust) -## Exporting the `wasi:cli/run` interface +## Creating a command component + +A _command_ is a component with a specific export that allows it to be executed directly by `wasmtime` +(or other `wasi:cli` hosts). In Rust terms, it's the equivalent of an application (`bin`) package with +a `main` function, instead of a library crate (`lib`) package. + +Command components work by including a WebAssembly core export `_start` that indicates the component +has a natural `main`-like starting point. + +### 1. Create a new Rust binary project + +To create a command with cargo, run: + +```sh +cargo new runnable-example +``` + +Unlike library components, this does _not_ have the `--lib` flag (`--bin` is the default for `cargo new`). + +The created Rust source file is called `main.rs` instead of `lib.rs`, and contains a `main` function. + +You can write Rust in this project, just as you normally would, including importing your own or third-party crates. + +> All the crates that make up your project are linked together at build time, and compiled to a _single_ Wasm component. In this case, all the linking is happening at the Rust level: no WITs or component composition is involved. Only if you import Wasm interfaces do WIT and composition come into play. + +### 2. Write the relevant Rust + +The following code can be inserted into `runnable-example/src/main.rs`: + +```rust +pub fn main() { + eprintln!("Hello World!"); +} +``` + +### 3. Build the component + +To build the component, use `cargo`: + +```sh +cargo build --target=wasm32-wasip2 +``` + +The component can also be built in release mode: + +```console +cargo build --target=wasm32-wasip2 --release +``` + +### 4. Run the component with `wasmtime` + +To run your command component: + +```sh +wasmtime run ./target/wasm32-wasip2/debug/runnable-example.wasm +``` + +> [!WARNING] +> If your program prints to standard out or error, you may not see the printed output! +> +> Some versions of `wasmtime` have a bug where they don't flush output streams before exiting. To work +> around this, add a `std::thread::sleep()` with a 10 millisecond delay before exiting `main`. + +## Enabling a library component to be run via the `wasi:cli/run` interface Any reactor (library-like) component can *also* export the [`run` interface][wasi-cli-iface-run] inside [WASI CLI][wasi-cli], and signal to consumers that the library can also be run similarly to a binary. +Unlike command components, library components have no `_start`, but by exporting the `wasi:cli/run` interface, +tooling that recognizes these exports can easily execute the given WebAssembly binary (e.g. `wasmtime run`). + [wasi-cli-iface-run]: https://github.com/WebAssembly/wasi-cli/tree/main/wit/run.wit [wasi-cli]: https://github.com/WebAssembly/wasi-cli @@ -125,63 +191,3 @@ the interface and function to run (`wasi:cli/run` is detected and used automatic $ wasmtime run target/wasm32-wasip2/runnable-example.wasm Hello World! ``` - -## Creating a command component - -A _command_ is a component with a specific export that allows it to be executed directly by `wasmtime` -(or other `wasi:cli` hosts). In Rust terms, it's the equivalent of an application (`bin`) package with -a `main` function, instead of a library crate (`lib`) package. - -### 1. Create a new Rust binary project - -To create a command with cargo, run: - -```sh -cargo new runnable-example -``` - -Unlike library components, this does _not_ have the `--lib` flag (`--bin` is the default for `cargo new`). - -The created Rust source file is called `main.rs` instead of `lib.rs`, and contains a `main` function. - -You can write Rust in this project, just as you normally would, including importing your own or third-party crates. - -> All the crates that make up your project are linked together at build time, and compiled to a _single_ Wasm component. In this case, all the linking is happening at the Rust level: no WITs or component composition is involved. Only if you import Wasm interfaces do WIT and composition come into play. - -### 2. Write the relevant Rust - -The following code can be inserted into `runnable-example/src/main.rs`: - -```rust -pub fn main() { - eprintln!("Hello World!"); -} -``` - -### 3. Build the component - -To build the component, use `cargo`: - -```sh -cargo build --target=wasm32-wasip2 -``` - -The component can also be built in release mode: - -```console -cargo build --target=wasm32-wasip2 --release -``` - -### 4. Run the component with `wasmtime` - -To run your command component: - -```sh -wasmtime run ./target/wasm32-wasip2/debug/runnable-example.wasm -``` - -> [!WARNING] -> If your program prints to standard out or error, you may not see the printed output! -> -> Some versions of `wasmtime` have a bug where they don't flush output streams before exiting. To work -> around this, add a `std::thread::sleep()` with a 10 millisecond delay before exiting `main`. From aad3632a16566baa796f0abf363a3c3c676d6297 Mon Sep 17 00:00:00 2001 From: Victor Adossi Date: Wed, 15 Oct 2025 11:24:36 +0900 Subject: [PATCH 16/35] refactor(pl/rust): remove stdout print bug note --- .../language-support/creating-runnable-components/rust.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/component-model/src/language-support/creating-runnable-components/rust.md b/component-model/src/language-support/creating-runnable-components/rust.md index 3bcbdc3d..4c0a4707 100644 --- a/component-model/src/language-support/creating-runnable-components/rust.md +++ b/component-model/src/language-support/creating-runnable-components/rust.md @@ -57,12 +57,6 @@ To run your command component: wasmtime run ./target/wasm32-wasip2/debug/runnable-example.wasm ``` -> [!WARNING] -> If your program prints to standard out or error, you may not see the printed output! -> -> Some versions of `wasmtime` have a bug where they don't flush output streams before exiting. To work -> around this, add a `std::thread::sleep()` with a 10 millisecond delay before exiting `main`. - ## Enabling a library component to be run via the `wasi:cli/run` interface Any reactor (library-like) component can *also* export the [`run` interface][wasi-cli-iface-run] inside [WASI CLI][wasi-cli], From cc63c63c35ec628f7f87461686194fab4e56c588 Mon Sep 17 00:00:00 2001 From: Victor Adossi Date: Wed, 15 Oct 2025 11:26:08 +0900 Subject: [PATCH 17/35] refactor(pl/rust): remove unneeded note --- .../language-support/importing-and-reusing-components/rust.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/component-model/src/language-support/importing-and-reusing-components/rust.md b/component-model/src/language-support/importing-and-reusing-components/rust.md index 195a0d34..445f7f4f 100644 --- a/component-model/src/language-support/importing-and-reusing-components/rust.md +++ b/component-model/src/language-support/importing-and-reusing-components/rust.md @@ -5,9 +5,6 @@ The world file (`wit/world.wit`) we generated doesn't specify any imports. If your component consumes other components, you can edit the `world.wit` file to import their interfaces. -> [!NOTE] -> This section is about importing custom WIT interfaces from library components. - For example, suppose you have created and built an adder component as explained in the [exporting an interface section](#exporting-an-interface-with-cargo-component) and want to use that component in a calculator component. Here is a partial example world for a calculator that imports the add interface: ```wit From 90e2f5f85d01dcae0995db1dc9d69475bf00d659 Mon Sep 17 00:00:00 2001 From: Victor Adossi Date: Wed, 15 Oct 2025 11:34:48 +0900 Subject: [PATCH 18/35] refactor(pl/rust): clarify use of bindings --- .../importing-and-reusing-components/rust.md | 37 +++++++++++++------ 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/component-model/src/language-support/importing-and-reusing-components/rust.md b/component-model/src/language-support/importing-and-reusing-components/rust.md index 445f7f4f..d672e8d3 100644 --- a/component-model/src/language-support/importing-and-reusing-components/rust.md +++ b/component-model/src/language-support/importing-and-reusing-components/rust.md @@ -5,7 +5,8 @@ The world file (`wit/world.wit`) we generated doesn't specify any imports. If your component consumes other components, you can edit the `world.wit` file to import their interfaces. -For example, suppose you have created and built an adder component as explained in the [exporting an interface section](#exporting-an-interface-with-cargo-component) and want to use that component in a calculator component. Here is a partial example world for a calculator that imports the add interface: +For example, suppose you have created and built the adder component as explained in the earlier tutorials and want to use +that component in a calculator component. Here is a partial example world for a calculator that imports the add interface: ```wit // in the 'calculator' project @@ -18,8 +19,9 @@ interface calculate { } world calculator { - export calculate; import docs:adder/add@0.1.0; + + export calculate; } ``` @@ -39,28 +41,39 @@ at the path that is given, and will pull its contents into the local project und ### Calling the import from Rust -Now the declaration of `add` in the adder's WIT file is visible to the `calculator` project. To invoke the imported `add` interface from the `calculate` implementation: +Now the declaration of `add` in the adder's WIT file is visible to the `calculator` project. + +To invoke the imported `add` interface from the `calculate` implementation: ```rust // src/lib.rs -mod bindings; -use bindings::exports::docs::calculator::calculate::Guest; - -// Bring the imported add function into scope -use bindings::docs::calculator::add::add; +// Generated code that includes both the import stubs for adder functionality +// and the stubs for exports is generated into the `bindings` module below +// +// Note that while wit_bindgen::generate only creates stubs for imports, +// not implementation -- this component will be built with *unsatisfied* +// (but usable) imports (e.g. the add). +mod bindings { + use super::Component; + wit_bindgen::generate!(); + export!(Component); +} +/// The struct from which all implementation will hang struct Component; -impl Guest for Component { +// Implementation of the `docs:calculator/calculate` export +impl bindings::exports::docs::calculator::calculate::Guest for Component { fn eval_expression(expr: String) -> u32 { - // Cleverly parse `expr` into values and operations, and evaluate - // them meticulously. - add(123, 456) + // TODO: Cleverly parse `expr` into values and operations, and evaluate them meticulously. + bindings::docs::calculator::add::add(123, 456) } } ``` +Filling out the implementation of `eval_expression` and actually parsing real expressions is left as an exercise for the reader. + ### Fulfilling the import When you build this using `cargo build`, the `add` interface remains unsatisfied (i.e. imported). From 35503d102f24654ba6b7ce8e6deb524159d27943 Mon Sep 17 00:00:00 2001 From: Victor Adossi Date: Wed, 15 Oct 2025 11:37:33 +0900 Subject: [PATCH 19/35] refactor(pl/rust): improve composition ending notes --- .../importing-and-reusing-components/rust.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/component-model/src/language-support/importing-and-reusing-components/rust.md b/component-model/src/language-support/importing-and-reusing-components/rust.md index d672e8d3..e14c82e2 100644 --- a/component-model/src/language-support/importing-and-reusing-components/rust.md +++ b/component-model/src/language-support/importing-and-reusing-components/rust.md @@ -97,9 +97,9 @@ world root { } ``` -As the import is unfulfilled, the `calculator.wasm` component could not run by itself in its current form. +As the import is unfulfilled, the `calculator.wasm` component could not run by itself in its current form. The next step is to fulfill the `add` import, so that only `calculate` is exported, and the component can be run. -To fulfill the `add` import, so that only `calculate` is exported, you would -need to [compose the `calculator.wasm` with some `adder.wasm` into a single, self-contained component](../../composing-and-distributing/composing.md). +The process of fufilling imports via other component's exports is called "composition". Learn more about how to compose the calculator.wasm +with an adder.wasm into a single, self-contained component in the [component composition guide](../../composing-and-distributing/composing.md). [!NOTE]: # From 9cdab085749abb4ba5d190f6faee53bcd993f871 Mon Sep 17 00:00:00 2001 From: Victor Adossi Date: Wed, 15 Oct 2025 11:38:28 +0900 Subject: [PATCH 20/35] fix(pl/rust): cargo -> wkg --- .../language-support/importing-and-reusing-components/rust.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/component-model/src/language-support/importing-and-reusing-components/rust.md b/component-model/src/language-support/importing-and-reusing-components/rust.md index e14c82e2..029ccf35 100644 --- a/component-model/src/language-support/importing-and-reusing-components/rust.md +++ b/component-model/src/language-support/importing-and-reusing-components/rust.md @@ -27,7 +27,7 @@ world calculator { ### Referencing the package to import -Because the `docs:adder` package is in a different project, we must first tell `cargo` how to find it. To do this, we add a +Because the `docs:adder` package is in a different project, we must first tell `wkg` how to find it. To do this, we add a custom `wkg.toml` to our project: ```toml From 25b3b6b8cfcc3c6c9b1e771f01eb84e36316e3bf Mon Sep 17 00:00:00 2001 From: Victor Adossi Date: Wed, 15 Oct 2025 21:28:38 +0900 Subject: [PATCH 21/35] refactor(lang/js): move advanced sections, rework runnable functions --- component-model/src/SUMMARY.md | 2 + .../src/creating-runnable-components.md | 45 ++- .../src/importing-and-reusing-components.md | 7 +- .../building-a-simple-component/javascript.md | 341 +----------------- .../javascript.md | 28 ++ .../javascript.md | 312 ++++++++++++++++ 6 files changed, 400 insertions(+), 335 deletions(-) create mode 100644 component-model/src/language-support/creating-runnable-components/javascript.md create mode 100644 component-model/src/language-support/importing-and-reusing-components/javascript.md diff --git a/component-model/src/SUMMARY.md b/component-model/src/SUMMARY.md index e7951b33..888eaf34 100644 --- a/component-model/src/SUMMARY.md +++ b/component-model/src/SUMMARY.md @@ -29,8 +29,10 @@ - [Other Languages](./language-support/building-a-simple-component/other-languages.md) - [Importing and reusing components](./importing-and-reusing-components.md) - [Rust](./language-support/importing-and-reusing-components/rust.md) + - [Javascript](./language-support/importing-and-reusing-components/javascript.md) - [Creating runnable components](./creating-runnable-components.md) - [Rust](./language-support/creating-runnable-components/rust.md) + - [Javascript](./language-support/creating-runnable-components/javascript.md) - [Using WIT resources](./using-wit-resources.md) - [Rust](./language-support/using-wit-resources/rust.md) - [Running Components](./running-components.md) diff --git a/component-model/src/creating-runnable-components.md b/component-model/src/creating-runnable-components.md index 06e5c4e0..86cfe002 100644 --- a/component-model/src/creating-runnable-components.md +++ b/component-model/src/creating-runnable-components.md @@ -2,20 +2,55 @@ This section contains language-specific guides on how to create runnable components. +## Running arbitrary functions from reactor (library-like) components + +In practice, any interface that is exported from a WebAssembly component can be run by either: + +- Creating a custom host/component that imports and reuses the functionality +- Using high level generic tooling like `wasmtime run --invoke` + +For example, given a WebAssembly component which satisfies the following WIT: + +```wit +{{#include ../examples/tutorial/wit/adder/world.wit}} +``` + +Use of the exported `add` function inside the `add` interface requires writing a host or other component that is +built to import and use that functionality. This is exemplified by the [`example-host` available in this repo][example-host]. + +Alternatively tooling that works generically over components `wasmtime run --invoke`: + +```sh +wasmtime run --invoke 'add(1, 2)' add.component.wasm +``` + +Wasmtime contains code that can generically interpret exports, convert arguments to WebAssembly arguments, and execute +an existing component dynamically. + +[example-host](https://github.com/bytecodealliance/component-docs/blob/main/component-model/examples/example-host/README.md) + +## Creating components that behave like binaries + +While running arbitrary functions require either a custom host/platform or a dynamic tool like `wasmtime run --invoke`, +components that are treatable as binaries (i.e. a CLI application) can also be built. + At a high level there are at least two ways to create components that are more like binaries than libraries -(i.e. that are easy to run from a tool like `wasmtime`): +(i.e. that are easy to run from a tool like `wasmtime run`): 1. Creating a "command" component -2. Exporting the `wasi:cli/run` interface +2. Exporting the [`wasi:cli/run` interface][wasi-cli-run] This section explores how to do the above in relevant languages. +[wasi-cli-iface-run]: https://github.com/WebAssembly/wasi-cli/tree/main/wit/run.wit + ## Languages This guide is implemented for various languages: -| Language | -|-----------------------------------------------------------------| -| [Rust](./language-support/creating-runnable-components/rust.md) | +| Language | +|-----------------------------------------------------------------------------| +| [Rust](./language-support/creating-runnable-components/rust.md) | +| [Javascript](./language-support/creating-runnable-components/javascript.md) | [docs-wit]: ./design/wit.md diff --git a/component-model/src/importing-and-reusing-components.md b/component-model/src/importing-and-reusing-components.md index 794110be..3af06b94 100644 --- a/component-model/src/importing-and-reusing-components.md +++ b/component-model/src/importing-and-reusing-components.md @@ -19,8 +19,9 @@ The `calculator` component has the following interface: This guide is implemented for various languages: -| Language | -|---------------------------------------------------------------------| -| [Rust](./language-support/importing-and-reusing-components/rust.md) | +| Language | +|---------------------------------------------------------------------------------| +| [Rust](./language-support/importing-and-reusing-components/rust.md) | +| [Javascript](./language-support/importing-and-reusing-components/javascript.md) | [docs-wit]: ./design/wit.md diff --git a/component-model/src/language-support/building-a-simple-component/javascript.md b/component-model/src/language-support/building-a-simple-component/javascript.md index be7c15fb..bb1c699c 100644 --- a/component-model/src/language-support/building-a-simple-component/javascript.md +++ b/component-model/src/language-support/building-a-simple-component/javascript.md @@ -48,6 +48,20 @@ Building a WebAssembly component with JavaScript often consists of: 2. Creating the component by writing JavaScript that satisfies the interface 3. Compiling the interface-compliant JavaScript to WebAssembly +### Building Reactor Components with `jco` + +Reactor components are WebAssembly components that are long-running +and meant to be called repeatedly over time. +Unlike "command" components, which are analogous to executables, +reactor components are analogous to libraries of functionality. + +Components expose their interfaces via [WebAssembly Interface Types][docs-wit], +hand-in-hand with the [Component Model][docs-component-model] +which enables components to use higher-level types interchangeably. + +[docs-wit]: ../../design/wit.md +[docs-component-model]: ../../design/why-component-model.md + ### What is WIT? [WebAssembly Interface Types ("WIT")][docs-wit] is a featureful Interface Definition Language ("IDL") @@ -232,7 +246,6 @@ You should see output similar to the following: > > With this project pulled locally, you also run `npm run transpile`, which outputs to `dist/transpiled`. - Thanks to `jco` transpilation, you can import the resulting `dist/transpiled/adder.js` file and run it from any JavaScript application using a runtime that supports the [core WebAssembly specification][core-wasm] as implemented for JavaScript. @@ -280,332 +293,6 @@ With `jco transpile`, any WebAssembly binary (compiled from any language) can be [wasm-core-module]: https://webassembly.github.io/spec/core/binary/modules.html [core-wasm]: https://webassembly.github.io/spec/core/ -## Building Reactor Components with `jco` - -Reactor components are WebAssembly components that are long-running -and meant to be called repeatedly over time. -Unlike "command" components, which are analogous to executables, -reactor components are analogous to libraries of functionality. - -Components expose their interfaces via [WebAssembly Interface Types][docs-wit], -hand-in-hand with the [Component Model][docs-component-model] -which enables components to use higher-level types interchangeably. - -[docs-wit]: ../../design/wit.md -[docs-component-model]: ../../design/why-component-model.md - -### Exporting WIT Interfaces with `jco` - -Packaging reusable functionality into WebAssembly components isn't useful -if we have no way to *expose* that functionality. -This section offers a slightly deeper dive into the usage of WIT in WebAssembly components. - -As in the previous example, `export`ing WIT interfaces for other components (or a WebAssembly host) to use -is fundamental to developing WebAssembly programs. - -Let's examine a [`jco` example project called `string-reverse`][jco-examples-string-reverse] -that exposes functionality for reversing a string. - -To build a project like `string-reverse` from the ground up, first we'd start with a WIT like the following. -In a new directory called `string-reverse`, paste this code into a file called `wit/component.wit`: - -```wit -{{#include ../../../examples/tutorial/js/string-reverse/component.wit}} -``` - -As a slightly deeper crash course on [WIT][docs-wit], here's what the above code describes: - -- We've defined a namespace called `example`. -- We've defined a package called `string-reverse` inside the `example` namespace. -- This WIT file corresponds to version `0.1.0` of the `example:string-reverse` package. -- We've defined an interface called `reverse` that contains *one* function called `reverse-string`. -- We specify that the `reverse` interface has existed *since* the `0.1.0` version. -- The `reverse-string` function (whose fully qualified name is - `example:string-reverse/reverse.reverse-string`) takes a string and returns a string. -- We've defined a `world` called `string-reverse` that exports the functionality - provided by the `reverse` interface. - -> [!WARNING] -> How do we *know* that `reverse` actually reverses a string? -> -> Unfortunately, that problem is not really solvable at this level—this is between you -> and the writer of the component that implements the WIT interface. -> -> Of course, with WebAssembly, you *can* enforce static checks if you're so inclined, *before* you run any given binary. - -OK now let's see what the JS code looks like to *implement* the `component` world. -Paste the following code into a file called `string-reverse.mjs`: - -```mjs -{{#include ../../../examples/tutorial/js/string-reverse/string-reverse.mjs}} -``` - -> This code uses `split()` to convert the string into an array of characters, -> reverses the array, and uses `join()` to convert the array back to a string, -> since JavaScript has no built-in string reverse method. - -> [!NOTE] -> To view the full code listing along with instructions, see the [`examples/tutorials/jco/string-reverse` folder][jco-examples-string-reverse]. - -To use `jco` to compile this component, you can run the following inside your `string-reverse` directory: - -```console -npx jco componentize \ - --wit wit/component.wit \ - --world-name string-reverse \ - --out string-reverse.wasm \ - --disable=all \ - string-reverse.mjs -``` - -You should see output like the following: - -``` -OK Successfully written string-reverse.wasm. -``` - -> [!NOTE] -> As with the previous example, we're not using any of the advanced [WebAssembly System Interface][wasi] features, -> so we `--disable` all of them. -> -> Rather than typing out the `jco componentize` command manually, you can also run -> the build command with `npm run build` if you use the code from -> [the `string-reverse` folder][string-reverse-package-json]. - - -Now that we have a WebAssembly binary, we can *also* use `jco` to run it in a native JavaScript context by *transpiling* -the WebAssembly binary (which could have come from anywhere!) to a JavaScript module. - -```console -npx jco transpile string-reverse.wasm -o dist/transpiled -``` - -You should see output that looks like this: - -``` - Transpiled JS Component Files: - - - dist/transpiled/interfaces/example-string-reverse-reverse.d.ts 0.1 KiB - - dist/transpiled/string-reverse.core.wasm 10.1 MiB - - dist/transpiled/string-reverse.d.ts 0.15 KiB - - dist/transpiled/string-reverse.js 2.55 KiB -``` - -> [!TIP] -> A gentle reminder: transpilation *does* produce a [TypeScript declaration file][ts-decl-file], -> for use in TypeScript projects. - -Now that we have a transpiled module, we can run it from any JavaScript context -that supports core WebAssembly (whether Node.js or the browser). - -For Node.js, we can use code like this. -Paste the following code into a file called `run.js` in your `string-reverse` directory: - -```mjs -{{#include ../../../examples/tutorial/js/string-reverse/run.js}} -``` - -> [!NOTE] -> In the `jco` example project, you can run `npm run transpiled-js` to build the existing code. - -As before, we also need a `package.json` file: - -```json -{{#include ../../../examples/tutorial/js/string-reverse/package.json}} -``` - -Then run: - -```bash -node run.js -``` - -Assuming you have the `dist/transpiled` folder populated (by running `jco transpile` in the previous step), -you should see output like the following: - -``` -reverseString('!dlrow olleh') = hello world! -``` - -While it's somewhat redundant in this context, what we've done from NodeJS demonstrates the usefulness of WebAssembly and the `jco` toolchain. -With the help of `jco`, we have: - -- Compiled JavaScript to a WebAssembly module (`jco compile`), adhering to an interface defined via WIT -- Converted the compiled WebAssembly module (which could be from *any* language) to a module that can be used from any compliant JS runtime (`jco transpile`) -- Run the transpiled WebAssembly component from a JavaScript native runtime (NodeJS) - -[repo]: https://github.com/bytecodealliance/component-docs -[jco-examples-string-reverse]: https://github.com/bytecodealliance/jco/tree/main/examples/components/string-reverse -[ts-decl-file]: https://www.typescriptlang.org/docs/handbook/declaration-files/deep-dive.html#declaration-file-theory-a-deep-dive -[string-reverse-package-json]: https://github.com/bytecodealliance/jco/blob/main/examples/components/string-reverse/package.json#L6 - -### Advanced: Importing and Reusing WIT Interfaces via Composition - -Just as `export`ing functionality is core to building useful WebAssembly components, -`import`ing and reusing functionality is key to using the strengths of WebAssembly. - -Restated, **WIT and the Component Model enable WebAssembly to *compose***. This means we can build on top of functionality -that already exists and `export` *new* functionality that depends on existing functionality. - -Let's say in addition to eversing the string (in the previous example), -we want to build shared functionality that *also* upper-cases the text it receives. - -We can reuse the reversing functionality *and* export a new interface which enables us to reverse and upper-case. - -Let's examine a [`jco` example project called `string-reverse-upper`][jco-examples-string-reverse-upper] that exposes -functionality for reversing *and* upper-casing a string. - -Here's the WIT one might write to enable this functionality: - -```wit -{{#include ../../../examples/tutorial/js/string-reverse-upper/component.wit}} -``` - -This time, the `world` named `revup` that we are building *relies* on the interface `reverse` -in the package `string-reverse` from the namespace `example`. - -We can make use of *any* WebAssembly component that matches that interface, -as long as we *compose* their functionality with the component that implements the `revup` world. - -The `revup` world `import`s (and makes use) of `reverse` in order to `export` (provide) the `reversed-upper` interface, -which contains the `reverse-and-uppercase` function (in JavaScript, `reverseAndUppercase`). - -> [!NOTE] -> Functionality is imported via the `interface`, *not* the `world`. -> `world`s can be included/used, but the syntax is slightly different for that. - -The JavaScript to make this work ([`string-reverse-upper.mjs` in `jco/examples`][string-reverse-upper-mjs]) -looks like this: - -```js -{{#include ../../../examples/tutorial/js/string-reverse-upper/string-reverse-upper.mjs}} -``` - -If we place the above WIT file in the `wit` subdirectory, we also need to create a -`wit/deps` subdirectory and copy `../string-reverse/wit/component.wit` into `wit/deps`. - -We can build the component with `jco componentize`: - -```console -npx jco componentize \ - string-reverse-upper.mjs \ - --wit wit/ \ - --world-name revup \ - --out string-reverse-upper.incomplete.wasm \ - --disable=all -``` - -> If you get an error message, verify that your `wit/component.wit` file -> begins with `package example:string-reverse-upper@0.1.0;`, and that your `wit/deps/` directory -> contains a file beginning with `package example:string-reverse@0.1.0;`. -> In general, your main package should be at the top level of your `wit` directory, -> and any dependencies should be in a subdirectory of that directory (normally `deps`). - -Although we've successfully built a WebAssembly component, unlike with the other examples, -ours is *not yet complete*. - -We can see that if we print the WIT of the generated component by running `jco wit`: - -```console -npx jco wit string-reverse-upper.incomplete.wasm -``` - -You should see output like the following: - -``` -package root:component; - -world root { - import example:string-reverse/reverse@0.1.0; - - export example:string-reverse-upper/reversed-upper@0.1.0; -} -``` - -This tells us that the component still has *unfulfilled `import`s*: -we *use* the `reverseString` function that's in `reverse` as if it exists, -but it's not yet a real part of the WebAssembly component (hence we've named it `.incomplete.wasm`). - -To compose the two components we built earlier (`string-reverse-upper/string-reverse-upper.incomplete.wasm` and `string-reverse/string-reverse.wasm`), -we'll need the [WebAssembly Composition tool (`wac`)][wac]. We can use `wac plug`: - -```console -wac plug \ - -o string-reverse-upper.wasm \ - --plug ../string-reverse/string-reverse.wasm \ - string-reverse-upper.incomplete.wasm -``` - -> [!NOTE] -> You can also run this step with `npm run compose`, if using the full project from the `jco` repository. - -A new component `string-reverse-upper.wasm` should now be present, which is a "complete" component. -We can check the output of `npx jco wit` to ensure that all the imports are satisfied: - -```sh -npx jco wit string-reverse-upper.wasm -``` - -You should see output like the following: - -```wit -package root:component; - -world root { - export example:string-reverse-upper/reversed-upper@0.1.0; -} -``` - -It's as-if we never imported any functionality at all—the functionality present in `string-reverse.wasm` -has been *merged into* `string-reverse-upper.wasm`, and it now simply `export`s the advanced functionality. - -We can run this completed component with in any WebAssembly-capable native JavaScript environment -by using the transpiled result: - -```console -npx jco transpile string-reverse-upper.wasm -o dist/transpiled -``` - -> [!NOTE] -> In the example project, you can run `npm run transpile` instead, -> which will also change the extension on `dist/transpiled/string-reverse-upper.js` to `.mjs`. - -You should see output like the following: - -``` - Transpiled JS Component Files: - - - dist/transpiled/interfaces/example-string-reverse-upper-reversed-upper.d.ts 0.12 KiB - - dist/transpiled/string-reverse-upper.core.wasm 10.1 MiB - - dist/transpiled/string-reverse-upper.core2.wasm 10.1 MiB - - dist/transpiled/string-reverse-upper.d.ts 0.19 KiB - - dist/transpiled/string-reverse-upper.js 6.13 KiB -``` - -> [!TIP] -> Notice that there are *two* core WebAssembly files. That's because two core WebAssembly modules were involved -> in creating the ultimate functionality we needed. - -To run the transpiled component, we can write code like the following: - -```js -{{#include ../../../examples/tutorial/js/string-reverse-upper/run.js}} -``` - -> [!NOTE] -> In the [`jco` example project][jco-examples-string-reverse-upper], -> you can run `npm run transpiled-js`. - -You should see output like the following: - -``` -reverseAndUppercase('!dlroW olleH') = HELLO WORLD! -``` - -[wac]: https://github.com/bytecodealliance/wac -[jco-examples-string-reverse-upper]: https://github.com/bytecodealliance/jco/tree/main/examples/components/string-reverse-upper -[string-reverse-upper-mjs]: https://github.com/bytecodealliance/jco/blob/main/examples/components/string-reverse-upper/string-reverse-upper.mjs - [!TIP]: # [!NOTE]: # [!WARNING]: # diff --git a/component-model/src/language-support/creating-runnable-components/javascript.md b/component-model/src/language-support/creating-runnable-components/javascript.md new file mode 100644 index 00000000..6055a24e --- /dev/null +++ b/component-model/src/language-support/creating-runnable-components/javascript.md @@ -0,0 +1,28 @@ +# Creating Runnable Components (Javascript) + +## Exporting `wasi:cli/run` to create + +Components created with `jco` can export the `wasi:cli/run` interface, similarly to WebAssembly components written in other languages. + +Exporting the `wasi:cli/run` interfaces enables ecosystem tooling to interoperate with (and run) the component you've built + (e.g. `wasmtime run`). Components that conform to `wasi:cli/run` can be very concise. + +For example: + +```js +export const run = { + run() { + console.log("Hello World!"); + } +} +``` + +The above component can be made recognizable as "runnable" to `wasi:cli`-aware tooling with the following WIT: + +```wit +package runnable:js-component; + +world component { + export wasi:cli/run@0.2.4; +} +``` diff --git a/component-model/src/language-support/importing-and-reusing-components/javascript.md b/component-model/src/language-support/importing-and-reusing-components/javascript.md new file mode 100644 index 00000000..5fb31826 --- /dev/null +++ b/component-model/src/language-support/importing-and-reusing-components/javascript.md @@ -0,0 +1,312 @@ +# Importing and Reusing components (Javascript) + +## Composing existing code for use in a Javascript component + +Just as `export`ing functionality is core to building useful WebAssembly components, +`import`ing and reusing functionality is key to using the strengths of WebAssembly. + +Restated, **WIT and the Component Model enable WebAssembly to *compose***. This means we can build on top of functionality +that already exists and `export` *new* functionality that depends on existing functionality. + +Let's say in addition to eversing the string (in the previous example), +we want to build shared functionality that *also* upper-cases the text it receives. + +We can reuse the reversing functionality *and* export a new interface which enables us to reverse and upper-case. + +Let's examine a [`jco` example project called `string-reverse-upper`][jco-examples-string-reverse-upper] that exposes +functionality for reversing *and* upper-casing a string. + +Here's the WIT one might write to enable this functionality: + +```wit +{{#include ../../../examples/tutorial/js/string-reverse-upper/component.wit}} +``` + +This time, the `world` named `revup` that we are building *relies* on the interface `reverse` +in the package `string-reverse` from the namespace `example`. + +We can make use of *any* WebAssembly component that matches that interface, +as long as we *compose* their functionality with the component that implements the `revup` world. + +The `revup` world `import`s (and makes use) of `reverse` in order to `export` (provide) the `reversed-upper` interface, +which contains the `reverse-and-uppercase` function (in JavaScript, `reverseAndUppercase`). + +> [!NOTE] +> Functionality is imported via the `interface`, *not* the `world`. +> `world`s can be included/used, but the syntax is slightly different for that. + +The JavaScript to make this work ([`string-reverse-upper.mjs` in `jco/examples`][string-reverse-upper-mjs]) +looks like this: + +```js +{{#include ../../../examples/tutorial/js/string-reverse-upper/string-reverse-upper.mjs}} +``` + +If we place the above WIT file in the `wit` subdirectory, we also need to create a +`wit/deps` subdirectory and copy `../string-reverse/wit/component.wit` into `wit/deps`. + +We can build the component with `jco componentize`: + +```console +npx jco componentize \ + string-reverse-upper.mjs \ + --wit wit/ \ + --world-name revup \ + --out string-reverse-upper.incomplete.wasm \ + --disable=all +``` + +> If you get an error message, verify that your `wit/component.wit` file +> begins with `package example:string-reverse-upper@0.1.0;`, and that your `wit/deps/` directory +> contains a file beginning with `package example:string-reverse@0.1.0;`. +> In general, your main package should be at the top level of your `wit` directory, +> and any dependencies should be in a subdirectory of that directory (normally `deps`). + +Although we've successfully built a WebAssembly component, unlike with the other examples, +ours is *not yet complete*. + +We can see that if we print the WIT of the generated component by running `jco wit`: + +```console +npx jco wit string-reverse-upper.incomplete.wasm +``` + +You should see output like the following: + +``` +package root:component; + +world root { + import example:string-reverse/reverse@0.1.0; + + export example:string-reverse-upper/reversed-upper@0.1.0; +} +``` + +This tells us that the component still has *unfulfilled `import`s*: +we *use* the `reverseString` function that's in `reverse` as if it exists, +but it's not yet a real part of the WebAssembly component (hence we've named it `.incomplete.wasm`). + +To compose the two components we built earlier (`string-reverse-upper/string-reverse-upper.incomplete.wasm` and `string-reverse/string-reverse.wasm`), +we'll need the [WebAssembly Composition tool (`wac`)][wac]. We can use `wac plug`: + +```console +wac plug \ + -o string-reverse-upper.wasm \ + --plug ../string-reverse/string-reverse.wasm \ + string-reverse-upper.incomplete.wasm +``` + +> [!NOTE] +> You can also run this step with `npm run compose`, if using the full project from the `jco` repository. + +A new component `string-reverse-upper.wasm` should now be present, which is a "complete" component. +We can check the output of `npx jco wit` to ensure that all the imports are satisfied: + +```sh +npx jco wit string-reverse-upper.wasm +``` + +You should see output like the following: + +```wit +package root:component; + +world root { + export example:string-reverse-upper/reversed-upper@0.1.0; +} +``` + +It's as-if we never imported any functionality at all—the functionality present in `string-reverse.wasm` +has been *merged into* `string-reverse-upper.wasm`, and it now simply `export`s the advanced functionality. + +We can run this completed component with in any WebAssembly-capable native JavaScript environment +by using the transpiled result: + +```console +npx jco transpile string-reverse-upper.wasm -o dist/transpiled +``` + +> [!NOTE] +> In the example project, you can run `npm run transpile` instead, +> which will also change the extension on `dist/transpiled/string-reverse-upper.js` to `.mjs`. + +You should see output like the following: + +``` + Transpiled JS Component Files: + + - dist/transpiled/interfaces/example-string-reverse-upper-reversed-upper.d.ts 0.12 KiB + - dist/transpiled/string-reverse-upper.core.wasm 10.1 MiB + - dist/transpiled/string-reverse-upper.core2.wasm 10.1 MiB + - dist/transpiled/string-reverse-upper.d.ts 0.19 KiB + - dist/transpiled/string-reverse-upper.js 6.13 KiB +``` + +> [!TIP] +> Notice that there are *two* core WebAssembly files. That's because two core WebAssembly modules were involved +> in creating the ultimate functionality we needed. + +To run the transpiled component, we can write code like the following: + +```js +{{#include ../../../examples/tutorial/js/string-reverse-upper/run.js}} +``` + +> [!NOTE] +> In the [`jco` example project][jco-examples-string-reverse-upper], +> you can run `npm run transpiled-js`. + +You should see output like the following: + +``` +reverseAndUppercase('!dlroW olleH') = HELLO WORLD! +``` + +[wac]: https://github.com/bytecodealliance/wac +[jco-examples-string-reverse-upper]: https://github.com/bytecodealliance/jco/tree/main/examples/components/string-reverse-upper +[string-reverse-upper-mjs]: https://github.com/bytecodealliance/jco/blob/main/examples/components/string-reverse-upper/string-reverse-upper.mjs + +## Using `jco transpile` to run components from Javsacript + +Packaging reusable functionality into WebAssembly components isn't useful +if we have no way to *expose* that functionality. +This section offers a slightly deeper dive into the usage of WIT in WebAssembly components. + +`export`ing WIT interfaces for other components (or a WebAssembly host) to use +is fundamental to developing WebAssembly programs. + +Let's examine a [`jco` example project called `string-reverse`][jco-examples-string-reverse] +that exposes functionality for reversing a string. + +To build a project like `string-reverse` from the ground up, first we'd start with a WIT like the following. +In a new directory called `string-reverse`, paste this code into a file called `wit/component.wit`: + +```wit +{{#include ../../../examples/tutorial/js/string-reverse/component.wit}} +``` + +As a slightly deeper crash course on [WIT][docs-wit], here's what the above code describes: + +- We've defined a namespace called `example`. +- We've defined a package called `string-reverse` inside the `example` namespace. +- This WIT file corresponds to version `0.1.0` of the `example:string-reverse` package. +- We've defined an interface called `reverse` that contains *one* function called `reverse-string`. +- We specify that the `reverse` interface has existed *since* the `0.1.0` version. +- The `reverse-string` function (whose fully qualified name is + `example:string-reverse/reverse.reverse-string`) takes a string and returns a string. +- We've defined a `world` called `string-reverse` that exports the functionality + provided by the `reverse` interface. + +> [!WARNING] +> How do we *know* that `reverse` actually reverses a string? +> +> Unfortunately, that problem is not really solvable at this level—this is between you +> and the writer of the component that implements the WIT interface. +> +> Of course, with WebAssembly, you *can* enforce static checks if you're so inclined, *before* you run any given binary. + +OK now let's see what the JS code looks like to *implement* the `component` world. +Paste the following code into a file called `string-reverse.mjs`: + +```mjs +{{#include ../../../examples/tutorial/js/string-reverse/string-reverse.mjs}} +``` + +> This code uses `split()` to convert the string into an array of characters, +> reverses the array, and uses `join()` to convert the array back to a string, +> since JavaScript has no built-in string reverse method. + +> [!NOTE] +> To view the full code listing along with instructions, see the [`examples/tutorials/jco/string-reverse` folder][jco-examples-string-reverse]. + +To use `jco` to compile this component, you can run the following inside your `string-reverse` directory: + +```console +npx jco componentize \ + --wit wit/component.wit \ + --world-name string-reverse \ + --out string-reverse.wasm \ + --disable=all \ + string-reverse.mjs +``` + +You should see output like the following: + +``` +OK Successfully written string-reverse.wasm. +``` + +> [!NOTE] +> As with the previous example, we're not using any of the advanced [WebAssembly System Interface][wasi] features, +> so we `--disable` all of them. +> +> Rather than typing out the `jco componentize` command manually, you can also run +> the build command with `npm run build` if you use the code from +> [the `string-reverse` folder][string-reverse-package-json]. + +Now that we have a WebAssembly binary, we can *also* use `jco` to run it in a native JavaScript context by *transpiling* +the WebAssembly binary (which could have come from anywhere!) to a JavaScript module. + +```console +npx jco transpile string-reverse.wasm -o dist/transpiled +``` + +You should see output that looks like this: + +``` + Transpiled JS Component Files: + + - dist/transpiled/interfaces/example-string-reverse-reverse.d.ts 0.1 KiB + - dist/transpiled/string-reverse.core.wasm 10.1 MiB + - dist/transpiled/string-reverse.d.ts 0.15 KiB + - dist/transpiled/string-reverse.js 2.55 KiB +``` + +> [!TIP] +> A gentle reminder: transpilation *does* produce a [TypeScript declaration file][ts-decl-file], +> for use in TypeScript projects. + +Now that we have a transpiled module, we can run it from any JavaScript context +that supports core WebAssembly (whether Node.js or the browser). + +For Node.js, we can use code like this. +Paste the following code into a file called `run.js` in your `string-reverse` directory: + +```mjs +{{#include ../../../examples/tutorial/js/string-reverse/run.js}} +``` + +> [!NOTE] +> In the `jco` example project, you can run `npm run transpiled-js` to build the existing code. + +As before, we also need a `package.json` file: + +```json +{{#include ../../../examples/tutorial/js/string-reverse/package.json}} +``` + +Then run: + +```bash +node run.js +``` + +Assuming you have the `dist/transpiled` folder populated (by running `jco transpile` in the previous step), +you should see output like the following: + +``` +reverseString('!dlrow olleh') = hello world! +``` + +While it's somewhat redundant in this context, what we've done from NodeJS demonstrates the usefulness of WebAssembly and the `jco` toolchain. +With the help of `jco`, we have: + +- Compiled JavaScript to a WebAssembly module (`jco compile`), adhering to an interface defined via WIT +- Converted the compiled WebAssembly module (which could be from *any* language) to a module that can be used from any compliant JS runtime (`jco transpile`) +- Run the transpiled WebAssembly component from a JavaScript native runtime (NodeJS) + +[repo]: https://github.com/bytecodealliance/component-docs +[jco-examples-string-reverse]: https://github.com/bytecodealliance/jco/tree/main/examples/components/string-reverse +[ts-decl-file]: https://www.typescriptlang.org/docs/handbook/declaration-files/deep-dive.html#declaration-file-theory-a-deep-dive +[string-reverse-package-json]: https://github.com/bytecodealliance/jco/blob/main/examples/components/string-reverse/package.json#L6 From a30658410e333e8545446e810e40222396a58ac5 Mon Sep 17 00:00:00 2001 From: Victor Adossi Date: Wed, 15 Oct 2025 21:32:02 +0900 Subject: [PATCH 22/35] refactor(lang/rust): add target to cargo build cmd --- component-model/examples/tutorial/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/component-model/examples/tutorial/README.md b/component-model/examples/tutorial/README.md index 314603d1..8cbb43a3 100644 --- a/component-model/examples/tutorial/README.md +++ b/component-model/examples/tutorial/README.md @@ -45,9 +45,9 @@ To expand the exercise to add more components, add another operator world, expan Use [`wac`](https://github.com/bytecodealliance/wac) to build and compose the calculator component. ```sh -(cd calculator && cargo build --release) -(cd adder && cargo build --release) -(cd command && cargo build --release) +(cd calculator && cargo build --target=wasm32-wasip2 --release) +(cd adder && cargo build --target=wasm32-wasip2 --release) +(cd command && cargo build --target=wasm32-wasip2 --release) wac plug calculator/target/wasm32-wasip1/release/calculator.wasm --plug adder/target/wasm32-wasip1/release/adder.wasm -o composed.wasm wac plug command/target/wasm32-wasip1/release/command.wasm --plug composed.wasm -o final.wasm ``` From 9b4e288ae38e20b344ac44ac557e8d0c1fdff9db Mon Sep 17 00:00:00 2001 From: Victor Adossi Date: Wed, 15 Oct 2025 21:33:03 +0900 Subject: [PATCH 23/35] fix(lang/rust): target wasip1 -> wasip2 --- component-model/examples/tutorial/README.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/component-model/examples/tutorial/README.md b/component-model/examples/tutorial/README.md index 8cbb43a3..86f5083e 100644 --- a/component-model/examples/tutorial/README.md +++ b/component-model/examples/tutorial/README.md @@ -48,8 +48,8 @@ Use [`wac`](https://github.com/bytecodealliance/wac) to build and compose the ca (cd calculator && cargo build --target=wasm32-wasip2 --release) (cd adder && cargo build --target=wasm32-wasip2 --release) (cd command && cargo build --target=wasm32-wasip2 --release) -wac plug calculator/target/wasm32-wasip1/release/calculator.wasm --plug adder/target/wasm32-wasip1/release/adder.wasm -o composed.wasm -wac plug command/target/wasm32-wasip1/release/command.wasm --plug composed.wasm -o final.wasm +wac plug calculator/target/wasm32-wasip2/release/calculator.wasm --plug adder/target/wasm32-wasip2/release/adder.wasm -o composed.wasm +wac plug command/target/wasm32-wasip2/release/command.wasm --plug composed.wasm -o final.wasm ``` Now, run the component with Wasmtime: @@ -67,9 +67,9 @@ wasmtime run final.wasm 1 2 add ```sh mkdir -p deps/docs -cp adder/target/wasm32-wasip1/release/adder.wasm deps/docs/adder-impl.wasm -cp calculator/target/wasm32-wasip1/release/calculator.wasm deps/docs/calculator-impl.wasm -cp command/target/wasm32-wasip1/release/command.wasm deps/docs/command-impl.wasm +cp adder/target/wasm32-wasip2/release/adder.wasm deps/docs/adder-impl.wasm +cp calculator/target/wasm32-wasip2/release/calculator.wasm deps/docs/calculator-impl.wasm +cp command/target/wasm32-wasip2/release/command.wasm deps/docs/command-impl.wasm ``` Now we are ready to construct a WAC file to define our composition. Ours instantiates our three components, declaring @@ -84,12 +84,12 @@ package example:composition; let adder-instance = new docs:adder-impl { }; // Instantiate the calculator-impl component that implements the calculator world. -// In the `new` expression, specify the source of the `add` import to be `adder-instance`'s `add` export. +// In the `new` expression, specify the source of the `add` import to be `adder-instance`'s `add` export. let calculator-instance = new docs:calculator-impl { add: adder-instance.add }; // Instantiate a command-impl component that implements the app world. -// The command component might import other interfaces, such as WASI interfaces, but we want to leave -// those as imports in the final component, so supply `...` to allow those other imports to remain unresolved. +// The command component might import other interfaces, such as WASI interfaces, but we want to leave +// those as imports in the final component, so supply `...` to allow those other imports to remain unresolved. // The command's exports (in this case, `wasi:cli/run`) remain unaffected in the resulting instance. let command-instance = new docs:command-impl { calculate: calculator-instance.calculate,... }; @@ -101,12 +101,12 @@ export command-instance["wasi:cli/run@0.2.0"]; Now, perform your composition by passing the WAC file to `wac compose`. ```sh -wac compose composition.wac -o final.wasm +wac compose composition.wac -o final.wasm ``` > Note, instead of moving all the components to a `deps/docs` directory, you can pass the paths to the components inline > ```sh -> wac compose --dep docs:adder-impl=./adder/target/wasm32-wasip1/release/adder.wasm --dep docs:calculator-impl=./calculator/target/wasm32-wasip1/release/calculator.wasm --dep docs:command-impl=./command/target/wasm32-wasip1/release/command.wasm -o final.wasm composition.wac +> wac compose --dep docs:adder-impl=./adder/target/wasm32-wasip2/release/adder.wasm --dep docs:calculator-impl=./calculator/target/wasm32-wasip2/release/calculator.wasm --dep docs:command-impl=./command/target/wasm32-wasip2/release/command.wasm -o final.wasm composition.wac > ``` Run the component with Wasmtime: From 5dd68eb7c584a6f4fdab0b0e67bc39aa76b1ba3b Mon Sep 17 00:00:00 2001 From: Victor Adossi Date: Wed, 15 Oct 2025 21:34:57 +0900 Subject: [PATCH 24/35] fix(lang/rust): remove unneeded dead_code allow annotation --- component-model/examples/tutorial/adder/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/component-model/examples/tutorial/adder/src/lib.rs b/component-model/examples/tutorial/adder/src/lib.rs index fd025cc2..1b098fa8 100644 --- a/component-model/examples/tutorial/adder/src/lib.rs +++ b/component-model/examples/tutorial/adder/src/lib.rs @@ -20,7 +20,6 @@ mod bindings { /// Struct off of which the implementation will hang /// /// The name of this struct is not significant. -#[allow(dead_code)] struct AdderComponent; impl bindings::exports::docs::adder::add::Guest for AdderComponent { From 8c36f77b96d2a1f4f3e99b5195812776ef41e805 Mon Sep 17 00:00:00 2001 From: Victor Adossi Date: Wed, 15 Oct 2025 21:50:44 +0900 Subject: [PATCH 25/35] feat: add generic guides for other languages --- component-model/src/SUMMARY.md | 2 ++ .../src/creating-runnable-components.md | 9 ++++--- .../src/importing-and-reusing-components.md | 9 ++++--- .../other-languages.md | 20 +++++++++++++++ .../other-languages.md | 25 +++++++++++++++++++ 5 files changed, 57 insertions(+), 8 deletions(-) create mode 100644 component-model/src/language-support/creating-runnable-components/other-languages.md create mode 100644 component-model/src/language-support/importing-and-reusing-components/other-languages.md diff --git a/component-model/src/SUMMARY.md b/component-model/src/SUMMARY.md index 888eaf34..7601aa42 100644 --- a/component-model/src/SUMMARY.md +++ b/component-model/src/SUMMARY.md @@ -30,9 +30,11 @@ - [Importing and reusing components](./importing-and-reusing-components.md) - [Rust](./language-support/importing-and-reusing-components/rust.md) - [Javascript](./language-support/importing-and-reusing-components/javascript.md) + - [Other Languages](./language-support/importing-and-reusing-components/other-languages.md) - [Creating runnable components](./creating-runnable-components.md) - [Rust](./language-support/creating-runnable-components/rust.md) - [Javascript](./language-support/creating-runnable-components/javascript.md) + - [Other languages](./language-support/creating-runnable-components/other-languages.md) - [Using WIT resources](./using-wit-resources.md) - [Rust](./language-support/using-wit-resources/rust.md) - [Running Components](./running-components.md) diff --git a/component-model/src/creating-runnable-components.md b/component-model/src/creating-runnable-components.md index 86cfe002..25b57554 100644 --- a/component-model/src/creating-runnable-components.md +++ b/component-model/src/creating-runnable-components.md @@ -48,9 +48,10 @@ This section explores how to do the above in relevant languages. This guide is implemented for various languages: -| Language | -|-----------------------------------------------------------------------------| -| [Rust](./language-support/creating-runnable-components/rust.md) | -| [Javascript](./language-support/creating-runnable-components/javascript.md) | +| Language | +|---------------------------------------------------------------------------------------| +| [Rust](./language-support/creating-runnable-components/rust.md) | +| [Javascript](./language-support/creating-runnable-components/javascript.md) | +| [Other Languages](./language-support/creating-runnable-components/other-languages.md) | [docs-wit]: ./design/wit.md diff --git a/component-model/src/importing-and-reusing-components.md b/component-model/src/importing-and-reusing-components.md index 3af06b94..1a32e332 100644 --- a/component-model/src/importing-and-reusing-components.md +++ b/component-model/src/importing-and-reusing-components.md @@ -19,9 +19,10 @@ The `calculator` component has the following interface: This guide is implemented for various languages: -| Language | -|---------------------------------------------------------------------------------| -| [Rust](./language-support/importing-and-reusing-components/rust.md) | -| [Javascript](./language-support/importing-and-reusing-components/javascript.md) | +| Language | +|-------------------------------------------------------------------------------------------| +| [Rust](./language-support/importing-and-reusing-components/rust.md) | +| [Javascript](./language-support/importing-and-reusing-components/javascript.md) | +| [Other languages](./language-support/importing-and-reusing-components/other-languages.md) | [docs-wit]: ./design/wit.md diff --git a/component-model/src/language-support/creating-runnable-components/other-languages.md b/component-model/src/language-support/creating-runnable-components/other-languages.md new file mode 100644 index 00000000..7b19780d --- /dev/null +++ b/component-model/src/language-support/creating-runnable-components/other-languages.md @@ -0,0 +1,20 @@ +# Creating Runnable Components (Other Languages) + +Just because a given programming language is not listed in this guide does not +mean it is impossible to create runnable WebAssembly components with it. + +For languages not listed in this guide, it is often possible to create runnable components by +following the main principles of the other guides, using the help of the available WebAssembly +toolchain. + +Generally, WebAssembly toolchains in the language in question may contain a way to: + +1. Create a WebAssembly component with the `_start` export (a "command" compnent) +2. Create a component that exports the `wasi:cli/run` interface + +## Adding a New Language to the Guide + +Know of a language guide we should add to this guide? Create a PR to this [repository][repo-pr] that adds +the new language guide (similar to others in this section). + +[repo-pr]: https://github.com/bytecodealliance/component-docs/pulls diff --git a/component-model/src/language-support/importing-and-reusing-components/other-languages.md b/component-model/src/language-support/importing-and-reusing-components/other-languages.md new file mode 100644 index 00000000..563a6f08 --- /dev/null +++ b/component-model/src/language-support/importing-and-reusing-components/other-languages.md @@ -0,0 +1,25 @@ +# Importing and Reusing components (Other Languages) + +Just because a given programming language is not listed in this guide does not +mean it is impossible to import and reuse WebAssembly components with it. + +For languages not listed in this guide, it is often possible to import and reuse WebAssembly components +by following the main principles of the other guides where applicable, using the help of the local +WebAssembly toolchain. + +Generally, WebAssembly toolchains in the language in question should contain a way to: + +1. Create components that import other components +2. Create host/platforms that can load and run components +3. Compose together components (possibly during build time) + +Note that generic tooling like [`wac`][wac] can be used to compose components together, regardless of language. + +[wac]: https://github.com/bytecodealliance/wac + +## Adding a New Language to the Guide + +Know of a language guide we should add to this guide? Create a PR to this [repository][repo-pr] that adds +the new language guide (similar to others in this section). + +[repo-pr]: https://github.com/bytecodealliance/component-docs/pulls From 8319b4d6ea9bfc5ba61d458c3c155a54ad45f092 Mon Sep 17 00:00:00 2001 From: Victor Adossi Date: Wed, 15 Oct 2025 21:57:28 +0900 Subject: [PATCH 26/35] chore: update wording on creating runnable components section --- component-model/src/creating-runnable-components.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/component-model/src/creating-runnable-components.md b/component-model/src/creating-runnable-components.md index 25b57554..f89971fa 100644 --- a/component-model/src/creating-runnable-components.md +++ b/component-model/src/creating-runnable-components.md @@ -40,7 +40,10 @@ At a high level there are at least two ways to create components that are more l 1. Creating a "command" component 2. Exporting the [`wasi:cli/run` interface][wasi-cli-run] -This section explores how to do the above in relevant languages. +While command components simply have a `_start` export, components that export the [`wasi:cli/run` run interface][wasi-cli-iface-run] +are able to make use of more robust standardized interfaces that mimic a CLI environment like stdout, stderr, via WASI. + +This section explores how to create components that are trivially runanbel (like binaries) in relevant languages. [wasi-cli-iface-run]: https://github.com/WebAssembly/wasi-cli/tree/main/wit/run.wit From f468916959cb73862e23318acf043f9e88dccdf1 Mon Sep 17 00:00:00 2001 From: Victor Adossi Date: Wed, 15 Oct 2025 22:05:17 +0900 Subject: [PATCH 27/35] refactor(lang/rust): description of reactors vs command --- .../language-support/creating-runnable-components/rust.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/component-model/src/language-support/creating-runnable-components/rust.md b/component-model/src/language-support/creating-runnable-components/rust.md index 4c0a4707..b93f9b56 100644 --- a/component-model/src/language-support/creating-runnable-components/rust.md +++ b/component-model/src/language-support/creating-runnable-components/rust.md @@ -59,11 +59,13 @@ wasmtime run ./target/wasm32-wasip2/debug/runnable-example.wasm ## Enabling a library component to be run via the `wasi:cli/run` interface -Any reactor (library-like) component can *also* export the [`run` interface][wasi-cli-iface-run] inside [WASI CLI][wasi-cli], -and signal to consumers that the library can also be run similarly to a binary. +While reactor (library-like) components export interfaces that are meant to be used directly, +they can *also* export the [`wasi:cli/run` interface][wasi-cli-iface-run] from [WASI CLI][wasi-cli], +and signal to consumers that the library can also be run similarly to a binary that would run via a +command line interface. Unlike command components, library components have no `_start`, but by exporting the `wasi:cli/run` interface, -tooling that recognizes these exports can easily execute the given WebAssembly binary (e.g. `wasmtime run`). +tooling that recognizes these exports can easily execute a given WebAssembly binary (e.g. `wasmtime run`). [wasi-cli-iface-run]: https://github.com/WebAssembly/wasi-cli/tree/main/wit/run.wit [wasi-cli]: https://github.com/WebAssembly/wasi-cli From 0227de44182cd8fc70d808a44136c11e23e3920a Mon Sep 17 00:00:00 2001 From: Victor Adossi Date: Wed, 15 Oct 2025 22:09:17 +0900 Subject: [PATCH 28/35] fix(lang/rust): add some rationale for executable libraries --- .../src/language-support/creating-runnable-components/rust.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/component-model/src/language-support/creating-runnable-components/rust.md b/component-model/src/language-support/creating-runnable-components/rust.md index b93f9b56..996c19dd 100644 --- a/component-model/src/language-support/creating-runnable-components/rust.md +++ b/component-model/src/language-support/creating-runnable-components/rust.md @@ -59,6 +59,9 @@ wasmtime run ./target/wasm32-wasip2/debug/runnable-example.wasm ## Enabling a library component to be run via the `wasi:cli/run` interface +Sometimes, it is useful to create a component that can *both* be used as a library (via +exported interface) and executed standalone like a command component. + While reactor (library-like) components export interfaces that are meant to be used directly, they can *also* export the [`wasi:cli/run` interface][wasi-cli-iface-run] from [WASI CLI][wasi-cli], and signal to consumers that the library can also be run similarly to a binary that would run via a From 26f52f3f114ab550e1970db710ce8ce5857466bb Mon Sep 17 00:00:00 2001 From: Victor Adossi Date: Wed, 15 Oct 2025 22:14:11 +0900 Subject: [PATCH 29/35] fix: add redirects for moved language-support pages --- component-model/book.toml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/component-model/book.toml b/component-model/book.toml index 52c90595..74f0eaf2 100644 --- a/component-model/book.toml +++ b/component-model/book.toml @@ -22,6 +22,15 @@ level = 1 "/creating-and-consuming.html" = "/language-support.html" "/runtimes/wasmtime.html" = "/running-components/wasmtime.html" "/runtimes/jco.html" = "/running-components/jco.html" +"/language-support/csharp.html" = "/language-support/building-a-simple-component/csharp.html" +"/language-support/c.html" = "/language-support/building-a-simple-component/c.html" +"/language-support/go.html" = "/language-support/building-a-simple-component/go.html" +"/language-support/javascript.html" = "/language-support/building-a-simple-component/javascript.html" +"/language-support/moonbit.html" = "/language-support/building-a-simple-component/moonbit.html" +"/language-support/other-languages.html" = "/language-support/building-a-simple-component/other-languages.html" +"/language-support/python.html" = "/language-support/building-a-simple-component/python.html" +"/language-support/rust.html" = "/language-support/building-a-simple-component/rust.html" +"/language-support/wat.html" = "/language-support/building-a-simple-component/wat.html" [preprocessor.alerts] From 03f9528aa5bd29635d352befccde6d929990fe9a Mon Sep 17 00:00:00 2001 From: Victor Adossi <123968127+vados-cosmonic@users.noreply.github.com> Date: Thu, 16 Oct 2025 11:45:32 +0900 Subject: [PATCH 30/35] refactor: improve language around other languages Co-authored-by: Kate Goldenring --- .../creating-runnable-components/other-languages.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/component-model/src/language-support/creating-runnable-components/other-languages.md b/component-model/src/language-support/creating-runnable-components/other-languages.md index 7b19780d..d3530408 100644 --- a/component-model/src/language-support/creating-runnable-components/other-languages.md +++ b/component-model/src/language-support/creating-runnable-components/other-languages.md @@ -1,7 +1,6 @@ # Creating Runnable Components (Other Languages) -Just because a given programming language is not listed in this guide does not -mean it is impossible to create runnable WebAssembly components with it. +This guide is a work in progress and does not have examples for all language toolchains with components support. For languages not listed in this guide, it is often possible to create runnable components by following the main principles of the other guides, using the help of the available WebAssembly From 181440aba27a83584b541608f2f75d47dc114d46 Mon Sep 17 00:00:00 2001 From: Victor Adossi <123968127+vados-cosmonic@users.noreply.github.com> Date: Thu, 16 Oct 2025 11:46:06 +0900 Subject: [PATCH 31/35] refactor: other languages contribution CTA Co-authored-by: Kate Goldenring --- .../creating-runnable-components/other-languages.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/component-model/src/language-support/creating-runnable-components/other-languages.md b/component-model/src/language-support/creating-runnable-components/other-languages.md index d3530408..9c662c0a 100644 --- a/component-model/src/language-support/creating-runnable-components/other-languages.md +++ b/component-model/src/language-support/creating-runnable-components/other-languages.md @@ -11,9 +11,9 @@ Generally, WebAssembly toolchains in the language in question may contain a way 1. Create a WebAssembly component with the `_start` export (a "command" compnent) 2. Create a component that exports the `wasi:cli/run` interface -## Adding a New Language to the Guide +## Adding an Example for a Language to this Section -Know of a language guide we should add to this guide? Create a PR to this [repository][repo-pr] that adds -the new language guide (similar to others in this section). +Are you interested in documenting this section for your language toolchain? Create a PR to this [repository][repo-pr] that adds +the guide (similar to others in this section). [repo-pr]: https://github.com/bytecodealliance/component-docs/pulls From 5b2460081fba247ed44252189fca6fc7696ababe Mon Sep 17 00:00:00 2001 From: Victor Adossi <123968127+vados-cosmonic@users.noreply.github.com> Date: Wed, 22 Oct 2025 20:04:14 +0900 Subject: [PATCH 32/35] fix: remove "not impossible" wording Co-authored-by: Kate Goldenring --- .../importing-and-reusing-components/other-languages.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/component-model/src/language-support/importing-and-reusing-components/other-languages.md b/component-model/src/language-support/importing-and-reusing-components/other-languages.md index 563a6f08..5397138e 100644 --- a/component-model/src/language-support/importing-and-reusing-components/other-languages.md +++ b/component-model/src/language-support/importing-and-reusing-components/other-languages.md @@ -1,7 +1,6 @@ # Importing and Reusing components (Other Languages) -Just because a given programming language is not listed in this guide does not -mean it is impossible to import and reuse WebAssembly components with it. +This guide is a work in progress and does not have examples for all language toolchains with components support. For languages not listed in this guide, it is often possible to import and reuse WebAssembly components by following the main principles of the other guides where applicable, using the help of the local From e99de7cc83ba11767ff0dd6bb1b1aa567bf9ce65 Mon Sep 17 00:00:00 2001 From: Victor Adossi <123968127+vados-cosmonic@users.noreply.github.com> Date: Wed, 22 Oct 2025 20:04:40 +0900 Subject: [PATCH 33/35] fix: wording on the for other-languages.md Co-authored-by: Kate Goldenring --- .../importing-and-reusing-components/other-languages.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/component-model/src/language-support/importing-and-reusing-components/other-languages.md b/component-model/src/language-support/importing-and-reusing-components/other-languages.md index 5397138e..31b92914 100644 --- a/component-model/src/language-support/importing-and-reusing-components/other-languages.md +++ b/component-model/src/language-support/importing-and-reusing-components/other-languages.md @@ -18,7 +18,7 @@ Note that generic tooling like [`wac`][wac] can be used to compose components to ## Adding a New Language to the Guide -Know of a language guide we should add to this guide? Create a PR to this [repository][repo-pr] that adds -the new language guide (similar to others in this section). +Are you interested in documenting this section for your language toolchain? Create a PR to this [repository][repo-pr] that adds +the guide (similar to others in this section). [repo-pr]: https://github.com/bytecodealliance/component-docs/pulls From 28a6721f0704a612b737052ab237ce22044e7da3 Mon Sep 17 00:00:00 2001 From: Victor Adossi <123968127+vados-cosmonic@users.noreply.github.com> Date: Wed, 22 Oct 2025 20:06:29 +0900 Subject: [PATCH 34/35] fix: typos Co-authored-by: Mikhail Katychev --- .../creating-runnable-components/other-languages.md | 4 ++-- .../language-support/importing-and-reusing-components/rust.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/component-model/src/language-support/creating-runnable-components/other-languages.md b/component-model/src/language-support/creating-runnable-components/other-languages.md index 9c662c0a..288a22ca 100644 --- a/component-model/src/language-support/creating-runnable-components/other-languages.md +++ b/component-model/src/language-support/creating-runnable-components/other-languages.md @@ -3,12 +3,12 @@ This guide is a work in progress and does not have examples for all language toolchains with components support. For languages not listed in this guide, it is often possible to create runnable components by -following the main principles of the other guides, using the help of the available WebAssembly +applying the core concepts found in other guides, using the help of the available WebAssembly toolchain. Generally, WebAssembly toolchains in the language in question may contain a way to: -1. Create a WebAssembly component with the `_start` export (a "command" compnent) +1. Create a WebAssembly component with the `_start` export (a "command" component) 2. Create a component that exports the `wasi:cli/run` interface ## Adding an Example for a Language to this Section diff --git a/component-model/src/language-support/importing-and-reusing-components/rust.md b/component-model/src/language-support/importing-and-reusing-components/rust.md index 029ccf35..8460402f 100644 --- a/component-model/src/language-support/importing-and-reusing-components/rust.md +++ b/component-model/src/language-support/importing-and-reusing-components/rust.md @@ -99,7 +99,7 @@ world root { As the import is unfulfilled, the `calculator.wasm` component could not run by itself in its current form. The next step is to fulfill the `add` import, so that only `calculate` is exported, and the component can be run. -The process of fufilling imports via other component's exports is called "composition". Learn more about how to compose the calculator.wasm +The process of fulfilling imports via other component's exports is called "composition". Learn more about how to compose the calculator.wasm with an adder.wasm into a single, self-contained component in the [component composition guide](../../composing-and-distributing/composing.md). [!NOTE]: # From 1f8a7c1fbc11d8f060666d92a1c7e279100ea475 Mon Sep 17 00:00:00 2001 From: Victor Adossi Date: Wed, 22 Oct 2025 20:10:40 +0900 Subject: [PATCH 35/35] refactor: other-languages section for building a simple component --- .../other-languages.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/component-model/src/language-support/building-a-simple-component/other-languages.md b/component-model/src/language-support/building-a-simple-component/other-languages.md index 6c383d05..1b164b16 100644 --- a/component-model/src/language-support/building-a-simple-component/other-languages.md +++ b/component-model/src/language-support/building-a-simple-component/other-languages.md @@ -1,14 +1,14 @@ # Other Languages with Component Model Support -More languages continue to add support for the component model. The following is a living list of other languages that support the component model and documentation on how to get started using components in those languages. +This guide is a work in progress and does not example for all langauge toolchains +with WebAssembly component support. -[comment]: # (Add first language here) +As more languages continue to add support for the component model, we welcome +contributions of documentation for how to get started using components in those languages. -## Adding a New Language to the Guide +## Adding an Example for a Language to this Section -Interested in adding another language to this list? Create a PR to this [document](https://github.com/bytecodealliance/component-docs/blob/main/component-model/src/language-support/other-languages.md) that adds an entry with the following information: +Are you interested in documenting this section for your language toolchain? +Create a PR to this [repository][repo-pr] that adds the guide (similar to others in this section). -- Language name as a level 3 header -- Link to the project page on Github page -- Link to the project hosted documentation on getting started with components. Feel free to use our example WITs if you want to match the style of this guide. -- Please insert alphabetically \ No newline at end of file +[repo-pr]: https://github.com/bytecodealliance/component-docs/pulls