Skip to content

Commit 7a58bdb

Browse files
authored
Merge pull request #107 from rylev/add-example
Add basic example of wac CLI and programatic API
2 parents 4746b71 + 474c10a commit 7a58bdb

File tree

8 files changed

+145
-1
lines changed

8 files changed

+145
-1
lines changed

Cargo.lock

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@ wit = ["wac-resolver/wit"]
4848
registry = ["wac-resolver/registry", "dep:indicatif", "dep:warg-client", "dep:warg-protocol"]
4949
native-tls-vendored = ["warg-client?/native-tls-vendored"]
5050

51+
[workspace]
52+
members = ["examples/programmatic"]
53+
5154
[workspace.dependencies]
5255
wac-parser = { path = "crates/wac-parser", version = "0.1.0", default-features = false }
5356
wac-resolver = { path = "crates/wac-resolver", version = "0.1.0", default-features = false }
@@ -64,7 +67,10 @@ clap = { version = "4.5.4", features = ["derive"] }
6467
semver = { version = "1.0.22", features = ["serde"] }
6568
pretty_env_logger = "0.5.0"
6669
log = "0.4.21"
67-
tokio = { version = "1.37.0", default-features = false, features = ["macros", "rt-multi-thread"] }
70+
tokio = { version = "1.37.0", default-features = false, features = [
71+
"macros",
72+
"rt-multi-thread",
73+
] }
6874
owo-colors = { version = "4.0.0", features = ["supports-colors"] }
6975
indexmap = { version = "2.2.6", features = ["serde"] }
7076
id-arena = "2.2.1"

examples/README.md

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
# Examples
2+
3+
This example is composed of three different but equal ways for composing two example components together
4+
* two ways using the `wac` CLI
5+
* using the `wac-graph` library crate
6+
7+
The example uses two input components (both located in the `deps` directory) that will be composed together:
8+
9+
The `hello` component exports a function `hello` which returns a string:
10+
11+
```bash
12+
# Print the wit for the hello component
13+
$ wasm-tools component wit deps/example/hello.wasm
14+
package root:component;
15+
16+
world root {
17+
export hello: func() -> string;
18+
}
19+
```
20+
21+
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:
22+
23+
```bash
24+
# Print the wit for the greeter component
25+
$ wasm-tools component wit deps/example/greeter.wasm
26+
package root:component;
27+
28+
world root {
29+
import hello: func() -> string;
30+
31+
export greet: func() -> string;
32+
}
33+
```
34+
35+
The resulting composed component will therefore only have the exported `greet` function originally from the `greeter` component.
36+
37+
## `wac encode`
38+
39+
`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.
40+
41+
Running the following command should produce a new component that is the composition of the `hello` and `greeter` components.
42+
43+
```bash
44+
wac encode script.wac -o composed.wasm
45+
```
46+
47+
*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.
48+
49+
## `wac plug`
50+
51+
`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").
52+
53+
In this example, we can do this with the following invocation:
54+
55+
```bash
56+
wac plug --plug deps/example/hello.wasm deps/example/greeter.wasm -o composed.wasm
57+
```
58+
59+
## Programmatic Graph API
60+
61+
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:
62+
63+
```bash
64+
cargo run
65+
```

examples/deps/example/greeter.wasm

56.9 KB
Binary file not shown.

examples/deps/example/hello.wasm

38.5 KB
Binary file not shown.

examples/programmatic/Cargo.toml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
[package]
2+
name = "programmatic-example"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
[dependencies]
7+
wac-graph = { path = "../../crates/wac-graph" }

examples/programmatic/src/main.rs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
use wac_graph::{types::Package, CompositionGraph, EncodeOptions};
2+
3+
fn main() {
4+
let mut graph = CompositionGraph::new();
5+
6+
// Register the package dependencies into the graph
7+
let package = Package::from_file(
8+
"hello",
9+
None,
10+
"../deps/example/hello.wasm",
11+
graph.types_mut(),
12+
)
13+
.unwrap();
14+
let hello = graph.register_package(package).unwrap();
15+
let package = Package::from_file(
16+
"greeter",
17+
None,
18+
"../deps/example/greeter.wasm",
19+
graph.types_mut(),
20+
)
21+
.unwrap();
22+
let greeter = graph.register_package(package).unwrap();
23+
24+
// Instantiate the hello instance which does not have any arguments
25+
let hello_instance = graph.instantiate(hello);
26+
27+
// Instantiate the greeter instance which has a single argument "hello" which is exported by the hello instance
28+
let greeter_instance = graph.instantiate(greeter);
29+
let hello_export = graph
30+
.alias_instance_export(hello_instance, "hello")
31+
.unwrap();
32+
graph
33+
.set_instantiation_argument(greeter_instance, "hello", hello_export)
34+
.unwrap();
35+
36+
// Alias the "greet" export from the greeter instance
37+
let greet_export = graph
38+
.alias_instance_export(greeter_instance, "greet")
39+
.unwrap();
40+
// Export the "greet" function from the composition
41+
graph.export(greet_export, "greet").unwrap();
42+
43+
// Encode the graph into a WASM binary
44+
let encoding = graph.encode(EncodeOptions::default()).unwrap();
45+
std::fs::write("composition.wasm", encoding).unwrap();
46+
}

examples/script.wac

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package example:composition;
2+
3+
// Instantiate the `hello` component
4+
let hello = new example:hello {};
5+
6+
// Instantiate the `greeter` component plugging its one `hello` import with
7+
// the `hello` export of the `hello` component.
8+
let greeter = new example:greeter {
9+
hello: hello.hello,
10+
};
11+
12+
// Export the greet function from the greeter component
13+
export greeter.greet;

0 commit comments

Comments
 (0)