diff --git a/Cargo.lock b/Cargo.lock index 3e12d94..3738ff7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2497,6 +2497,13 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "programmatic-example" +version = "0.1.0" +dependencies = [ + "wac-graph", +] + [[package]] name = "prost" version = "0.12.3" diff --git a/Cargo.toml b/Cargo.toml index cafdad1..51a0e4d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -45,6 +45,9 @@ wat = ["wac-resolver/wat"] wit = ["wac-resolver/wit"] registry = ["wac-resolver/registry", "indicatif"] +[workspace] +members = ["examples/programmatic"] + [workspace.dependencies] wac-parser = { path = "crates/wac-parser", version = "0.1.0", default-features = false } wac-resolver = { path = "crates/wac-resolver", version = "0.1.0", default-features = false } @@ -61,7 +64,10 @@ clap = { version = "4.5.4", features = ["derive"] } semver = { version = "1.0.22", features = ["serde"] } pretty_env_logger = "0.5.0" log = "0.4.21" -tokio = { version = "1.37.0", default-features = false, features = ["macros", "rt-multi-thread"] } +tokio = { version = "1.37.0", default-features = false, features = [ + "macros", + "rt-multi-thread", +] } owo-colors = { version = "4.0.0", features = ["supports-colors"] } indexmap = { version = "2.2.6", features = ["serde"] } id-arena = "2.2.1" diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 0000000..b3c6777 --- /dev/null +++ b/examples/README.md @@ -0,0 +1,65 @@ +# Examples + +This example is composed of three different but equal ways for composing two example components together +* two ways using the `wac` CLI +* using the `wac-graph` library crate + +The example uses two input components (both located in the `deps` directory) that will be composed together: + +The `hello` component exports a function `hello` which returns a string: + +```bash +# Print the wit for the hello component +$ wasm-tools component wit deps/example/hello.wasm +package root:component; + +world root { + export hello: func() -> string; +} +``` + +The `hello` exported function from `hello.wasm` will be plugged into the `hello` import of the `greeter` component which has the same signature as the `hello` exported function: + +```bash +# Print the wit for the greeter component +$ wasm-tools component wit deps/example/greeter.wasm +package root:component; + +world root { + import hello: func() -> string; + + export greet: func() -> string; +} +``` + +The resulting composed component will therefore only have the exported `greet` function originally from the `greeter` component. + +## `wac encode` + +`wac` can be used as a CLI tool. The `wac encode` command takes a wac script as input which defines how two components are composed together. + +Running the following command should produce a new component that is the composition of the `hello` and `greeter` components. + +```bash +wac encode script.wac -o composed.wasm +``` + +*Note*: `wac encode` expects to find any input components inside of a `deps` folder in a directory named after the namespace part of the input component's name (however, this is configurable with the `--deps-dir` option). In our example, the wac script uses the `example:greeter` and `example:hello` input components so `wac encode` expects to find those components in the `deps/example` directory. + +## `wac plug` + +`wac` also comes with an opinionated CLI option called `wac plug` which will "plug" the exports of one component (the "plug") into equivalently named imports of another component (the "socket"). + +In this example, we can do this with the following invocation: + +```bash +wac plug --plug deps/example/hello.wasm deps/example/greeter.wasm -o composed.wasm +``` + +## Programmatic Graph API + +You can also build the composition using the programmatic API used by the `programmatic` example binary. This can be done by running the following inside the `programmatic` directory: + +```bash +cargo run +``` diff --git a/examples/deps/example/greeter.wasm b/examples/deps/example/greeter.wasm new file mode 100644 index 0000000..dfe64ab Binary files /dev/null and b/examples/deps/example/greeter.wasm differ diff --git a/examples/deps/example/hello.wasm b/examples/deps/example/hello.wasm new file mode 100644 index 0000000..bb27941 Binary files /dev/null and b/examples/deps/example/hello.wasm differ diff --git a/examples/programmatic/Cargo.toml b/examples/programmatic/Cargo.toml new file mode 100644 index 0000000..d13d7f4 --- /dev/null +++ b/examples/programmatic/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "programmatic-example" +version = "0.1.0" +edition = "2021" + +[dependencies] +wac-graph = { path = "../../crates/wac-graph" } diff --git a/examples/programmatic/src/main.rs b/examples/programmatic/src/main.rs new file mode 100644 index 0000000..4309101 --- /dev/null +++ b/examples/programmatic/src/main.rs @@ -0,0 +1,46 @@ +use wac_graph::{types::Package, CompositionGraph, EncodeOptions}; + +fn main() { + let mut graph = CompositionGraph::new(); + + // Register the package dependencies into the graph + let package = Package::from_file( + "hello", + None, + "../deps/example/hello.wasm", + graph.types_mut(), + ) + .unwrap(); + let hello = graph.register_package(package).unwrap(); + let package = Package::from_file( + "greeter", + None, + "../deps/example/greeter.wasm", + graph.types_mut(), + ) + .unwrap(); + let greeter = graph.register_package(package).unwrap(); + + // Instantiate the hello instance which does not have any arguments + let hello_instance = graph.instantiate(hello); + + // Instantiate the greeter instance which has a single argument "hello" which is exported by the hello instance + let greeter_instance = graph.instantiate(greeter); + let hello_export = graph + .alias_instance_export(hello_instance, "hello") + .unwrap(); + graph + .set_instantiation_argument(greeter_instance, "hello", hello_export) + .unwrap(); + + // Alias the "greet" export from the greeter instance + let greet_export = graph + .alias_instance_export(greeter_instance, "greet") + .unwrap(); + // Export the "greet" function from the composition + graph.export(greet_export, "greet").unwrap(); + + // Encode the graph into a WASM binary + let encoding = graph.encode(EncodeOptions::default()).unwrap(); + std::fs::write("composition.wasm", encoding).unwrap(); +} diff --git a/examples/script.wac b/examples/script.wac new file mode 100644 index 0000000..7e7ddb7 --- /dev/null +++ b/examples/script.wac @@ -0,0 +1,13 @@ +package example:composition; + +// Instantiate the `hello` component +let hello = new example:hello {}; + +// Instantiate the `greeter` component plugging its one `hello` import with +// the `hello` export of the `hello` component. +let greeter = new example:greeter { + hello: hello.hello, +}; + +// Export the greet function from the greeter component +export greeter.greet;