From b718cd958ecc0fc260ffa3465406e0b899140f9c Mon Sep 17 00:00:00 2001 From: Peter Huene Date: Thu, 15 Dec 2022 14:20:46 -0800 Subject: [PATCH 01/10] Add a design document around registry integration. This commit adds a design document for the developer experience surrounding using packages from a component registry in `cargo-component`. Closes #42. --- docs/design/registries.md | 368 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 368 insertions(+) create mode 100644 docs/design/registries.md diff --git a/docs/design/registries.md b/docs/design/registries.md new file mode 100644 index 00000000..7fb4e70c --- /dev/null +++ b/docs/design/registries.md @@ -0,0 +1,368 @@ +# Component Registries + +This document describes the design of using component registries from +`cargo-component`. + +## Motivation + +Currently, `cargo-component` only supports referencing interface definitions +from local files. + +It also lacks support for directly referencing other components to use as a +dependency. + +In the near future, _component registries_ will exist that are designed +specifically to support the WebAssembly component model. + +Component authoring tooling like `cargo-component` should be able to easily +reference packages from a component registry to facilitate the development of +components. + +## Goals + +The design follows these goals: + +* **Follow `cargo` conventions** + + * The design should be familiar (enough) to Rust developers given that using + dependencies from `crates.io` is universally understood. + +* **Don't force wit on users** + + * The design should enable users to use on components from a registry without + forcing them to immediately learn wit. + +* **Fully support the component model** + + * The design should enable users to leverage wit when authoring their + component and make it easy to reference registry packages in wit. + +## Registry package types + +This design follows the [SIG Registry glossary](https://github.com/bytecodealliance/SIG-Registries/blob/main/glossary.md) +definition of package types. + +`cargo-component` will support expressing dependencies on the following package +types: + +* `wit` - a binary-encoded wit document defining any number of interfaces and + worlds; a wit package describes component model _types_. + +* `component` - a binary-encoded WebAssembly component; a component package + contains an _implementation_ (i.e. code) in addition to describing component + model _types_. + +Note that both package types use the binary AST of the component model. + +## Design + +With the goal being to feel familiar to Rust developers, `Cargo.toml` will be +used to express dependencies on packages from a component registry. + +However, as to not recreate all of the expressivity of wit itself using TOML, +what can be accomplished with `Cargo.toml` alone will be limited. + +When more expressivity is required, a wit document can be used to directly describe the authored component's type. + +### `Cargo.toml` syntax + +This design proposes the following tables in `Cargo.toml`: + +* `[package.metadata.component]` + + This table contains information about the component being authored. The + supported fields are: + + * `package` - the name of the component package to use when publishing to a + component registry; required to publish. + + * `targets` - a target world for the component being authored; this causes + bindings to be generated for the specified world's imports and exports. + + * `world` - a path to a wit document specifying a world definition for the + component being authored; it must contain a `world` with the same name as + the current crate name. + + The `targets` and `world` fields are mutually exclusive. + +* `[package.metadata.component.dependencies]` + + This table contains information about component dependencies. + + The dependencies may come from a component registry or from local wit + documents and components. + + Specifying dependencies from a registry is done in the general form of: + + ```toml + = { package = "", version = "", registry = "" } + ``` + + The `registry` field is optional and defaults to `default` (see the `[package.metadata.component.registries]` + table below). + + As packages in a component registry are namespaced (unlike Rust crate + registries), the short-form differs from what is supported by `cargo`: + + ```toml + = ":" + ``` + + Which is equivalent to: + + ```toml + = { package = "", version = "" } + ``` + + Local wit documents and components are specified using the `path` field: + + ```toml + = { path = "" } + ``` + + In the future, it may be possible to specify a path to a directory containing + a `Cargo.toml` that itself defines a component and treat it as a component + package dependency. + + The names of dependencies correspond to "packages" in a `wit` document and + may also influence the names of imports in the component being authored ([see dependency behavior](#dependency-behavior)). + +* `[package.metadata.component.registries]` + + This table is entirely optional and is a map of friendly names to registry + URL (the format of the URL is dependent upon the underlying + registry client implementation): + + ```toml + = "" + ``` + + Dependencies may specify a specific registry to use by specifying the `registry` field: + + ```toml + foo = { package = "ns/foo", version = "0.1.0", registry = "my-registry" } + ``` + + The `default` name is used when no `registry` field is specified in a + dependency. Therefore, specifying a registry with name `default` will + override the built-in default in `cargo-component` (expected to be a + future Bytecode Alliance component registry instance). + + A registry URL may use the `file://` scheme to point at local directory that + may have vendored packages. This "local registry" will be the first supported + registry implementation in `cargo-component` while the implementation of + component registries is still in progress. + + It should be possible to specify the registries at the workspace root `Cargo.toml` as well, allowing for a single set of registries to be used across a workspace. + +#### The `targets` field + +The `targets` field in `[package.metadata.component]` is used to specify a +world the component is intended to target. + +If not specified, the component will only import from component package +dependencies by default. + +The syntax of the value is identical to referencing a world from a package in a +`wit` document: + +```toml +"." +``` + +Here `package-name` is the name of one of the dependencies in `[package.metadata.component.dependencies]`. + +Assuming an entry for a `wasi` dependency in `[package.metadata.component.dependencies]` +and the package contains a world named `command`, a component being authored +may specify a value of `wasi.command` to target the `command` world, for +example. + +Bindings will be automatically generated for the specified world's imports and +exports. + +It is an error to specify a name of a package that is not specified in the +dependencies table. + +It is also an error to specify a world name that is not defined in the +specified package. + +#### The `world` field + +The `world` field in `[package.metadata.component]` is used to specify the path +to a wit document defining the authored component's world; the document must contain a world with the same name as the current crate. + +The field exists to grant developers explicit control over the _component type_ +(i.e. its imports and exports) of the component being authored. + +The `world` field is mutually exclusive with the `targets` field as it +is expected that wit should be able to express the targeting relationship (i.e. +via a proposed `include` syntax, perhaps). + +The presence of a `world` field alters the [behavior of dependencies](#dependency-behavior). + +#### Dependency behavior + +The presence of a `world` field in `[package.metadata.component]` alters the +behavior of dependencies. + +When the `world` field _is not_ specified: + +* There should be at most one wit package dependency and it must + be used in the `targets` field, if present; defining a dependency on a wit + package not used in a `targets` field will result in an "unused dependency" + warning from `cargo-component`. + +* Any number of component package dependencies are allowed; bindings will be + generated to (instance) import the component's directly exported functions; + the imports are _in addition to_ any imports from a targeted world. + +When the `world` field _is_ specified: + +* Any number of wit and component package dependencies are allowed. + +* The dependencies are mapped to package references in the specified wit + document. + +* Component package dependencies are not automatically translated to imports as + it is expected the the world being defined will describe how to import them. + +## Tooling to extract a world from `Cargo.toml`. + +It is expected that `cargo-component` will have a subcommand for extracting a +wit document from a `Cargo.toml` file that does not already contain a `world` +field. + +It would then add the `world` field to the `Cargo.toml` to point at the +extracted wit document. + +The extracted wit document should be semantically equivalent to the world +synthesized by `cargo-component` from the `targets` field and component package +dependencies when the `world` field is not present. + +## Examples + +### Targeting only a world from a registry + +An example `Cargo.toml`: + +```toml +[package] +name = "my-component" +version = "1.2.3" + +[dependencies] +# Rust crate dependencies here + +[package.metadata.component] +package = "my-org/my-component" +targets = "wasi.command" + +[package.metadata.component.dependencies] +wasi = "webassembly/wasi:1.2.3" +``` + +The above might be the future default output from `cargo component new`. + +Here the component being authored _may_ import what is expected to be imported +by the `command` world and _must_ export what is expected to be exported +by the `command` world via the generated bindings. + +In theory, the authored component could then simply run in any host that +supports the `wasi.command` world (e.g. a future wasmtime CLI). + +### Targeting a world and using other components from a registry + +An example `Cargo.toml`: + +```toml +[package] +name = "my-component" +version = "1.2.3" + +[dependencies] +# Rust crate dependencies here + +[package.metadata.component] +package = "my-org/my-component" +targets = "wasi.command" + +[package.metadata.component.dependencies] +wasi = "webassembly/wasi:1.2.3" +regex = "fancy-components/regex:1.0.0" +transcoder = "fancy-components/transcoder:1.0.0" +``` + +In this example, the component still targets the `wasi.command` world as above. + +However, it will also import an _instance_ named `regex` and an instance named +`transcoder` that export the functions directly exported by the `regex` and +`transcoder` components, respectively. + +The component produced by `cargo-component` will contain URL references to the +component dependencies and these serve as hints for later composition tooling +to instantiate those particular components and link them with this one by +default. As they are instance imports, the authored component may still be +linked against alternative implementations provided they implement the expected +interfaces according to component model subtyping rules. + +Running the proposed command to generate a wit document from this `Cargo.toml` should create a wit file that looks something like: + +```wit +world my-component { + include wasi.command # proposed syntax for including a world in another + + import regex: regex + + import transcoder: transcoder +} +``` + +### Defining a custom world for a component + +An example `Cargo.toml`: + +```toml +[package] +name = "my-component" +version = "1.2.3" + +[dependencies] +# Rust crate dependencies here + +[package.metadata.component] +package = "my-org/my-component" +world = "./component.wit" + +[package.metadata.component.dependencies] +wasi = "webassembly/wasi:1.2.3" +regex = "fancy-components/regex:1.0.0" +transcoder = "fancy-components/transcoder:1.0.0" +``` + +An example `component.wit`: + +```wit +world my-component { + include wasi.command # proposed syntax for including a world in another + + import regex: regex + + import transcoder: transcoder + + parse: func(x: string) -> result<_, string> +} +``` + +Because the `world` field is specified, the dependencies are interpreted as wit +document packages only; `regex` and `transcoder` are not imported automatically +as in the previous example. + +The resulting type for this component will the same as the previous example, +but it will also export a `parse` function of type `string -> result<_, string>` +as specified in the world. + +The wit document package names of `wasi`, `regex`, and `transcoder` map +directly to the names of the dependencies in `[package.metadata.component.dependencies]`. + +Referencing a package in the wit document that is not defined in the `[package.metadata.component.dependencies]` +table is an error. From 2d8ad30372cbc4672b9e2768c41c5d684e3df2dd Mon Sep 17 00:00:00 2001 From: Peter Huene Date: Thu, 15 Dec 2022 14:26:31 -0800 Subject: [PATCH 02/10] Clean up TOML examples so they parse for highlighting. --- docs/design/registries.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/design/registries.md b/docs/design/registries.md index 7fb4e70c..04881475 100644 --- a/docs/design/registries.md +++ b/docs/design/registries.md @@ -95,7 +95,7 @@ This design proposes the following tables in `Cargo.toml`: Specifying dependencies from a registry is done in the general form of: ```toml - = { package = "", version = "", registry = "" } + name = { package = "", version = "", registry = "" } ``` The `registry` field is optional and defaults to `default` (see the `[package.metadata.component.registries]` @@ -105,19 +105,19 @@ This design proposes the following tables in `Cargo.toml`: registries), the short-form differs from what is supported by `cargo`: ```toml - = ":" + name = ":" ``` Which is equivalent to: ```toml - = { package = "", version = "" } + name = { package = "", version = "" } ``` Local wit documents and components are specified using the `path` field: ```toml - = { path = "" } + name = { path = "" } ``` In the future, it may be possible to specify a path to a directory containing @@ -134,7 +134,7 @@ This design proposes the following tables in `Cargo.toml`: registry client implementation): ```toml - = "" + name = "" ``` Dependencies may specify a specific registry to use by specifying the `registry` field: @@ -166,7 +166,7 @@ dependencies by default. The syntax of the value is identical to referencing a world from a package in a `wit` document: -```toml +``` "." ``` From 224e64594d5969a6cfc28059c6aaefc6ef9a30d6 Mon Sep 17 00:00:00 2001 From: Peter Huene Date: Thu, 15 Dec 2022 14:51:59 -0800 Subject: [PATCH 03/10] Add more examples. This commit adds examples for exporting things from a world definition. --- docs/design/registries.md | 111 +++++++++++++++++++++++++++++++++++++- 1 file changed, 110 insertions(+), 1 deletion(-) diff --git a/docs/design/registries.md b/docs/design/registries.md index 04881475..7509b2cd 100644 --- a/docs/design/registries.md +++ b/docs/design/registries.md @@ -358,7 +358,7 @@ document packages only; `regex` and `transcoder` are not imported automatically as in the previous example. The resulting type for this component will the same as the previous example, -but it will also export a `parse` function of type `string -> result<_, string>` +but it will also export a `parse` function of type `(string) -> result<_, string>` as specified in the world. The wit document package names of `wasi`, `regex`, and `transcoder` map @@ -366,3 +366,112 @@ directly to the names of the dependencies in `[package.metadata.component.depend Referencing a package in the wit document that is not defined in the `[package.metadata.component.dependencies]` table is an error. + +### Exporting only functions from a component + +An example `Cargo.toml`: + +```toml +[package] +name = "my-component" +version = "1.2.3" + +[dependencies] +# Rust crate dependencies here + +[package.metadata.component] +package = "my-org/my-component" +world = "./component.wit" +``` + +An example `component.wit`: + +```wit +world my-component { + greet: func(name: string) -> string +} +``` + +Here this component has no component registry dependencies and only exports a +single function named `greet` with type `(string) -> string`. + +### Exporting an interface directly from a component + +An example `Cargo.toml`: + +```toml +[package] +name = "my-component" +version = "1.2.3" + +[dependencies] +# Rust crate dependencies here + +[package.metadata.component] +package = "my-org/my-component" +world = "./component.wit" + +[package.metadata.component.dependencies] +http-types = "http/types:1.2.3" +``` + +An example `component.wit`: + +```wit +world my-component { + import downstream: http-types.handler + + include http-types.handler # proposed wit syntax +} +``` + +In this example, the authored component doesn't target any particular world. + +It imports a purely abstract HTTP `handler` interface from wit package `http/types` +with name `downstream`. As this import comes from a wit package and not a +component package, it offers no hints to any composition tooling as to what +implementation of the downstream "handler" to link with in the future. + +It then exports the same handler interface directly from the component by including its exports. + +This allows the component to act as a middleware for some other HTTP handler: +it may forward requests to the downstream handler (possibly post-processing the +response) or it may respond to the request itself. + +### Exporting a named interface from a component + +An example `Cargo.toml`: + +```toml +[package] +name = "my-component" +version = "1.2.3" + +[dependencies] +# Rust crate dependencies here + +[package.metadata.component] +package = "my-org/my-component" +world = "./component.wit" + +[package.metadata.component.dependencies] +http-types = "http/types:1.2.3" +``` + +An example `component.wit`: + +```wit +world my-component { + import downstream: http-types.handler + + handler: http-types.handler +} +``` + +This example is nearly identical to the previous, except instead of directly +exporting the handler functions from the component, it exports an instance +named `handler` that exports the functions. + +The difference lies in the wit syntax: the former example uses the `include` +syntax to indicate the interface's exports should be included in the world's +exports and this example exports the interface with the name `handler`. From c0fe1b6082d000949965341dcd619a822385e5c5 Mon Sep 17 00:00:00 2001 From: Peter Huene Date: Thu, 15 Dec 2022 15:14:04 -0800 Subject: [PATCH 04/10] Remove `file://` URLs for local filesystem registries. --- docs/design/registries.md | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/docs/design/registries.md b/docs/design/registries.md index 7509b2cd..574d5c53 100644 --- a/docs/design/registries.md +++ b/docs/design/registries.md @@ -102,7 +102,7 @@ This design proposes the following tables in `Cargo.toml`: table below). As packages in a component registry are namespaced (unlike Rust crate - registries), the short-form differs from what is supported by `cargo`: + registries), the shorthand form differs from what is supported by `cargo`: ```toml name = ":" @@ -137,9 +137,16 @@ This design proposes the following tables in `Cargo.toml`: name = "" ``` + This is the shorthand form of: + + ```toml + name = { url = "" } + ``` + Dependencies may specify a specific registry to use by specifying the `registry` field: ```toml + [package.metadata.component.dependencies] foo = { package = "ns/foo", version = "0.1.0", registry = "my-registry" } ``` @@ -148,12 +155,21 @@ This design proposes the following tables in `Cargo.toml`: override the built-in default in `cargo-component` (expected to be a future Bytecode Alliance component registry instance). - A registry URL may use the `file://` scheme to point at local directory that - may have vendored packages. This "local registry" will be the first supported - registry implementation in `cargo-component` while the implementation of + A local filesystem registry (i.e. a directory containing vendored packages + and their package logs) may be specified using the `path` field of a registry + entry: + + ```toml + name = { path = "" } + ``` + + Local filesystem registries will be the first supported registry + implementation in `cargo-component` while the implementation of component registries is still in progress. - It should be possible to specify the registries at the workspace root `Cargo.toml` as well, allowing for a single set of registries to be used across a workspace. + It should be possible to specify the registries at the workspace root `Cargo.toml` + as well, allowing for a single set of registries to be used across a + workspace. #### The `targets` field From 5e1025ce2f5858ae64cd3b26340247670c68fbe0 Mon Sep 17 00:00:00 2001 From: Peter Huene Date: Thu, 15 Dec 2022 16:36:01 -0800 Subject: [PATCH 05/10] Fix typo. --- docs/design/registries.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/design/registries.md b/docs/design/registries.md index 574d5c53..d728df1b 100644 --- a/docs/design/registries.md +++ b/docs/design/registries.md @@ -29,7 +29,7 @@ The design follows these goals: * **Don't force wit on users** - * The design should enable users to use on components from a registry without + * The design should enable users to use components from a registry without forcing them to immediately learn wit. * **Fully support the component model** From 3f34f7fd1ba708949142dbf44fb922622af14e27 Mon Sep 17 00:00:00 2001 From: Peter Huene Date: Fri, 16 Dec 2022 10:43:56 -0800 Subject: [PATCH 06/10] Update docs/design/registries.md Co-authored-by: Brian --- docs/design/registries.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/design/registries.md b/docs/design/registries.md index d728df1b..9ff5d0a7 100644 --- a/docs/design/registries.md +++ b/docs/design/registries.md @@ -365,7 +365,7 @@ world my-component { import transcoder: transcoder - parse: func(x: string) -> result<_, string> + export parse: func(x: string) -> result<_, string> } ``` From a787641663c9f552e50b0463092c3438c960abd0 Mon Sep 17 00:00:00 2001 From: Peter Huene Date: Fri, 16 Dec 2022 10:44:03 -0800 Subject: [PATCH 07/10] Update docs/design/registries.md Co-authored-by: Brian --- docs/design/registries.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/design/registries.md b/docs/design/registries.md index 9ff5d0a7..08276682 100644 --- a/docs/design/registries.md +++ b/docs/design/registries.md @@ -480,7 +480,7 @@ An example `component.wit`: world my-component { import downstream: http-types.handler - handler: http-types.handler + export handler: http-types.handler } ``` From a983fe80d188de86db22c33c0f414f44cc49e4af Mon Sep 17 00:00:00 2001 From: Peter Huene Date: Fri, 16 Dec 2022 10:44:09 -0800 Subject: [PATCH 08/10] Update docs/design/registries.md Co-authored-by: Brian --- docs/design/registries.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/design/registries.md b/docs/design/registries.md index 08276682..a20c0a9e 100644 --- a/docs/design/registries.md +++ b/docs/design/registries.md @@ -404,7 +404,7 @@ An example `component.wit`: ```wit world my-component { - greet: func(name: string) -> string + export greet: func(name: string) -> string } ``` From f8bf935bd045b90de6c6482f144217f9b184e114 Mon Sep 17 00:00:00 2001 From: Peter Huene Date: Wed, 1 Feb 2023 16:00:39 -0800 Subject: [PATCH 09/10] Update the `targets` field in the component metadata. This commit updates the `targets` field in the component metadata to better represent targeting a local world via a `path` field. As a result, the previous `world` field has been removed. --- docs/design/registries.md | 226 ++++++++------------------------------ 1 file changed, 47 insertions(+), 179 deletions(-) diff --git a/docs/design/registries.md b/docs/design/registries.md index a20c0a9e..e03d8908 100644 --- a/docs/design/registries.md +++ b/docs/design/registries.md @@ -78,12 +78,8 @@ This design proposes the following tables in `Cargo.toml`: * `targets` - a target world for the component being authored; this causes bindings to be generated for the specified world's imports and exports. - - * `world` - a path to a wit document specifying a world definition for the - component being authored; it must contain a `world` with the same name as - the current crate name. - The `targets` and `world` fields are mutually exclusive. + See below for more information on the `targets` field. * `[package.metadata.component.dependencies]` @@ -176,88 +172,53 @@ This design proposes the following tables in `Cargo.toml`: The `targets` field in `[package.metadata.component]` is used to specify a world the component is intended to target. -If not specified, the component will only import from component package -dependencies by default. +Specifying the target is done in the general form of: -The syntax of the value is identical to referencing a world from a package in a -`wit` document: - -``` -"." +```toml +targets = { dependency = "", document = "", world = "" } ``` -Here `package-name` is the name of one of the dependencies in `[package.metadata.component.dependencies]`. - -Assuming an entry for a `wasi` dependency in `[package.metadata.component.dependencies]` -and the package contains a world named `command`, a component being authored -may specify a value of `wasi.command` to target the `command` world, for -example. - -Bindings will be automatically generated for the specified world's imports and -exports. - -It is an error to specify a name of a package that is not specified in the -dependencies table. - -It is also an error to specify a world name that is not defined in the -specified package. - -#### The `world` field - -The `world` field in `[package.metadata.component]` is used to specify the path -to a wit document defining the authored component's world; the document must contain a world with the same name as the current crate. - -The field exists to grant developers explicit control over the _component type_ -(i.e. its imports and exports) of the component being authored. - -The `world` field is mutually exclusive with the `targets` field as it -is expected that wit should be able to express the targeting relationship (i.e. -via a proposed `include` syntax, perhaps). +If _only_ the `dependency` field is specified, the dependency must be a +component and it signifies that the component being authored will target the +same world as the dependency. -The presence of a `world` field alters the [behavior of dependencies](#dependency-behavior). +Otherwise, the specified dependency must be a wit package and the `document` +field is required. The `world` field remains optional; if not present the +default world of the specified document is used. -#### Dependency behavior +The `targets` field has a shorthand form of `"[.[.]]"`. -The presence of a `world` field in `[package.metadata.component]` alters the -behavior of dependencies. +For example, the following: -When the `world` field _is not_ specified: - -* There should be at most one wit package dependency and it must - be used in the `targets` field, if present; defining a dependency on a wit - package not used in a `targets` field will result in an "unused dependency" - warning from `cargo-component`. - -* Any number of component package dependencies are allowed; bindings will be - generated to (instance) import the component's directly exported functions; - the imports are _in addition to_ any imports from a targeted world. - -When the `world` field _is_ specified: +```toml +targets = "wasi.cli.command" +``` -* Any number of wit and component package dependencies are allowed. +is equivalent to: -* The dependencies are mapped to package references in the specified wit - document. +```toml +targets = { dependency = "wasi", document = "cli", world = "command" } +``` -* Component package dependencies are not automatically translated to imports as - it is expected the the world being defined will describe how to import them. +Components may target a local wit package by specifying the `path` field: -## Tooling to extract a world from `Cargo.toml`. +```toml +targets = { path = "", world = "" } +``` -It is expected that `cargo-component` will have a subcommand for extracting a -wit document from a `Cargo.toml` file that does not already contain a `world` -field. +The path will be parsed as a wit document and it may reference external packages +as specified in the `package.metadata.component.dependencies` table. If `world` is omitted in this form, the document must define a default world. -It would then add the `world` field to the `Cargo.toml` to point at the -extracted wit document. +Components that target a local wit document _will not_ automatically import +from component package dependencies; it is expected that the document will +fully describe the imports and exports of the component. -The extracted wit document should be semantically equivalent to the world -synthesized by `cargo-component` from the `targets` field and component package -dependencies when the `world` field is not present. +Components that target a dependency's world will _additionally import_ from +any component package dependencies in addition to the imports of the targeted world ## Examples -### Targeting only a world from a registry +### Targeting a world from a dependency An example `Cargo.toml`: @@ -271,7 +232,7 @@ version = "1.2.3" [package.metadata.component] package = "my-org/my-component" -targets = "wasi.command" +targets = "wasi.cli.command" [package.metadata.component.dependencies] wasi = "webassembly/wasi:1.2.3" @@ -284,9 +245,9 @@ by the `command` world and _must_ export what is expected to be exported by the `command` world via the generated bindings. In theory, the authored component could then simply run in any host that -supports the `wasi.command` world (e.g. a future wasmtime CLI). +supports the `wasi.cli.command` world (e.g. a future Wasmtime CLI). -### Targeting a world and using other components from a registry +### Targeting a dependency's world and using other components from a registry An example `Cargo.toml`: @@ -300,7 +261,7 @@ version = "1.2.3" [package.metadata.component] package = "my-org/my-component" -targets = "wasi.command" +targets = "wasi.cli.command" [package.metadata.component.dependencies] wasi = "webassembly/wasi:1.2.3" @@ -308,7 +269,8 @@ regex = "fancy-components/regex:1.0.0" transcoder = "fancy-components/transcoder:1.0.0" ``` -In this example, the component still targets the `wasi.command` world as above. +In this example, the component still targets the `wasi.cli.command` world as +above. However, it will also import an _instance_ named `regex` and an instance named `transcoder` that export the functions directly exported by the `regex` and @@ -321,18 +283,6 @@ default. As they are instance imports, the authored component may still be linked against alternative implementations provided they implement the expected interfaces according to component model subtyping rules. -Running the proposed command to generate a wit document from this `Cargo.toml` should create a wit file that looks something like: - -```wit -world my-component { - include wasi.command # proposed syntax for including a world in another - - import regex: regex - - import transcoder: transcoder -} -``` - ### Defining a custom world for a component An example `Cargo.toml`: @@ -347,7 +297,7 @@ version = "1.2.3" [package.metadata.component] package = "my-org/my-component" -world = "./component.wit" +targets = { path = "component.wit" } [package.metadata.component.dependencies] wasi = "webassembly/wasi:1.2.3" @@ -358,20 +308,19 @@ transcoder = "fancy-components/transcoder:1.0.0" An example `component.wit`: ```wit -world my-component { - include wasi.command # proposed syntax for including a world in another - - import regex: regex +default world my-component { + include wasi.cli.command # a proposed syntax for including a world in another - import transcoder: transcoder + import regex: regex.root + import transcoder: transcoder.root export parse: func(x: string) -> result<_, string> } ``` -Because the `world` field is specified, the dependencies are interpreted as wit -document packages only; `regex` and `transcoder` are not imported automatically -as in the previous example. +Because the `targets` specifies a local wit document, the dependencies are +interpreted as wit external packages only; `regex` and `transcoder` are not +imported automatically as in the previous example. The resulting type for this component will the same as the previous example, but it will also export a `parse` function of type `(string) -> result<_, string>` @@ -397,97 +346,16 @@ version = "1.2.3" [package.metadata.component] package = "my-org/my-component" -world = "./component.wit" +targets = { path = "component.wit" } ``` An example `component.wit`: ```wit -world my-component { +default world my-component { export greet: func(name: string) -> string } ``` Here this component has no component registry dependencies and only exports a single function named `greet` with type `(string) -> string`. - -### Exporting an interface directly from a component - -An example `Cargo.toml`: - -```toml -[package] -name = "my-component" -version = "1.2.3" - -[dependencies] -# Rust crate dependencies here - -[package.metadata.component] -package = "my-org/my-component" -world = "./component.wit" - -[package.metadata.component.dependencies] -http-types = "http/types:1.2.3" -``` - -An example `component.wit`: - -```wit -world my-component { - import downstream: http-types.handler - - include http-types.handler # proposed wit syntax -} -``` - -In this example, the authored component doesn't target any particular world. - -It imports a purely abstract HTTP `handler` interface from wit package `http/types` -with name `downstream`. As this import comes from a wit package and not a -component package, it offers no hints to any composition tooling as to what -implementation of the downstream "handler" to link with in the future. - -It then exports the same handler interface directly from the component by including its exports. - -This allows the component to act as a middleware for some other HTTP handler: -it may forward requests to the downstream handler (possibly post-processing the -response) or it may respond to the request itself. - -### Exporting a named interface from a component - -An example `Cargo.toml`: - -```toml -[package] -name = "my-component" -version = "1.2.3" - -[dependencies] -# Rust crate dependencies here - -[package.metadata.component] -package = "my-org/my-component" -world = "./component.wit" - -[package.metadata.component.dependencies] -http-types = "http/types:1.2.3" -``` - -An example `component.wit`: - -```wit -world my-component { - import downstream: http-types.handler - - export handler: http-types.handler -} -``` - -This example is nearly identical to the previous, except instead of directly -exporting the handler functions from the component, it exports an instance -named `handler` that exports the functions. - -The difference lies in the wit syntax: the former example uses the `include` -syntax to indicate the interface's exports should be included in the world's -exports and this example exports the interface with the name `handler`. From a1ad16c719039b8b9e014dcf6f89b94f71267ee8 Mon Sep 17 00:00:00 2001 From: Peter Huene Date: Thu, 2 Feb 2023 12:07:52 -0800 Subject: [PATCH 10/10] Update docs/design/registries.md Co-authored-by: Brian --- docs/design/registries.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/design/registries.md b/docs/design/registries.md index e03d8908..d1fd6224 100644 --- a/docs/design/registries.md +++ b/docs/design/registries.md @@ -322,7 +322,7 @@ Because the `targets` specifies a local wit document, the dependencies are interpreted as wit external packages only; `regex` and `transcoder` are not imported automatically as in the previous example. -The resulting type for this component will the same as the previous example, +The resulting type for this component will be the same as the previous example, but it will also export a `parse` function of type `(string) -> result<_, string>` as specified in the world.