diff --git a/component-model/src/advanced/canonical-abi.md b/component-model/src/advanced/canonical-abi.md index df895b1..2a5a0e3 100644 --- a/component-model/src/advanced/canonical-abi.md +++ b/component-model/src/advanced/canonical-abi.md @@ -4,4 +4,4 @@ An ABI is an **application binary interface** - an agreement on how to pass data The component model defines a **canonical ABI** - an ABI to which all [components](../design/components.md) adhere. This guarantees that components can talk to each other without confusion, even if they are built in different languages. Internally, a C component might represent strings in a quite different way from a Rust component, but the canonical ABI provides a format for them to pass strings across the boundary between them. -> ⓘ For a more formal definition of what the Canonical ABI is, take a look at the [Canonical ABI explainer](https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md). +> For a more formal definition of what the Canonical ABI is, take a look at the [Canonical ABI explainer](https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md). diff --git a/component-model/src/design/components.md b/component-model/src/design/components.md index c3dd542..53cfb1a 100644 --- a/component-model/src/design/components.md +++ b/component-model/src/design/components.md @@ -6,4 +6,4 @@ The external interface of a component - its imports and exports - corresponds to a [world](./worlds.md). The component, however, internally defines how that world is implemented. -> ⓘ For a more formal definition of what a component is, take a look at the [Component Model specification](https://github.com/WebAssembly/component-model). +> For a more formal definition of what a component is, take a look at the [Component Model specification](https://github.com/WebAssembly/component-model). diff --git a/component-model/src/design/interfaces.md b/component-model/src/design/interfaces.md index eac851c..69f3c57 100644 --- a/component-model/src/design/interfaces.md +++ b/component-model/src/design/interfaces.md @@ -7,4 +7,4 @@ An **interface** describes a single-focus, composable contract, through which co Interfaces are defined using [the WIT language](./wit.md). -> ⓘ For a more formal definition of what an interface is, take a look at the [WIT specification](https://github.com/WebAssembly/component-model/blob/main/design/mvp/WIT.md). +> For a more formal definition of what an interface is, take a look at the [WIT specification](https://github.com/WebAssembly/component-model/blob/main/design/mvp/WIT.md). diff --git a/component-model/src/design/packages.md b/component-model/src/design/packages.md index 3a9a4a3..295c96a 100644 --- a/component-model/src/design/packages.md +++ b/component-model/src/design/packages.md @@ -6,4 +6,4 @@ A WIT package is not a [world](./worlds.md). It's a way of grouping related inte * The WebAssembly System Interface (WASI) defines a number of packages, including one named `wasi:clocks`. Our HTTP proxy world could import the `wall-clock` interface from the `wasi:clocks` package, rather than having to define a custom clock interface. -> ⓘ For a more formal definition of what a WIT package is, take a look at the [WIT specification](https://github.com/WebAssembly/component-model/blob/main/design/mvp/WIT.md). +> For a more formal definition of what a WIT package is, take a look at the [WIT specification](https://github.com/WebAssembly/component-model/blob/main/design/mvp/WIT.md). diff --git a/component-model/src/design/why-component-model.md b/component-model/src/design/why-component-model.md index b68d9d9..4882d79 100644 --- a/component-model/src/design/why-component-model.md +++ b/component-model/src/design/why-component-model.md @@ -16,4 +16,4 @@ Moreover, a component interacts with a runtime or other components _only_ by cal Now that you have a better idea about how the component model can help you, take a look at [how to build components](../language-support.md) in your favorite language! -> ⓘ For more background on why the component model was created, take a look at the specification's [goals](https://github.com/WebAssembly/component-model/blob/main/design/high-level/Goals.md), [use cases](https://github.com/WebAssembly/component-model/blob/main/design/high-level/UseCases.md) and [design choices](https://github.com/WebAssembly/component-model/blob/main/design/high-level/Choices.md). +> For more background on why the component model was created, take a look at the specification's [goals](https://github.com/WebAssembly/component-model/blob/main/design/high-level/Goals.md), [use cases](https://github.com/WebAssembly/component-model/blob/main/design/high-level/UseCases.md) and [design choices](https://github.com/WebAssembly/component-model/blob/main/design/high-level/Choices.md). diff --git a/component-model/src/design/wit.md b/component-model/src/design/wit.md index 7cfd35e..9315c03 100644 --- a/component-model/src/design/wit.md +++ b/component-model/src/design/wit.md @@ -91,29 +91,29 @@ WIT defines the following primitive types: | Identifier | Description | |----------------------------|-------------| -| `bool` | Boolean value - true or false. | -| `s8`, `s16`, `s32`, `s64` | Signed integers of the appropriate width. For example, `s32` is a 32-bit integer. | -| `u8`, `u16`, `u32`, `u64` | Unsigned integers of the appropriate width. For example, `u32` is a 32-bit integer. | -| `f32`, `f64` | Floating-point numbers of the appropriate width. For example, `f64` is a 64-bit (double precision) floating-point number. See the note on NaNs below. | +| `bool` | Boolean value `true` or `false`. | +| `s8`, `s16`, `s32`, `s64` | Signed integers of the appropriate width. For example, `s32` is a signed 32-bit integer. | +| `u8`, `u16`, `u32`, `u64` | Unsigned integers of the appropriate width. For example, `u32` is an unsigned 32-bit integer. | +| `f32`, `f64` | Floating-point numbers of the appropriate width. For example, `f64` is a 64-bit (double precision) floating-point number. See the note on `NaN`s below. | | `char` | Unicode character. (Specifically, a [Unicode scalar value](https://unicode.org/glossary/#unicode_scalar_value).) | | `string` | A Unicode string - that is, a finite sequence of characters. | -The `f32` and `f64` types support the usual set of IEEE 754 single and double-precision values, except that they logically only have a single NaN value. The exact bit-level representation of a NaN is not guaranteed to be preserved when values pass through WIT interfaces. +> The `f32` and `f64` types support the usual set of IEEE 754 single and double-precision values, except that they logically only have a single `nan` value. The exact bit-level representation of an IEEE 754 `NaN` is not guaranteed to be preserved when values pass through WIT interfaces as the singular WIT `nan` value. ### Lists -`list` for any type T denotes an ordered sequence of values of type T. T can be any type, built-in or user-defined: +`list` for any type `T` denotes an ordered sequence of values of type `T`. `T` can be any type, built-in or user-defined: ```wit -list // byte buffer -list // a list of customers +list // byte buffer +list // a list of customers ``` This is similar to Rust `Vec`, or Java `List`. ### Options -`option` for any type T may contain a value of type T, or may contain no value. T can be any type, built-in or user-defined. For example, a lookup function might return an option, allowing for the possibility that the lookup key wasn't found: +`option` for any type `T` may contain a value of type `T`, or may contain no value. `T` can be any type, built-in or user-defined. For example, a lookup function might return an option, allowing for the possibility that the lookup key wasn't found: ```wit option @@ -125,7 +125,7 @@ This is similar to Rust `Option`, C++ `std::optional`, or Haskell `Maybe`. ### Results -`result` for any types T and E may contain a value of type T _or_ a value of type E (but not both). This is typically used for "value or error" situations; for example, a HTTP request function might return a result, with the success case (the T type) representing a HTTP response, and the error case (the E type) representing the various kinds of error that might occur: +`result` for any types `T` and `E `may contain a value of type `T` _or_ a value of type `E` (but not both). This is typically used for "value or error" situations; for example, a HTTP request function might return a result, with the success case (the `T` type) representing a HTTP response, and the error case (the `E` type) representing the various kinds of error that might occur: ```wit result @@ -145,11 +145,11 @@ result // no data associated with either case ### Tuples -A tuple type is an ordered _fixed length_ sequence of values of specified types. It is similar to a [_record_](#records), except that the fields are identified by their order instead of by names. +A `tuple` type is an ordered _fixed length_ sequence of values of specified types. It is similar to a [_record_](#records), except that the fields are identified by their order instead of by names. ```wit -tuple // An integer and a string -tuple // An integer, then a string, then an integer +tuple // An integer and a string +tuple // An integer, then a string, then an integer ``` This is similar to tuples in Rust or OCaml. @@ -160,7 +160,7 @@ You can define your own types within an `interface` or `world`. WIT offers sever ### Records -A record type declares a set of named fields, each of the form `name: type`, separated by commas. A record instance contains a value for every field. Field types can be built-in or user-defined. The syntax is as follows: +A `record` type declares a set of named fields, each of the form `name: type`, separated by commas. A record instance contains a value for every field. Field types can be built-in or user-defined. The syntax is as follows: ```wit record customer { @@ -177,7 +177,7 @@ Records are similar to C or Rust `struct`s. ### Variants -A variant type declares one or more cases. Each case has a name and, optionally, a type of data associated with that case. A variant instance contains exactly one case. Cases are separated by commas. The syntax is as follows: +A `variant` type declares one or more cases. Each case has a name and, optionally, a type of data associated with that case. A variant instance contains exactly one case. Cases are separated by commas. The syntax is as follows: ```wit variant allowed-destinations { @@ -193,7 +193,7 @@ Variants are similar to Rust `enum`s or OCaml discriminated unions. The closest ### Enums -An enum type is a variant type where none of the cases have associated data: +An `enum` type is a variant type where none of the cases have associated data: ```wit enum color { @@ -203,7 +203,7 @@ enum color { } ``` -This can provide a simpler representation in languages without discriminated unions. For example, a WIT enum can translate directly to a C++ `enum`. +This can provide a simpler representation in languages without discriminated unions. For example, a WIT `enum` can translate directly to a C++ `enum`. ### Resources @@ -248,18 +248,16 @@ blob-read: func(self: borrow, n: u32) -> list; blob-merge: static func(lhs: blob, rhs: blob) -> blob; ``` -When a resource type name is wrapped with `borrow<...>`, it stands for a +When a `resource` type name is wrapped with `borrow<...>`, it stands for a "borrowed" resource. A borrowed resource represents a temporary loan of a resource from the caller to the callee for the duration of the call. In contrast, when the owner of an owned resource drops that resource, the resource is destroyed. -> Note: more precisely, these are borrowed or owned `handles` of the resource. -> Learn more about `handles` in the [upstream component model -> specification](https://github.com/WebAssembly/component-model/blob/main/design/mvp/WIT.md#handles). +> More precisely, these are borrowed or owned `handles` of the resource. Learn more about `handles` in the [upstream component model specification](https://github.com/WebAssembly/component-model/blob/main/design/mvp/WIT.md#handles). ### Flags -A flags type is a set of named booleans. In an instance of the type, each flag will be either true or false. +A `flags` type is a set of named booleans. In an instance of the type, each flag will be either `true` or `false`. ```wit flags allowed-methods { @@ -270,7 +268,7 @@ flags allowed-methods { } ``` -> A flags type is logically equivalent to a record type where each field is of type `bool`, but it is represented more efficiently (as a bitfield) at the binary level. +> A `flags` type is logically equivalent to a record type where each field is of type `bool`, but it is represented more efficiently (as a bitfield) at the binary level. ### Type aliases @@ -292,10 +290,10 @@ do-nothing: func(); The function type is the word `func`, followed by a parenthesised, comma-separated list of parameters (names and types). If the function returns a value, this is expressed as an arrow symbol (`->`) followed by the return type: ```wit -/// This function does not return a value +// This function does not return a value print: func(message: string); -/// These functions return values +// These functions return values add: func(a: u64, b: u64) -> u64; lookup: func(store: kv-store, key: string) -> option; ``` @@ -365,11 +363,13 @@ interface error-reporter { } world multi-function-device { - /// The component implements the `printer` interface + // The component implements the `printer` interface export printer; - /// The component implements the `scan` function + + // The component implements the `scan` function export scan: func() -> list; - /// The component needs to be supplied with an `error-reporter` + + // The component needs to be supplied with an `error-reporter` import error-reporter; } ``` @@ -407,8 +407,10 @@ You can `include` another world. This causes your world to export all that world ```wit world glow-in-the-dark-multi-function-device { - // The component provides all the same exports, and depends on all the same imports, as a `multi-function-device`... + // The component provides all the same exports, and depends on + // all the same imports, as a `multi-function-device`... include multi-function-device; + // ...but also exports a function to make it glow in the dark export glow: func(brightness: u8); } @@ -455,4 +457,4 @@ world proxy { } ``` -> ⓘ For a more formal definition of the WIT language, take a look at the [WIT specification](https://github.com/WebAssembly/component-model/blob/main/design/mvp/WIT.md). +> For a more formal definition of the WIT language, take a look at the [WIT specification](https://github.com/WebAssembly/component-model/blob/main/design/mvp/WIT.md). diff --git a/component-model/src/design/worlds.md b/component-model/src/design/worlds.md index 13ed3f0..e708c68 100644 --- a/component-model/src/design/worlds.md +++ b/component-model/src/design/worlds.md @@ -17,4 +17,4 @@ For a component to run, its imports must be fulfilled, by a host or by other com * A (trivial) "HTTP proxy" world would export a "handle HTTP requests" interface, and import a "send HTTP requests" interface. A host, or another component, would call the exported "handle" interface, passing an HTTP request; the component would forward it on via the imported "send" interface. To be a _useful_ proxy, the component may also need to import interfaces such as I/O and clock time - without those imports the component could not perform, for example, on-disk caching. * A "regex parser" world would export a "parse regex" function, and would import nothing. This declares not only that the component implementing this world can parse regular expressions, but also that it calls no other APIs. A user of such a parser could know, without looking at the implementation, that is does not access the file system, or send the user's regexes to a network service. -> ⓘ For a more formal definition of what a WIT world is, take a look at the [WIT world specification](https://github.com/WebAssembly/component-model/blob/main/design/mvp/WIT.md#wit-worlds). +> For a more formal definition of what a WIT world is, take a look at the [WIT world specification](https://github.com/WebAssembly/component-model/blob/main/design/mvp/WIT.md#wit-worlds). diff --git a/component-model/src/introduction.md b/component-model/src/introduction.md index 25a1187..db001b0 100644 --- a/component-model/src/introduction.md +++ b/component-model/src/introduction.md @@ -25,7 +25,7 @@ The WebAssembly Component Model is a broad-reaching architecture for building in [Running]: ./creating-and-consuming/running.md [Distributing]: ./creating-and-consuming/distributing.md -> ⓘ This documentation is aimed at _users_ of the component model: developers of libraries and applications. _Compiler and Wasm runtime developers_ can take a look at the [Component Model specification](https://github.com/WebAssembly/component-model) to see how to add support for the component model to their project. +> This documentation is aimed at _users_ of the component model: developers of libraries and applications. _Compiler and Wasm runtime developers_ can take a look at the [Component Model specification](https://github.com/WebAssembly/component-model) to see how to add support for the component model to their project. ## Status diff --git a/component-model/src/language-support.md b/component-model/src/language-support.md index cc5a962..a343ab6 100644 --- a/component-model/src/language-support.md +++ b/component-model/src/language-support.md @@ -3,9 +3,10 @@ WebAssembly can be targeted by the majority of top programming languages; however, the level of support varies. This document details the subset of languages that target WASI and support -components. This is a living document, so if you are aware of advancements in a toolchain, please do -not hesitate to [contribute documentation](https://github.com/bytecodealliance/component-docs/blob/main/CONTRIBUTING.md). You can find more information about the development of support for specific languages [here](https://github.com/bytecodealliance/governance/blob/main/SIGs/SIG-guest-languages/proposal.md). Each section covers how to build and -run components for a given toolchain. +components. + +> This is a living document, so if you are aware of advancements in a toolchain, please do +not hesitate to [contribute documentation](https://github.com/bytecodealliance/component-docs/blob/main/CONTRIBUTING.md). You can find more information about the development of support for specific languages in the [Guest Languages Special Interest Group Proposal](https://github.com/bytecodealliance/governance/blob/main/SIGs/SIG-guest-languages/proposal.md) document. One of the benefits of components is their portability across host runtimes. The runtime only needs to know what world the component is targeting in order to import or execute the component. This @@ -16,6 +17,9 @@ toolchain section walks through creating a component of this world, which can be example host or from an application of that toolchain. This aims to provide a full story for using components within and among toolchains. +Each section covers how to build and +run components for a given toolchain: + - [Wasm Language Support](#wasm-language-support) - [Language Agnostic Tooling](#language-agnostic-tooling) - [Building a Component with `wasm-tools`](#building-a-component-with-wasm-tools) diff --git a/component-model/src/language-support/c.md b/component-model/src/language-support/c.md index 9f24ffe..b84cfc9 100644 --- a/component-model/src/language-support/c.md +++ b/component-model/src/language-support/c.md @@ -35,7 +35,7 @@ Now, you can compile the function into a Wasm module via clang: clang add.c example.c example_component_type.o -o add-core.wasm -mexec-model=reactor ``` -> Note: Use the `clang` included in the WASI SDK installation, for example at `/bin/clang`. +> Use the `clang` included in the WASI SDK installation, for example at `/bin/clang`. Next, you need to transform the module into a component. For this example, you can use `wasm-tools component new`: ```sh diff --git a/component-model/src/language-support/python.md b/component-model/src/language-support/python.md index 40006f8..b1652ba 100644 --- a/component-model/src/language-support/python.md +++ b/component-model/src/language-support/python.md @@ -28,7 +28,7 @@ If you want to generate bindings produced for the WIT world (for an IDE or typec $ componentize-py --wit-path /path/to/examples/example-host/add.wit --world example bindings . ``` -> Note: you do not need to generate the bindings in order to `componentize` in the next step. `componentize` will generate bindings on-the-fly and bundle them into the produced component. +> You do not need to generate the bindings in order to `componentize` in the next step. `componentize` will generate bindings on-the-fly and bundle them into the produced component. You can see that bindings were created in an `example` package which contains an `Example` protocol with an `add` method that we can implement: @@ -100,7 +100,7 @@ Component built successfully Wasm components can also be invoked from Python applications. This walks through using tooling to call the [`app.wasm` component from the examples](../../examples/example-host/add.wasm). -> Note: `wasmtime-py` does not currently support running components build with `componentize-py`. This is because `wasmtime-py` does not yet support [resources](../design/wit.md#resources), which components built with `componentize-py` always use, since `componentize-py` unconditionally imports most of the `wasi:cli` world. +> `wasmtime-py` does not currently support running components build with `componentize-py`. This is because `wasmtime-py` does not yet support [resources](../design/wit.md#resources), which components built with `componentize-py` always use, since `componentize-py` unconditionally imports most of the `wasi:cli` world. First, install [Python 3.11 or later](https://www.python.org/) and [pip](https://pypi.org/project/pip/) if you don't already have them. Then, install [`wasmtime-py`](https://github.com/bytecodealliance/wasmtime-py): diff --git a/component-model/src/language-support/rust.md b/component-model/src/language-support/rust.md index ac77a5c..658e21c 100644 --- a/component-model/src/language-support/rust.md +++ b/component-model/src/language-support/rust.md @@ -227,54 +227,54 @@ As mentioned above, `cargo component build` doesn't generate a WIT file for a co 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; + ```wit + package docs:app; -world app { - import docs:calculator/calculate@0.1.0; -} -``` + 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. + > `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" -``` + ```toml + [package.metadata.component.target] + path = "wit" + ``` -(This entry is created automatically for library components but not for command components.) + (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" } -``` + ```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. + > 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; + ```rust + use bindings::docs::calculator::calculate::eval_expression; -fn main() { - let result = eval_expression("1 + 1"); - println!("1 + 1 = {result}"); -} -``` + fn main() { + let result = eval_expression("1 + 1"); + println!("1 + 1 = {result}"); + } + ``` 5. [Compose the command component with the `.wasm` components that implement the imports.](../creating-and-consuming/composing.md) 6. Run the composed component: -```sh -$ wasmtime run ./my-composed-command.wasm -1 + 1 = 579 # might need to go back and do some work on the calculator implementation -``` + ```sh + $ wasmtime run ./my-composed-command.wasm + 1 + 1 = 579 # might need to go back and do some work on the calculator implementation + ``` ## Using user-defined types @@ -367,61 +367,61 @@ To implement the calculator using `cargo component`: 2. Define a Rust `struct` to represent the calculator state: -```rust -use std::cell::RefCell; + ```rust + use std::cell::RefCell; -struct CalcEngine { - stack: 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>`. + > 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}; + ```rust + use bindings::exports::docs::rpn::types::{GuestEngine, Operation}; -impl GuestEngine for CalcEngine { - fn new() -> Self { - CalcEngine { - stack: RefCell::new(vec![]) + 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_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 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! + 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; -} + ```rust + struct Implementation; + impl Guest for Implementation { + type Engine = CalcEngine; + } -bindings::export!(Implementation with_types_in bindings); -``` + 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. @@ -433,43 +433,43 @@ To use the calculator engine in another component, that component must import th 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; + ```wit + package docs:rpn-cmd; -world app { - import docs:rpn/types@0.1.0; -} -``` + 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" + ```toml + [package.metadata.component] + package = "docs:rpn-cmd" -[package.metadata.component.target] -path = "wit" + [package.metadata.component.target] + path = "wit" -[package.metadata.component.target.dependencies] -"docs:rpn" = { path = "../wit" } # or wherever your resource WIT is -``` + [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}"); -} -``` + ```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.](../creating-and-consuming/composing.md). You can then run the composed command with `wasmtime run`. @@ -479,62 +479,62 @@ If you are hosting a Wasm runtime, you can export a resource from your host for 1. Use `wasmtime::component::bindgen!` to specify the WIT you are a host for: -```rust -wasmtime::component::bindgen!({ - path: "../wit" -}); -``` + ```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, - } -}); -``` + ```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. + > 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 { /* ... */ } -``` + ```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. -} -``` + ```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`. + > **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, -} -``` + ```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 + ```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. } - // etc. -} -``` + ``` diff --git a/component-model/src/tutorial.md b/component-model/src/tutorial.md index 68dc04b..b0e4abf 100644 --- a/component-model/src/tutorial.md +++ b/component-model/src/tutorial.md @@ -21,18 +21,18 @@ These files can be found in the component book repository in the [`wit` director * A world describing an world that exports the "add" interface. Again, components such as the calculator can call it when they need to add numbers. -```wit -// wit/adder/world.wit -package docs:adder@0.1.0; + ```wit + // wit/adder/world.wit + package docs:adder@0.1.0; -interface add { - add: func(a: u32, b: u32) -> u32; -} + interface add { + add: func(a: u32, b: u32) -> u32; + } -world adder { - export add; -} -``` + world adder { + export add; + } + ``` * An interface for the calculator itself. We'll use this later to carry out calculations. It contains an evaluate function, and an enum that delineates the operations that can be involved in @@ -48,27 +48,27 @@ world adder { of the calculator component. -```wit -// wit/calculator/world.wit -package docs:calculator@0.1.0; + ```wit + // wit/calculator/world.wit + package docs:calculator@0.1.0; -interface calculate { - enum op { - add, - } - eval-expression: func(op: op, x: u32, y: u32) -> u32; -} + interface calculate { + enum op { + add, + } + eval-expression: func(op: op, x: u32, y: u32) -> u32; + } -world calculator { - export calculate; - import docs:adder/add@0.1.0; -} + world calculator { + export calculate; + import docs:adder/add@0.1.0; + } -world app { - import calculate; -} + world app { + import calculate; + } -``` + ``` ## Create an `add` component @@ -134,9 +134,7 @@ wasm-tools compose calculator.wasm -d adder.wasm -o composed.wasm wasm-tools compose command.wasm -d composed.wasm -o final.wasm ``` -> If you'd prefer to take a more visual approach to composing components, see the [documentation on -> composing components with -> wasmbuilder.app](creating-and-consuming/composing.md#composing-components-with-a-visual-interface). +> If you'd prefer to take a more visual approach to composing components, see the [documentation on composing components with wasmbuilder.app](creating-and-consuming/composing.md#composing-components-with-a-visual-interface). ## Running the calculator