Hello! This is my first post on Zulip, I hope I am doing it right. Thanks for all your hard work everyone!
I am currently diving into cargo component
and wasmtime
to evaluate their use for a (simple) plugin system in Rust.
I have the following wasm-component
created via cargo component
:
Cargo.toml
[package]
name = "test-plugin-lib"
version = "0.1.0"
edition = "2021"
[dependencies]
wit-bindgen-rt = { version = "0.26.0", features = ["bitflags"] }
[lib]
crate-type = ["cdylib"]
[profile.release]
codegen-units = 1
opt-level = "s"
debug = false
strip = true
lto = true
[package.metadata.component]
package = "component:test-plugin-lib"
[package.metadata.component.dependencies]
wit/world.wit
package nerdachse:test-plugin-lib@0.1.0;
interface register {
init: func() -> string;
}
interface info {
info: func() -> plugininfo;
record plugininfo {
name: string,
version: string,
permissions: list<string>,
}
}
world plugin {
export info;
export register;
}
lib.rs
#[allow(warnings)]
mod bindings;
use bindings::exports::nerdachse::test_plugin_lib::info::Guest as InfoGuest;
use bindings::exports::nerdachse::test_plugin_lib::info::Plugininfo;
use bindings::exports::nerdachse::test_plugin_lib::register::Guest as RegisterGuest;
//use bindings::Guest as WorldGuest;
struct Component;
impl RegisterGuest for Component {
fn init() -> String {
"I want moar!".to_owned()
}
}
impl InfoGuest for Component {
fn info() -> Plugininfo {
Plugininfo {
name: "testplugin".to_owned(),
version: "0.0.1".to_owned(),
permissions: vec!["ladida".to_owned(), "permission2".to_string()],
}
}
}
bindings::export!(Component with_types_in bindings);
And a host:
Cargo.toml
[package]
name = "simple-host"
version = "0.1.0"
edition = "2021"
[dependencies]
anyhow = "1.0.86"
wasmtime = "22.0.0"
wasmtime-wasi = "22.0.0"
Now I want to call the info
function for all plugins in a path:
let mut plugin_instances: Vec<(PathBuf, wasmtime::component::Instance)> = vec![];
let mut store = wasmtime::Store::new(&engine, host);
for (path, component) in plugins {
match linker.instantiate(&mut store, &component) {
Ok(instance) => {
plugin_instances.push((path, instance));
}
Err(e) => {
eprintln!("Failed to instantiate plugin@{path:?}: {e:?}");
}
}
}
for (path, plugin) in plugin_instances {
let info_func = {
let mut exports: wasmtime::component::Exports = plugin.exports(&mut store);
let mut register = exports
.instance("info")
.expect(&format!("plugin@{:?} to have info instance", path));
match register.typed_func::<(), (String,)>("info") {
Ok(info) => info,
Err(e) => {
eprintln!("plugin@{:?} does not have an info function: {}!", path, e);
continue;
}
}
};
Running this gives me the error:
Running `target/release/simple-host`
Found wasm plugin @ "../compiled_plugins_folder/test_plugin_lib.wasm"
thread 'main' panicked at src/main.rs:80:18:
plugin@"../compiled_plugins_folder/test_plugin_lib.wasm" to have info instance
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
And I don't understand why. With "freestanding" functions inside the wit-world
it works fine, but never with interfaces
.
I'd appreciate any help I can get!
ah, I changed the package name to package test:guest;
and the call to .instance("test:guest/info")
and that's how it works. I find it a bit strange that you can call a world function without having to specify the package but not an instance. Now I need to think how I can ensure that this works for all plugins, because the package (name) suddenly becomes a burden :/
You might be interseted in exploring these examples of using bindgen!
to avoid writing down all the names here
Last updated: Dec 23 2024 at 14:03 UTC