Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add example: construct guest resource #24

Merged
merged 12 commits into from
Jan 16, 2025
Merged
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@
/Cargo.lock
/examples/*/target/
/examples/*/Cargo.lock
/examples/*/component.wat
/examples/*/component.wat
examples/guest_resource/src/bindings.rs
97 changes: 97 additions & 0 deletions examples/guest_resource.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
use anyhow::*;
use wasm_component_layer::*;

// The bytes of the component.
const WASM: &[u8] =
include_bytes!("./guest_resource/target/wasm32-unknown-unknown/debug/guest_resource.wasm");

pub fn main() {
// Create a new engine for instantiating a component.
let engine = Engine::new(wasmi_runtime_layer::Engine::default());

// Create a store for managing WASM data and any custom user-defined state.
let mut store = Store::new(&engine, ());

// Parse the component bytes and load its imports and exports.
let component = Component::new(&engine, WASM).unwrap();

// Create a linker that will be used to resolve the component's imports, if any.
let mut linker = Linker::default();

// Create a host interface that will be used to define the host functions that the component can call.
let host_interface = linker
.define_instance("test:guest/log".try_into().unwrap())
.unwrap();

// Define the host function that the component can call.
// This is our `log` function that will be called by the guest component.
host_interface
.define_func(
"log",
Func::new(
&mut store,
FuncType::new([ValueType::String], []),
move |_, params, _results| {
let params = match &params[0] {
Value::String(s) => s,
_ => panic!("Unexpected parameter type"),
};

println!("[HostLog] log");
println!(" └ {}", params.to_string());
Ok(())
},
),
)
.unwrap();

// Create an instance of the component using the linker.
let instance = linker.instantiate(&mut store, &component).unwrap();

// Get the interface that the interface exports.
let interface = instance
.exports()
.instance(&"test:guest/foo".try_into().unwrap())
.unwrap();

// Call the resource constructor for 'bar' using a direct function call
let resource_constructor = interface.func("[constructor]bar").unwrap();

// We need to provide a mutable reference to store the results.
// This can be any Value type, as it will get overwritten by the result.
// It is a Value::Bool here but will be overwritten by a Value::Own(ResourceOwn)
// after we call the constructor.
let mut results = vec![Value::Bool(false)];

// Construct the resource with the argument `42`
resource_constructor
.call(&mut store, &[Value::S32(42)], &mut results)
.unwrap();

let resource = match results[0] {
Value::Own(ref resource) => resource.clone(),
_ => panic!("Unexpected result type"),
};

let borrow_res = resource.borrow(store.as_context_mut()).unwrap();
let arguments = vec![Value::Borrow(borrow_res)];

let mut results = vec![Value::S32(0)];

// Get the `value` method of the `bar` resource
let method_bar_value = interface.func("[method]bar.value").unwrap();

// Call the method `bar.value`, mutate the result
method_bar_value
.call(&mut store, &arguments, &mut results)
.unwrap();

match results[0] {
Value::S32(v) => {
println!("[ResultLog]");
println!(" └ bar.value() = {}", v);
assert_eq!(v, 42);
}
_ => panic!("Unexpected result type"),
}
}
24 changes: 24 additions & 0 deletions examples/guest_resource/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
cargo-features = ["per-package-target"]

[package]
name = "guest_resource"
version = "0.1.0"
edition = "2021"

forced-target = "wasm32-unknown-unknown"

[dependencies]
wit-bindgen = { version = "0.37.0" }

[lib]
crate-type = ["cdylib"]

[profile.release]
codegen-units = 1
opt-level = "s"
debug = false
strip = true
lto = true

[package.metadata.component]
package = "component:guest-resource"
3 changes: 3 additions & 0 deletions examples/guest_resource/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Construct a Guest Resoruce

This example demonstrates how to construct a guest resource.
31 changes: 31 additions & 0 deletions examples/guest_resource/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
wit_bindgen::generate!({
path: "wit/world.wit"
});

export!(Component);

use std::cell::RefCell;

use exports::test::guest::foo::{Guest, GuestBar};
use test::guest::log::log;

struct Component {
val: RefCell<i32>,
}

impl Guest for Component {
type Bar = Self;
}

impl GuestBar for Component {
fn new(val: i32) -> Self {
log(&format!("Creating new Component with value: {}", val));
Component {
val: RefCell::new(val),
}
}

fn value(&self) -> i32 {
*self.val.borrow()
}
}
21 changes: 21 additions & 0 deletions examples/guest_resource/wit/world.wit
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package test:guest;

interface foo {
resource bar {
constructor(value: s32);

/// Returns the value of the resource.
value: func() -> s32;
}
}

interface log {
/// Logs some output from the guest component.
log: func(message: string);
}

/// An example world for the component to target.
world example {
import log;
export foo;
}