diff --git a/crates/component-macro/tests/codegen.rs b/crates/component-macro/tests/codegen.rs index 216d03e52062..90d58bd890f2 100644 --- a/crates/component-macro/tests/codegen.rs +++ b/crates/component-macro/tests/codegen.rs @@ -24,3 +24,85 @@ macro_rules! gentest { } component_macro_test_helpers::foreach!(gentest); + +mod with_key_and_resources { + use anyhow::Result; + use wasmtime::component::Resource; + + wasmtime::component::bindgen!({ + inline: " + package demo:pkg + + interface bar { + resource a + resource b + } + + world foo { + resource a + resource b + + import foo: interface { + resource a + resource b + } + + import bar + } + ", + with: { + "a": MyA, + "b": MyA, + "foo/a": MyA, + "foo/b": MyA, + "demo:pkg/bar/a": MyA, + "demo:pkg/bar/b": MyA, + }, + }); + + pub struct MyA; + + struct MyComponent; + + impl FooImports for MyComponent {} + + impl HostA for MyComponent { + fn drop(&mut self, _: Resource) -> Result<()> { + loop {} + } + } + + impl HostB for MyComponent { + fn drop(&mut self, _: Resource) -> Result<()> { + loop {} + } + } + + impl foo::Host for MyComponent {} + + impl foo::HostA for MyComponent { + fn drop(&mut self, _: Resource) -> Result<()> { + loop {} + } + } + + impl foo::HostB for MyComponent { + fn drop(&mut self, _: Resource) -> Result<()> { + loop {} + } + } + + impl demo::pkg::bar::Host for MyComponent {} + + impl demo::pkg::bar::HostA for MyComponent { + fn drop(&mut self, _: Resource) -> Result<()> { + loop {} + } + } + + impl demo::pkg::bar::HostB for MyComponent { + fn drop(&mut self, _: Resource) -> Result<()> { + loop {} + } + } +} diff --git a/crates/wasmtime/src/component/mod.rs b/crates/wasmtime/src/component/mod.rs index 618f829b2540..73aaad8e1cfc 100644 --- a/crates/wasmtime/src/component/mod.rs +++ b/crates/wasmtime/src/component/mod.rs @@ -326,16 +326,20 @@ pub(crate) use self::store::ComponentStoreData; /// // Restrict the code generated to what's needed for the interface /// // imports in the inlined WIT document fragment. /// interfaces: " -/// import package.foo +/// import wasi:cli/command /// ", /// -/// // Remap interface names to module names, imported from elsewhere. -/// // Using this option will prevent any code from being generated -/// // for the names mentioned in the mapping, assuming instead that the -/// // names mentioned come from a previous use of the `bindgen!` macro -/// // with `only_interfaces: true`. +/// // Remap imported interfaces or resources to types defined in Rust +/// // elsewhere. Using this option will prevent any code from being +/// // generated for interfaces mentioned here. Resources named here will +/// // not have a type generated to represent the resource. +/// // +/// // Interfaces mapped with this option should be previously generated +/// // with an invocation of this macro. Resources need to be mapped to a +/// // Rust type name. /// with: { -/// "a": somewhere::else::a, +/// "wasi:random/random": some::other::wasi::random::random, +/// "wasi:filesystem/types/descriptor": MyDescriptorType, /// }, /// }); /// ``` diff --git a/crates/wit-bindgen/src/lib.rs b/crates/wit-bindgen/src/lib.rs index 2a0d6d68b8aa..45fe6aedf3b1 100644 --- a/crates/wit-bindgen/src/lib.rs +++ b/crates/wit-bindgen/src/lib.rs @@ -912,16 +912,28 @@ impl<'a> InterfaceGenerator<'a> { self.assert_type(id, &name); } - fn type_resource(&mut self, id: TypeId, _name: &str, resource: &TypeDef, docs: &Docs) { - let camel = resource - .name - .as_ref() - .expect("resources are required to be named") - .to_upper_camel_case(); + fn type_resource(&mut self, id: TypeId, name: &str, resource: &TypeDef, docs: &Docs) { + let camel = name.to_upper_camel_case(); if self.types_imported() { self.rustdoc(docs); - uwriteln!(self.src, "pub enum {camel} {{}}"); + + let with_key = match self.current_interface { + Some((_, key, _)) => format!("{}/{name}", self.resolve.name_world_key(key)), + None => name.to_string(), + }; + match self.gen.opts.with.get(&with_key) { + Some(path) => { + uwriteln!( + self.src, + "pub use {}{path} as {camel};", + self.path_to_root() + ); + } + None => { + uwriteln!(self.src, "pub enum {camel} {{}}"); + } + } if self.gen.opts.async_.maybe_async() { uwriteln!(self.src, "#[wasmtime::component::__internal::async_trait]") @@ -1913,6 +1925,24 @@ impl<'a> InterfaceGenerator<'a> { self.push_str("\n"); } } + + fn path_to_root(&self) -> String { + let mut path_to_root = String::new(); + if let Some((_, key, is_export)) = self.current_interface { + match key { + WorldKey::Name(_) => { + path_to_root.push_str("super::"); + } + WorldKey::Interface(_) => { + path_to_root.push_str("super::super::super::"); + } + } + if is_export { + path_to_root.push_str("super::"); + } + } + path_to_root + } } impl<'a> RustGenerator<'a> for InterfaceGenerator<'a> { @@ -1925,23 +1955,12 @@ impl<'a> RustGenerator<'a> for InterfaceGenerator<'a> { } fn path_to_interface(&self, interface: InterfaceId) -> Option { - let mut path_to_root = String::new(); - if let Some((cur, key, is_export)) = self.current_interface { + if let Some((cur, _, _)) = self.current_interface { if cur == interface { return None; } - match key { - WorldKey::Name(_) => { - path_to_root.push_str("super::"); - } - WorldKey::Interface(_) => { - path_to_root.push_str("super::super::super::"); - } - } - if is_export { - path_to_root.push_str("super::"); - } } + let mut path_to_root = self.path_to_root(); let InterfaceName { path, .. } = &self.gen.interface_names[&interface]; path_to_root.push_str(path); Some(path_to_root)