# 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 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. * `target` - a target world for the component being authored; this causes bindings to be generated for the specified world's imports and exports. See the [target field](#the-target-field) section below for more information. * `[package.metadata.component.dependencies]` This table contains information about component dependencies. Each entry in the table must reference a WebAssembly component. A named import will be added for each component dependency in the world that the component targets. The dependencies may come from a component registry or local components. Specifying dependencies from a registry is done in the general form of: ```toml id = { 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), package ids must be quoted. For example: ```toml "my:component" = "0.1.0" ``` Which is equivalent to: ```toml "my:component" = { package = "my:component", version = "0.1.0" } ``` Local components are specified using the `path` field: ```toml id = { 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. * `[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 name = "" ``` This is the shorthand form of: ```toml name = { url = "" } ``` Component dependencies may specify a specific registry to use by specifying the `registry` field: ```toml [package.metadata.component.dependencies] "foo:bar" = { package = "foo:bar", 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). 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 `target` field The `target` field in `[package.metadata.component]` is used to specify a world the component is intended to target. There are two ways to specify a target world: referencing a registry package or using a local WIT document. #### Targeting a registry package You may use a shorthand form of specifying a target as follows: ```toml [package.metadata.component] target = "[/]@" ``` This is equivalent to: ```toml [package.metadata.component.target] package = "" version = "" world = "" # optional ``` The supported fields of `target` when referencing a registry package are: ```toml [package.metadata.component.target] package = "" version = "" world = "" registry = "" ``` The `package` and `version` fields are required. The `package`, `version`, and `registry` fields describe which package is being referenced. The `registry` field is optional. The `world` field is optional and defaults to the default world resolution of the WIT package as defined by [`Resolve::select_world`][select-world]. The referenced package may be either a WIT package or a component. [select-world]: https://docs.rs/wit-parser/latest/wit_parser/struct.Resolve.html#method.select_world #### Targeting a local WIT document Specifying a target from a local WIT document: ```toml [package.metadata.component.target] path = "" world = "" [package.metadata.component.target.dependencies] "" = "" # or any of the other forms of specifying a dependency # ... ``` The `path` field is required and specifies the path to the WIT document defining a world to target. The `world` field is optional and defaults to the default world resolution of the WIT package as defined by [`Resolve::select_world`][select-world]. The `[package.metadata.component.target.dependencies]` table is optional and defines the WIT package dependencies that may be referenced in the local WIT document. A non-empty `dependencies` table is only allowed when targeting a local WIT document. Each dependency in the table must be a WIT package. Referencing an external package in the WIT document that is not defined in the `dependencies` table is an error. ## Examples ### Targeting a world from a dependency 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" target = "wasi:cli/command@1.2.3" [package.metadata.component.dependencies] ``` 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 default `command` world and _must_ export what is expected to be exported by the 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 dependency's 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" target = "wasi:cli/command@1.2.3" [package.metadata.component.dependencies] "fancy-components:regex" = "1.0.0" "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. ### 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" [package.metadata.component.target.dependencies] "wasi:cli" = "1.2.3" [package.metadata.component.dependencies] "fancy-components:regex" = "1.0.0" "fancy-components:transcoder" = "1.0.0" ``` An example `wit/component.wit`: ```wit package my-org:my-component world my-world { include wasi:cli/command // a theoretical syntax for including a world in another export parse: func(x: string) -> result<_, string> } ``` 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. The WIT document package id of `wasi:cli` maps directly to the name of the dependency in `[package.metadata.component.target.dependencies]`. ### 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" [package.metadata.component.target] path = "wit" ``` An example `wit/component.wit`: ```wit package my-org:my-component world my-world { 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`.