Froidoh opened issue #8857:
Okay, so I am a total noob with
wasm(time)
and my mistake is totally a layer 8 problem. But after digging I couldn't find the solution, so here I am, opening an issue (sorry for that!)What I want:
I want to leverage
cargo component
andwasmtime
to create a simple plugin system experiment.So I created a new (binary) webassembly component via:
cargo component new test-plugin
.[package] name = "test-plugin" version = "0.1.0" edition = "2021" [dependencies] wit-bindgen-rt = { version = "0.26.0", features = ["bitflags"] } [profile.release] codegen-units = 1 opt-level = "s" debug = false strip = true lto = true # [package.metadata.component] # package = "component:test-plugin" [package.metadata.component.target] path = "wit" world = "test-plugin" [package.metadata.component.dependencies]
package justatest:testplugin@0.1.0; interface greetings { hello: func(s: string) -> string; } world test-plugin { export greetings; }
And my
src/main.rs
#[allow(warnings)] mod bindings; use bindings::exports::justatest::testplugin::greetings::Guest; struct Component; impl Guest for Component { fn hello(s: String) -> String { format!("Hello {s}") } } bindings::export!(Component with_types_in bindings); fn main() { println!("Hello from test-plugin!"); }
I then generated the binary via
cargo component build --release
and let it run via:wasmtime target/wasm32-wasi/release/test-plugin.wasm
Hello from test-plugin!As expected. Via the
wasmtime
cli I cannot--invoke
any functions on awasm32-wasi
webassembly module (at least that's what the cli tells me)...So then I created a
simple-host
as a "normal", "native" rust project, compiled viallvm
: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" #wit-component = "0.211.1"
use wasmtime::{ component::{Component, Linker, Val}, Config, Engine, }; use wasmtime_wasi::{WasiCtxBuilder, WasiView}; /* fn convert_to_component(path: impl AsRef<Path>) -> wasmtime::Result<Vec<u8>> { let bytes = &std::fs::read(&path).context("failed to read input file")?; wit_component::ComponentEncoder::default() .module(&bytes)? .encode() } */ struct Host { ctx: wasmtime_wasi::WasiCtx, table: wasmtime_wasi::ResourceTable, } impl WasiView for Host { fn table(&mut self) -> &mut wasmtime_wasi::ResourceTable { &mut self.table } fn ctx(&mut self) -> &mut wasmtime_wasi::WasiCtx { &mut self.ctx } } fn main() -> wasmtime::Result<()> { let mut config = Config::new(); config.wasm_component_model(true); //config.async_support(true); let engine = Engine::new(&config)?; // Load the component from disk let component = std::fs::read("../test-plugin/target/wasm32-wasi/release/test-plugin.wasm")?; let component = Component::from_binary(&engine, &component)?; //println!("bytes: {bytes:?}"); // As `cargo component` by default builds as `wasm32-wasi` webassembly module, we need to also include `wasmtime-wasi` and provide certain native functionality (in my own words) let mut wasi = WasiCtxBuilder::new(); wasi.arg("--help"); let host = Host { ctx: wasi.build(), table: wasmtime_wasi::ResourceTable::new(), }; let mut store = wasmtime::Store::new(&engine, host); // Configure the linker let mut linker = Linker::new(&engine); wasmtime_wasi::add_to_linker_sync(&mut linker)?; // The component expects one import `name` that // takes no params and returns a string linker .root() //.func_wrap("name", |_store, _params: ()| Ok((String::from("Alice"),)))? ; println!("have linker"); // Instantiate the component let instance = linker.instantiate(&mut store, &component)?; // TODO: find out whethere there is a way to programmatically "explore" an instance, // to see it's "API" so to speak. // Could be useful for generating some docs or whatnot /* let mut exports = instance.exports(&mut store); let root = exports.instance("greetings").expect("greetings to exist"); let modules = root.modules(); for (name, module) in modules { println!("module: {module:?}") } println!("no modules?!"); */ println!("have instance"); /* // TODO: I definitely misunderstood modules, because "greetings" does not exist let module = instance .get_module(&mut store, "greetings") .expect("module not found"); */ // Call the `greet` function let func = instance .get_func(&mut store, "hello") .expect("hello export not found"); let mut result = [wasmtime::component::Val::String("".into())]; func.call( &mut store, &[Val::String("freund nachtigall".to_string())], &mut result, )?; println!("Greeting: {:?}", result); Ok(()) }
When I run this I get a
panic
forhello export not found
.What am I doing wrong?
On the other hand, if I create a library component via
cargo component new test-plugin-lib --lib
which creates a default wit file and impl for it:package component:test-plugin-lib; /// An example world for the component to target. world example { export hello-world: func() -> string; }
and then load this and call it like so:
let func = instance .get_func(&mut store, "hello-world") .expect("hello export not found"); let mut result = [wasmtime::component::Val::String("".into())]; func.call( &mut store, &[], //&[Val::String("freund nachtigall".to_string())], &mut result, )?; println!("Greeting: {:?}", result);
it works like a charm.
So I guess my problem boils down to one or two things:
1) How do I get a function in a "namespace" programmatically like
hello
if the wit file looks like:package justatest:testplugin@0.1.0; interface greetings { hello: func(s: string) -> string; } world test-plugin { export greetings; }
2) Maybe it's different when it's a "
binary
" webassembly component in contrast to a lib?
Froidoh edited issue #8857:
Okay, so I am a total noob with
wasm(time)
and my mistake is totally a layer 8 problem. But after digging I couldn't find the solution, so here I am, opening an issue (sorry for that!)What I want:
I want to leverage
cargo component
andwasmtime
to create a simple plugin system experiment.So I created a new (binary) webassembly component via:
cargo component new test-plugin
.[package] name = "test-plugin" version = "0.1.0" edition = "2021" [dependencies] wit-bindgen-rt = { version = "0.26.0", features = ["bitflags"] } [profile.release] codegen-units = 1 opt-level = "s" debug = false strip = true lto = true # [package.metadata.component] # package = "component:test-plugin" [package.metadata.component.target] path = "wit" world = "test-plugin" [package.metadata.component.dependencies]
package justatest:testplugin@0.1.0; interface greetings { hello: func(s: string) -> string; } world test-plugin { export greetings; }
And my
src/main.rs
#[allow(warnings)] mod bindings; use bindings::exports::justatest::testplugin::greetings::Guest; struct Component; impl Guest for Component { fn hello(s: String) -> String { format!("Hello {s}") } } bindings::export!(Component with_types_in bindings); fn main() { println!("Hello from test-plugin!"); }
I then generated the binary via
cargo component build --release
and let it run via:wasmtime target/wasm32-wasi/release/test-plugin.wasm
Hello from test-plugin!As expected. Via the
wasmtime
cli I cannot--invoke
any functions on awasm32-wasi
webassembly module (at least that's what the cli tells me)...So then I created a
simple-host
as a "normal", "native" rust project, compiled viallvm
: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" #wit-component = "0.211.1"
use wasmtime::{ component::{Component, Linker, Val}, Config, Engine, }; use wasmtime_wasi::{WasiCtxBuilder, WasiView}; /* fn convert_to_component(path: impl AsRef<Path>) -> wasmtime::Result<Vec<u8>> { let bytes = &std::fs::read(&path).context("failed to read input file")?; wit_component::ComponentEncoder::default() .module(&bytes)? .encode() } */ struct Host { ctx: wasmtime_wasi::WasiCtx, table: wasmtime_wasi::ResourceTable, } impl WasiView for Host { fn table(&mut self) -> &mut wasmtime_wasi::ResourceTable { &mut self.table } fn ctx(&mut self) -> &mut wasmtime_wasi::WasiCtx { &mut self.ctx } } fn main() -> wasmtime::Result<()> { let mut config = Config::new(); config.wasm_component_model(true); //config.async_support(true); let engine = Engine::new(&config)?; // Load the component from disk let component = std::fs::read("../test-plugin/target/wasm32-wasi/release/test-plugin.wasm")?; let component = Component::from_binary(&engine, &component)?; //println!("bytes: {bytes:?}"); // As `cargo component` by default builds as `wasm32-wasi` webassembly module, we need to also include `wasmtime-wasi` and provide certain native functionality (in my own words) let mut wasi = WasiCtxBuilder::new(); wasi.arg("--help"); let host = Host { ctx: wasi.build(), table: wasmtime_wasi::ResourceTable::new(), }; let mut store = wasmtime::Store::new(&engine, host); // Configure the linker let mut linker = Linker::new(&engine); wasmtime_wasi::add_to_linker_sync(&mut linker)?; // The component expects one import `name` that // takes no params and returns a string linker .root() //.func_wrap("name", |_store, _params: ()| Ok((String::from("Alice"),)))? ; println!("have linker"); // Instantiate the component let instance = linker.instantiate(&mut store, &component)?; // TODO: find out whethere there is a way to programmatically "explore" an instance, // to see it's "API" so to speak. // Could be useful for generating some docs or whatnot /* let mut exports = instance.exports(&mut store); let root = exports.instance("greetings").expect("greetings to exist"); let modules = root.modules(); for (name, module) in modules { println!("module: {module:?}") } println!("no modules?!"); */ println!("have instance"); /* // TODO: I definitely misunderstood modules, because "greetings" does not exist let module = instance .get_module(&mut store, "greetings") .expect("module not found"); */ // Call the `greet` function let func = instance .get_func(&mut store, "hello") .expect("hello export not found"); let mut result = [wasmtime::component::Val::String("".into())]; func.call( &mut store, &[Val::String("freund nachtigall".to_string())], &mut result, )?; println!("Greeting: {:?}", result); Ok(()) }
When I run this I get a
panic
forhello export not found
.What am I doing wrong? I also tried with:
greetings.hello
andgreetings:hello
forget_func
but to no avail.On the other hand, if I create a library component via
cargo component new test-plugin-lib --lib
which creates a default wit file and impl for it:package component:test-plugin-lib; /// An example world for the component to target. world example { export hello-world: func() -> string; }
and then load this and call it like so:
let func = instance .get_func(&mut store, "hello-world") .expect("hello export not found"); let mut result = [wasmtime::component::Val::String("".into())]; func.call( &mut store, &[], //&[Val::String("freund nachtigall".to_string())], &mut result, )?; println!("Greeting: {:?}", result);
it works like a charm.
So I guess my problem boils down to one or two things:
1) How do I get a function in a "namespace" programmatically like
hello
if the wit file looks like:package justatest:testplugin@0.1.0; interface greetings { hello: func(s: string) -> string; } world test-plugin { export greetings; }
2) Maybe it's different when it's a "
binary
" webassembly component in contrast to a lib?
Froidoh edited issue #8857:
Okay, so I am a total noob with
wasm(time)
and my mistake is totally a layer 8 problem. But after digging I couldn't find the solution, so here I am, opening an issue (sorry for that!)What I want:
I want to leverage
cargo component
andwasmtime
to create a simple plugin system experiment.So I created a new (binary) webassembly component via:
cargo component new test-plugin
.[package] name = "test-plugin" version = "0.1.0" edition = "2021" [dependencies] wit-bindgen-rt = { version = "0.26.0", features = ["bitflags"] } [profile.release] codegen-units = 1 opt-level = "s" debug = false strip = true lto = true # [package.metadata.component] # package = "component:test-plugin" [package.metadata.component.target] path = "wit" world = "test-plugin" [package.metadata.component.dependencies]
package justatest:testplugin@0.1.0; interface greetings { hello: func(s: string) -> string; } world test-plugin { export greetings; }
And my
src/main.rs
#[allow(warnings)] mod bindings; use bindings::exports::justatest::testplugin::greetings::Guest; struct Component; impl Guest for Component { fn hello(s: String) -> String { format!("Hello {s}") } } bindings::export!(Component with_types_in bindings); fn main() { println!("Hello from test-plugin!"); }
I then generated the binary via
cargo component build --release
and let it run via:wasmtime target/wasm32-wasi/release/test-plugin.wasm
Hello from test-plugin!As expected. Via the
wasmtime
cli I cannot--invoke
any functions on awasm32-wasi
webassembly module (at least that's what the cli tells me)...So then I created a
simple-host
as a "normal", "native" rust project, compiled viallvm
: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" #wit-component = "0.211.1"
use wasmtime::{ component::{Component, Linker, Val}, Config, Engine, }; use wasmtime_wasi::{WasiCtxBuilder, WasiView}; /* fn convert_to_component(path: impl AsRef<Path>) -> wasmtime::Result<Vec<u8>> { let bytes = &std::fs::read(&path).context("failed to read input file")?; wit_component::ComponentEncoder::default() .module(&bytes)? .encode() } */ struct Host { ctx: wasmtime_wasi::WasiCtx, table: wasmtime_wasi::ResourceTable, } impl WasiView for Host { fn table(&mut self) -> &mut wasmtime_wasi::ResourceTable { &mut self.table } fn ctx(&mut self) -> &mut wasmtime_wasi::WasiCtx { &mut self.ctx } } fn main() -> wasmtime::Result<()> { let mut config = Config::new(); config.wasm_component_model(true); //config.async_support(true); let engine = Engine::new(&config)?; // Load the component from disk let component = std::fs::read("../test-plugin/target/wasm32-wasi/release/test-plugin.wasm")?; let component = Component::from_binary(&engine, &component)?; //println!("bytes: {bytes:?}"); // As `cargo component` by default builds as `wasm32-wasi` webassembly module, we need to also include `wasmtime-wasi` and provide certain native functionality (in my own words) let mut wasi = WasiCtxBuilder::new(); wasi.arg("--help"); let host = Host { ctx: wasi.build(), table: wasmtime_wasi::ResourceTable::new(), }; let mut store = wasmtime::Store::new(&engine, host); // Configure the linker let mut linker = Linker::new(&engine); wasmtime_wasi::add_to_linker_sync(&mut linker)?; // The component expects one import `name` that // takes no params and returns a string linker .root() //.func_wrap("name", |_store, _params: ()| Ok((String::from("Alice"),)))? ; println!("have linker"); // Instantiate the component let instance = linker.instantiate(&mut store, &component)?; // TODO: find out whethere there is a way to programmatically "explore" an instance, // to see it's "API" so to speak. // Could be useful for generating some docs or whatnot /* let mut exports = instance.exports(&mut store); let root = exports.instance("greetings").expect("greetings to exist"); let modules = root.modules(); for (name, module) in modules { println!("module: {module:?}") } println!("no modules?!"); */ println!("have instance"); /* // TODO: I definitely misunderstood modules, because "greetings" does not exist let module = instance .get_module(&mut store, "greetings") .expect("module not found"); */ // Call the `greet` function let func = instance .get_func(&mut store, "hello") .expect("hello export not found"); let mut result = [wasmtime::component::Val::String("".into())]; func.call( &mut store, &[Val::String("freund nachtigall".to_string())], &mut result, )?; println!("Greeting: {:?}", result); Ok(()) }
When I run this I get a
panic
forhello export not found
.What am I doing wrong? I also tried with:
greetings.hello
andgreetings:hello
forget_func
but to no avail.On the other hand, if I create a library component via
cargo component new test-plugin-lib --lib
which creates a default wit file and impl for it:package component:test-plugin-lib; /// An example world for the component to target. world example { export hello-world: func() -> string; }
and then load this and call it like so:
let func = instance .get_func(&mut store, "hello-world") .expect("hello export not found"); let mut result = [wasmtime::component::Val::String("".into())]; func.call( &mut store, &[], //&[Val::String("freund nachtigall".to_string())], &mut result, )?; println!("Greeting: {:?}", result);
it works like a charm.
So I guess my problem boils down to one or two things:
1) How do I get a function in an
interface
programmatically likehello
if the wit file looks like:package justatest:testplugin@0.1.0; interface greetings { hello: func(s: string) -> string; } world test-plugin { export greetings; }
2) Maybe it's different when it's a "
binary
" webassembly component in contrast to a lib?
Froidoh edited issue #8857:
Okay, so I am a total noob with
wasm(time)
and my mistake is totally a layer 8 problem. But after digging I couldn't find the solution, so here I am, opening an issue (sorry for that!)What I want:
I want to leverage
cargo component
andwasmtime
to create a simple plugin system experiment.So I created a new (binary) webassembly component via:
cargo component new test-plugin
.[package] name = "test-plugin" version = "0.1.0" edition = "2021" [dependencies] wit-bindgen-rt = { version = "0.26.0", features = ["bitflags"] } [profile.release] codegen-units = 1 opt-level = "s" debug = false strip = true lto = true # [package.metadata.component] # package = "component:test-plugin" [package.metadata.component.target] path = "wit" world = "test-plugin" [package.metadata.component.dependencies]
package justatest:testplugin@0.1.0; interface greetings { hello: func(s: string) -> string; } world test-plugin { export greetings; }
And my
src/main.rs
#[allow(warnings)] mod bindings; use bindings::exports::justatest::testplugin::greetings::Guest; struct Component; impl Guest for Component { fn hello(s: String) -> String { format!("Hello {s}") } } bindings::export!(Component with_types_in bindings); fn main() { println!("Hello from test-plugin!"); }
I then generated the binary via
cargo component build --release
and let it run via:wasmtime target/wasm32-wasi/release/test-plugin.wasm
Hello from test-plugin!As expected. Via the
wasmtime
cli I cannot--invoke
any functions on awasm32-wasi
webassembly module (at least that's what the cli tells me)...So then I created a
simple-host
as a "normal", "native" rust project, compiled viallvm
: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" #wit-component = "0.211.1"
use wasmtime::{ component::{Component, Linker, Val}, Config, Engine, }; use wasmtime_wasi::{WasiCtxBuilder, WasiView}; /* fn convert_to_component(path: impl AsRef<Path>) -> wasmtime::Result<Vec<u8>> { let bytes = &std::fs::read(&path).context("failed to read input file")?; wit_component::ComponentEncoder::default() .module(&bytes)? .encode() } */ struct Host { ctx: wasmtime_wasi::WasiCtx, table: wasmtime_wasi::ResourceTable, } impl WasiView for Host { fn table(&mut self) -> &mut wasmtime_wasi::ResourceTable { &mut self.table } fn ctx(&mut self) -> &mut wasmtime_wasi::WasiCtx { &mut self.ctx } } fn main() -> wasmtime::Result<()> { let mut config = Config::new(); config.wasm_component_model(true); //config.async_support(true); let engine = Engine::new(&config)?; // Load the component from disk let component = std::fs::read("../test-plugin/target/wasm32-wasi/release/test-plugin.wasm")?; let component = Component::from_binary(&engine, &component)?; //println!("bytes: {bytes:?}"); // As `cargo component` by default builds as `wasm32-wasi` webassembly module, we need to also include `wasmtime-wasi` and provide certain native functionality (in my own words) let mut wasi = WasiCtxBuilder::new(); wasi.arg("--help"); let host = Host { ctx: wasi.build(), table: wasmtime_wasi::ResourceTable::new(), }; let mut store = wasmtime::Store::new(&engine, host); // Configure the linker let mut linker = Linker::new(&engine); wasmtime_wasi::add_to_linker_sync(&mut linker)?; // The component expects one import `name` that // takes no params and returns a string linker .root() //.func_wrap("name", |_store, _params: ()| Ok((String::from("Alice"),)))? ; println!("have linker"); // Instantiate the component let instance = linker.instantiate(&mut store, &component)?; // TODO: find out whethere there is a way to programmatically "explore" an instance, // to see it's "API" so to speak. // Could be useful for generating some docs or whatnot /* let mut exports = instance.exports(&mut store); let root = exports.instance("greetings").expect("greetings to exist"); let modules = root.modules(); for (name, module) in modules { println!("module: {module:?}") } println!("no modules?!"); */ println!("have instance"); /* // TODO: I definitely misunderstood modules, because "greetings" does not exist let module = instance .get_module(&mut store, "greetings") .expect("module not found"); */ // Call the `greet` function let func = instance .get_func(&mut store, "hello") .expect("hello export not found"); let mut result = [wasmtime::component::Val::String("".into())]; func.call( &mut store, &[Val::String("freund nachtigall".to_string())], &mut result, )?; println!("Greeting: {:?}", result); Ok(()) }
When I run this I get a
panic
forhello export not found
.What am I doing wrong? I also tried with:
greetings.hello
andgreetings:hello
forget_func
but to no avail.On the other hand, if I create a library component via
cargo component new test-plugin-lib --lib
which creates a default wit file and impl for it:package component:test-plugin-lib; /// An example world for the component to target. world example { export hello-world: func() -> string; }
and then load this and call it like so:
let func = instance .get_func(&mut store, "hello-world") .expect("hello export not found"); let mut result = [wasmtime::component::Val::String("".into())]; func.call( &mut store, &[], //&[Val::String("freund nachtigall".to_string())], &mut result, )?; println!("Greeting: {:?}", result);
it works like a charm.
So I guess my problem boils down to one or two things:
1) How do I get a function in an
interface
programmatically likehello
if the wit file looks like:package justatest:testplugin@0.1.0; interface greetings { hello: func(s: string) -> string; } world test-plugin { export greetings; }
~2) Maybe it's different when it's a "
binary
" webassembly component in contrast to a lib?~I can answer that one: It's not!
If I add a
helloto
function like to:package justatest:testplugin@0.1.0; interface greetings { hello: func(s: string) -> string; } world test-plugin { export greetings; export helloto: func(name: string) -> string; }
and implement it like so:
#[allow(warnings)] mod bindings; use bindings::exports::justatest::testplugin::greetings::Guest; use bindings::Guest as WorldGuest; struct Component; impl Guest for Component { fn hello(s: String) -> String { format!("Hello {s}") } } impl WorldGuest for Component { fn helloto(name: String) -> String { format!("Bonjour {name}") } } bindings::export!(Component with_types_in bindings); fn main() { println!("Hello from test-plugin!"); }
then I can invoke
helloto
just fine :)So it all boils down to: > how can I invoke a function on an interface?!
Froidoh edited issue #8857:
EDIT:
So it all boils down to:
how can I invoke a function on an interface?!
ORIGINAL:
Okay, so I am a total noob with
wasm(time)
and my mistake is totally a layer 8 problem. But after digging I couldn't find the solution, so here I am, opening an issue (sorry for that!)What I want:
I want to leverage
cargo component
andwasmtime
to create a simple plugin system experiment.So I created a new (binary) webassembly component via:
cargo component new test-plugin
.[package] name = "test-plugin" version = "0.1.0" edition = "2021" [dependencies] wit-bindgen-rt = { version = "0.26.0", features = ["bitflags"] } [profile.release] codegen-units = 1 opt-level = "s" debug = false strip = true lto = true # [package.metadata.component] # package = "component:test-plugin" [package.metadata.component.target] path = "wit" world = "test-plugin" [package.metadata.component.dependencies]
package justatest:testplugin@0.1.0; interface greetings { hello: func(s: string) -> string; } world test-plugin { export greetings; }
And my
src/main.rs
#[allow(warnings)] mod bindings; use bindings::exports::justatest::testplugin::greetings::Guest; struct Component; impl Guest for Component { fn hello(s: String) -> String { format!("Hello {s}") } } bindings::export!(Component with_types_in bindings); fn main() { println!("Hello from test-plugin!"); }
I then generated the binary via
cargo component build --release
and let it run via:wasmtime target/wasm32-wasi/release/test-plugin.wasm
Hello from test-plugin!As expected. Via the
wasmtime
cli I cannot--invoke
any functions on awasm32-wasi
webassembly module (at least that's what the cli tells me)...So then I created a
simple-host
as a "normal", "native" rust project, compiled viallvm
: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" #wit-component = "0.211.1"
use wasmtime::{ component::{Component, Linker, Val}, Config, Engine, }; use wasmtime_wasi::{WasiCtxBuilder, WasiView}; /* fn convert_to_component(path: impl AsRef<Path>) -> wasmtime::Result<Vec<u8>> { let bytes = &std::fs::read(&path).context("failed to read input file")?; wit_component::ComponentEncoder::default() .module(&bytes)? .encode() } */ struct Host { ctx: wasmtime_wasi::WasiCtx, table: wasmtime_wasi::ResourceTable, } impl WasiView for Host { fn table(&mut self) -> &mut wasmtime_wasi::ResourceTable { &mut self.table } fn ctx(&mut self) -> &mut wasmtime_wasi::WasiCtx { &mut self.ctx } } fn main() -> wasmtime::Result<()> { let mut config = Config::new(); config.wasm_component_model(true); //config.async_support(true); let engine = Engine::new(&config)?; // Load the component from disk let component = std::fs::read("../test-plugin/target/wasm32-wasi/release/test-plugin.wasm")?; let component = Component::from_binary(&engine, &component)?; //println!("bytes: {bytes:?}"); // As `cargo component` by default builds as `wasm32-wasi` webassembly module, we need to also include `wasmtime-wasi` and provide certain native functionality (in my own words) let mut wasi = WasiCtxBuilder::new(); wasi.arg("--help"); let host = Host { ctx: wasi.build(), table: wasmtime_wasi::ResourceTable::new(), }; let mut store = wasmtime::Store::new(&engine, host); // Configure the linker let mut linker = Linker::new(&engine); wasmtime_wasi::add_to_linker_sync(&mut linker)?; // The component expects one import `name` that // takes no params and returns a string linker .root() //.func_wrap("name", |_store, _params: ()| Ok((String::from("Alice"),)))? ; println!("have linker"); // Instantiate the component let instance = linker.instantiate(&mut store, &component)?; // TODO: find out whethere there is a way to programmatically "explore" an instance, // to see it's "API" so to speak. // Could be useful for generating some docs or whatnot /* let mut exports = instance.exports(&mut store); let root = exports.instance("greetings").expect("greetings to exist"); let modules = root.modules(); for (name, module) in modules { println!("module: {module:?}") } println!("no modules?!"); */ println!("have instance"); /* // TODO: I definitely misunderstood modules, because "greetings" does not exist let module = instance .get_module(&mut store, "greetings") .expect("module not found"); */ // Call the `greet` function let func = instance .get_func(&mut store, "hello") .expect("hello export not found"); let mut result = [wasmtime::component::Val::String("".into())]; func.call( &mut store, &[Val::String("freund nachtigall".to_string())], &mut result, )?; println!("Greeting: {:?}", result); Ok(()) }
When I run this I get a
panic
forhello export not found
.What am I doing wrong? I also tried with:
greetings.hello
andgreetings:hello
forget_func
but to no avail.On the other hand, if I create a library component via
cargo component new test-plugin-lib --lib
which creates a default wit file and impl for it:package component:test-plugin-lib; /// An example world for the component to target. world example { export hello-world: func() -> string; }
and then load this and call it like so:
let func = instance .get_func(&mut store, "hello-world") .expect("hello export not found"); let mut result = [wasmtime::component::Val::String("".into())]; func.call( &mut store, &[], //&[Val::String("freund nachtigall".to_string())], &mut result, )?; println!("Greeting: {:?}", result);
it works like a charm.
So I guess my problem boils down to one or two things:
1) How do I get a function in an
interface
programmatically likehello
if the wit file looks like:package justatest:testplugin@0.1.0; interface greetings { hello: func(s: string) -> string; } world test-plugin { export greetings; }
~2) Maybe it's different when it's a "
binary
" webassembly component in contrast to a lib?~I can answer that one: It's not!
If I add a
helloto
function like to:package justatest:testplugin@0.1.0; interface greetings { hello: func(s: string) -> string; } world test-plugin { export greetings; export helloto: func(name: string) -> string; }
and implement it like so:
#[allow(warnings)] mod bindings; use bindings::exports::justatest::testplugin::greetings::Guest; use bindings::Guest as WorldGuest; struct Component; impl Guest for Component { fn hello(s: String) -> String { format!("Hello {s}") } } impl WorldGuest for Component { fn helloto(name: String) -> String { format!("Bonjour {name}") } } bindings::export!(Component with_types_in bindings); fn main() { println!("Hello from test-plugin!"); }
then I can invoke
helloto
just fine :)So it all boils down to:
how can I invoke a function on an interface?!
Last updated: Dec 23 2024 at 12:05 UTC