diff --git a/src/codegen/dialog.rs b/src/codegen/dialog.rs index cc2556d..c3c7ea5 100644 --- a/src/codegen/dialog.rs +++ b/src/codegen/dialog.rs @@ -1,7 +1,7 @@ use super::State; use crate::util::{ - FullTypePath, ResourceFuncKind, extract_arg_info, get_owned_type, get_return_type, make_path, - wit_func_name, + FullTypePath, ResourceFuncKind, constructor_resource_name, extract_arg_info, get_owned_type, + get_return_type, make_path, wit_func_name, }; use quote::quote; use syn::{Signature, parse_quote, visit_mut::VisitMut}; @@ -26,6 +26,16 @@ impl State { } else { quote! { Vec::new() } }; + let (ret_name, read_ret) = match constructor_resource_name(resource, &kind) { + Some(name) => ( + quote! { #name }, + quote! { MockedResource { name: #name.to_string(), ..Dialog::read_value(0) } }, + ), + None => ( + quote! { <#ty as WitName>::name() }, + quote! { Dialog::read_value(0) }, + ), + }; parse_quote! { #sig { let mut __params: Vec = #init_vec; @@ -33,8 +43,8 @@ impl State { __params.push(wasm_wave::to_string(&ToValue::to_value(&#arg_names)).unwrap()); )* proxy::util::dialog::print(0, &format!("import: {}({})", #display_name, __params.join(", "))); - proxy::util::dialog::print(0, &format!("return type: {}", <#ty as WitName>::name())); - let ret = Dialog::read_value(0); + proxy::util::dialog::print(0, &format!("return type: {}", #ret_name)); + let ret = #read_ret; proxy::util::dialog::print(0, &format!("ret: {}", wasm_wave::to_string(&ToValue::to_value(&ret)).unwrap())); ret } diff --git a/src/codegen/fuzz.rs b/src/codegen/fuzz.rs index f0a9c60..28a2d5e 100644 --- a/src/codegen/fuzz.rs +++ b/src/codegen/fuzz.rs @@ -1,7 +1,7 @@ use super::State; use crate::util::{ - FullTypePath, ResourceFuncKind, extract_arg_info, get_owned_type, get_return_type, make_path, - wit_func_name, + FullTypePath, ResourceFuncKind, constructor_resource_name, extract_arg_info, get_owned_type, + get_return_type, make_path, wit_func_name, }; use quote::quote; use syn::{Signature, parse_quote, visit_mut::VisitMut}; @@ -26,6 +26,12 @@ impl State { } else { quote! { Vec::new() } }; + let gen_ret = match constructor_resource_name(resource, &kind) { + Some(name) => { + quote! { MockedResource { name: #name.to_string(), ..u.arbitrary().unwrap() } } + } + None => quote! { u.arbitrary().unwrap() }, + }; parse_quote! { #sig { let mut __params: Vec = #init_vec; @@ -36,7 +42,7 @@ impl State { proxy::util::debug::print(&format!("import: {}({})", #display_name, __buf)); __buf += #display_name; let mut u = Unstructured::new(&__buf.as_bytes()); - let res = u.arbitrary().unwrap(); + let res = #gen_ret; let res_str = wasm_wave::to_string(&ToValue::to_value(&res)).unwrap(); proxy::util::debug::print(&format!("ret: {}", res_str)); res diff --git a/src/codegen/mod.rs b/src/codegen/mod.rs index 80ef4ad..0ead336 100644 --- a/src/codegen/mod.rs +++ b/src/codegen/mod.rs @@ -2,7 +2,7 @@ use crate::util::{ FullTypePath, get_resource_from_trait_name, get_return_type, make_path, toggle_wrapped_module, }; use anyhow::Result; -use heck::ToSnakeCase; +use heck::ToKebabCase; use quote::quote; use std::collections::{BTreeMap, BTreeSet}; use std::path::PathBuf; @@ -221,7 +221,7 @@ impl State { } /// The conversion interface refers to resources through type aliases, e.g. /// `WasiIoStreamsOutputStream = ...::wasi::io::streams::OutputStream`. - /// Returns the snake case name of the aliased resource, e.g. `output_stream`. + /// Returns the WIT name of the aliased resource, e.g. `output-stream`. fn resource_name(&self, alias: &Type) -> String { let Type::Path(alias) = alias else { unreachable!() @@ -242,7 +242,7 @@ impl State { .unwrap() .ident .to_string() - .to_snake_case() + .to_kebab_case() } fn into_output_file(self) -> File { File { diff --git a/src/codegen/replay.rs b/src/codegen/replay.rs index 552b297..9212c9b 100644 --- a/src/codegen/replay.rs +++ b/src/codegen/replay.rs @@ -1,7 +1,7 @@ use super::State; use crate::util::{ - FullTypePath, ResourceFuncKind, extract_arg_info, get_owned_type, get_return_type, make_path, - wit_func_name, + FullTypePath, ResourceFuncKind, constructor_resource_name, extract_arg_info, get_owned_type, + get_return_type, make_path, wit_func_name, }; use quote::quote; use syn::{Signature, parse_quote, visit_mut::VisitMut}; @@ -21,10 +21,16 @@ impl State { let display_name = wit_func_name(module_path, resource, func_name, &kind); let ret_ty = get_return_type(&sig.output); let replay_import = if let Some(ret_ty) = ret_ty { + let to_rust = match constructor_resource_name(resource, &kind) { + Some(name) => { + quote! { MockedResource { name: #name.to_string(), ..ret.to_rust() } } + } + None => quote! { ret.to_rust() }, + }; quote! { let wave = proxy::recorder::replay::replay_import(Some(#display_name), Some(&args)).unwrap(); let ret: Value = wasm_wave::from_str(&<#ret_ty as ValueTyped>::value_type(), &wave).unwrap(); - ret.to_rust() + #to_rust } } else { quote! { diff --git a/src/util.rs b/src/util.rs index b4bdd9d..2219ee4 100644 --- a/src/util.rs +++ b/src/util.rs @@ -64,6 +64,18 @@ pub fn wit_func_name( res.push_str(&func_name.to_string().to_kebab_case()); res } +/// In virtualized components, constructors return `Self`, i.e. `MockedResource`, so type-directed +/// traits like `Dialog` or `Arbitrary` cannot recover the resource name. Returns the WIT name of +/// the resource to patch into the `MockedResource`, if `kind` is a constructor. +pub fn constructor_resource_name( + resource: &Option, + kind: &Option, +) -> Option { + match kind { + Some(ResourceFuncKind::Constructor) => Some(resource.as_ref()?.to_kebab_case()), + _ => None, + } +} pub fn get_return_type(ret: &syn::ReturnType) -> Option { match ret { syn::ReturnType::Default => None,