Modify procedural macro to allow more flexible function signatures; Refactor code
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
use quote::{format_ident, quote};
|
||||
use syn::{FnArg, ImplItem, ItemImpl, Pat, Type, parse_macro_input};
|
||||
use syn::{FnArg, ImplItem, ItemImpl, ReturnType, Type, parse_macro_input};
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn component(attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
@@ -23,14 +23,12 @@ pub fn component(attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
|
||||
for item in &mut input.items {
|
||||
if let ImplItem::Fn(method) = item {
|
||||
// Check for #[action]
|
||||
let has_action = method
|
||||
.attrs
|
||||
.iter()
|
||||
.any(|attr| attr.path().is_ident("action"));
|
||||
|
||||
if has_action {
|
||||
// Remove the #[action] attribute so it doesn't cause compilation errors
|
||||
method.attrs.retain(|attr| !attr.path().is_ident("action"));
|
||||
|
||||
let method_name = &method.sig.ident;
|
||||
@@ -39,44 +37,61 @@ pub fn component(attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
snake_to_pascal(&method_name.to_string())
|
||||
);
|
||||
|
||||
let returns_result = match &method.sig.output {
|
||||
ReturnType::Default => false,
|
||||
ReturnType::Type(_, ty) => {
|
||||
quote!(#ty).to_string().contains("Result")
|
||||
}
|
||||
};
|
||||
|
||||
let mut variant_fields = Vec::new();
|
||||
let mut call_args = Vec::new();
|
||||
let mut variant_arg_names = Vec::new();
|
||||
|
||||
// Inspect arguments to decide what goes into the Enum and what is injected
|
||||
for arg in method.sig.inputs.iter().skip(1) {
|
||||
// Skip 'self'
|
||||
if let FnArg::Typed(pat_type) = arg {
|
||||
if is_sender_type(&pat_type.ty) {
|
||||
// Inject the 'tx' from handle_action, don't add to Enum
|
||||
call_args.push(quote!(tx));
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
// This is a data argument, add to Enum
|
||||
let ty = &pat_type.ty;
|
||||
variant_fields.push(quote!(#ty));
|
||||
|
||||
let ty = &pat_type.ty;
|
||||
variant_fields.push(quote!(#ty));
|
||||
|
||||
if let Pat::Ident(pi) = &*pat_type.pat {
|
||||
let arg_ident = &pi.ident;
|
||||
call_args.push(quote!(#arg_ident));
|
||||
let arg_id = format_ident!(
|
||||
"arg_{}",
|
||||
variant_arg_names.len()
|
||||
);
|
||||
variant_arg_names.push(arg_id.clone());
|
||||
call_args.push(quote!(#arg_id));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Generate Enum Variant
|
||||
if variant_fields.is_empty() {
|
||||
enum_variants.push(quote!(#variant_name));
|
||||
match_arms.push(quote! {
|
||||
#enum_name::#variant_name => self.#method_name(tx)
|
||||
});
|
||||
} else {
|
||||
enum_variants
|
||||
.push(quote!(#variant_name(#(#variant_fields),*)));
|
||||
// Extracting bindings for the match arm: Enum::Var(a, b) -> self.method(a, b, tx)
|
||||
let pattern_args = (0..variant_fields.len())
|
||||
.map(|i| format_ident!("arg_{}", i));
|
||||
let pattern_args_clone = pattern_args.clone();
|
||||
|
||||
match_arms.push(quote! {
|
||||
#enum_name::#variant_name(#(#pattern_args),*) => self.#method_name(#(#pattern_args_clone,)* tx)
|
||||
});
|
||||
}
|
||||
|
||||
// Generate Match Arm
|
||||
let pattern = if variant_arg_names.is_empty() {
|
||||
quote!(#enum_name::#variant_name)
|
||||
} else {
|
||||
quote!(#enum_name::#variant_name(#(#variant_arg_names),*))
|
||||
};
|
||||
|
||||
let call = if returns_result {
|
||||
quote!(self.#method_name(#(#call_args),*))
|
||||
} else {
|
||||
quote!({ self.#method_name(#(#call_args),*); ::core::result::Result::Ok(()) })
|
||||
};
|
||||
|
||||
match_arms.push(quote!(#pattern => #call));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -105,6 +120,11 @@ pub fn component(attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
TokenStream::from(expanded)
|
||||
}
|
||||
|
||||
fn is_sender_type(ty: &Type) -> bool {
|
||||
let s = quote!(#ty).to_string();
|
||||
s.contains("UnboundedSender")
|
||||
}
|
||||
|
||||
fn snake_to_pascal(s: &str) -> String {
|
||||
s.split('_')
|
||||
.map(|word| {
|
||||
@@ -118,9 +138,3 @@ fn snake_to_pascal(s: &str) -> String {
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
// Helper to detect if an argument is the Sender to inject it from handle_action
|
||||
fn is_sender_type(ty: &Type) -> bool {
|
||||
let s = quote!(#ty).to_string();
|
||||
s.contains("UnboundedSender")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user