| 213 | |
| 214 | #[proc_macro_attribute] |
| 215 | pub fn plugin_methods(_attr: TokenStream, item: TokenStream) -> TokenStream { |
| 216 | let input = parse_macro_input!(item as ItemImpl); |
| 217 | let self_ty = match &*input.self_ty { |
| 218 | Type::Path(p) => p.path.segments.last().expect("plugin_methods: empty path").ident.clone(), |
| 219 | _ => return quote! { compile_error!("#[plugin_methods] requires a simple type path"); }.into(), |
| 220 | }; |
| 221 | let class_name = self_ty.to_string(); |
| 222 | |
| 223 | let mut emitted: Vec<TokenStream2> = Vec::new(); |
| 224 | |
| 225 | for item in &input.items { |
| 226 | let ImplItem::Fn(method) = item else { continue; }; |
| 227 | let is_ctor = method.attrs.iter().any(|a| a.path().is_ident("plugin_ctor")); |
| 228 | let method_name = method.sig.ident.to_string(); |
| 229 | let method_ident = &method.sig.ident; |
| 230 | |
| 231 | if is_ctor { |
| 232 | let export_ident = format_ident!("__class_{}___init__", class_name); |
| 233 | emitted.push(quote! { |
| 234 | #[::wasm_pdk::plugin_fn] |
| 235 | fn #export_ident(self_h: ::wasm_pdk::Handle) -> ::wasm_pdk::Result<()> { |
| 236 | let instance = #self_ty::#method_ident(); |
| 237 | let id = #self_ty::__edge_insert(instance); |
| 238 | let id_handle = <i64 as ::wasm_pdk::IntoValue>::into_handle(id)?; |
| 239 | self_h.set_attr("__rust_id", &id_handle)?; |
| 240 | Ok(()) |
| 241 | } |
| 242 | }); |
| 243 | continue; |
| 244 | } |
| 245 | |
| 246 | let export_ident = format_ident!("__class_{}_{}", class_name, method_name); |
| 247 | let user_args: Vec<_> = method.sig.inputs.iter().skip(1).collect(); |
| 248 | let user_arg_names: Vec<_> = user_args.iter().filter_map(|a| match a { |
| 249 | FnArg::Typed(pt) => match &*pt.pat { Pat::Ident(id) => Some(id.ident.clone()), _ => None }, |
| 250 | _ => None, |
| 251 | }).collect(); |
| 252 | let user_arg_types: Vec<_> = user_args.iter().filter_map(|a| match a { |
| 253 | FnArg::Typed(pt) => Some((*pt.ty).clone()), |
| 254 | _ => None, |
| 255 | }).collect(); |
| 256 | let user_ret_ty: syn::Type = match &method.sig.output { |
| 257 | ReturnType::Default => syn::parse_quote!(()), |
| 258 | ReturnType::Type(_, t) => (**t).clone(), |
| 259 | }; |
| 260 | // Detect if user method already returns Result; avoids generating Result<Result<T>>. |
| 261 | let is_user_result = matches!(&user_ret_ty, Type::Path(p) |
| 262 | if p.path.segments.last().map(|s| s.ident == "Result").unwrap_or(false)); |
| 263 | let (wrapper_ret, call_expr) = if is_user_result { |
| 264 | (quote! { #user_ret_ty }, |
| 265 | quote! { instance.#method_ident(#(#user_arg_names),*) }) |
| 266 | } else { |
| 267 | (quote! { ::wasm_pdk::Result<#user_ret_ty> }, |
| 268 | quote! { Ok(instance.#method_ident(#(#user_arg_names),*)) }) |
| 269 | }; |
| 270 | |
| 271 | emitted.push(quote! { |
| 272 | #[::wasm_pdk::plugin_fn] |