use proc_macro::{TokenStream};
use proc_macro2::TokenStream as TokenStream2;
use quote::{quote,ToTokens};
use syn::{parse_macro_input, ItemFn};
#[proc_macro_attribute]
pub fn box_self(attr_postfix: TokenStream, item: TokenStream) -> TokenStream {
let orig_func = parse_macro_input!(item as ItemFn);
let func_vis = &orig_func.vis;
let func_sig = orig_func.sig.clone();
let func_generics = &func_sig.generics;
let func_output = &func_sig.output;
let orgi_fn_name=&func_sig.ident;
let s_fn_name=orgi_fn_name.to_string()+attr_postfix.to_string().as_str();
let func_params = func_sig.inputs.clone();
let new_func_name: proc_macro2::Ident = proc_macro2::Ident::new(s_fn_name.as_str(),proc_macro2::Span::call_site()); let delcaring_params=replace_params_declaration(func_params.to_token_stream().into());
let calling_params=extract_params_without_type(func_params.into_token_stream());
let new_func:TokenStream = quote!{
#[inline] #func_vis fn #new_func_name #func_generics(#delcaring_params) #func_output {
(*self).#orgi_fn_name(#calling_params)
}
}.into();
TokenStream::from_iter( [orig_func.to_token_stream().into() ,new_func])
}
fn replace_params_declaration(input: TokenStream2) -> TokenStream2 {
use proc_macro2::TokenTree;
let mut old_params=input.into_iter();
let mut new_params=Vec::new();
while let Some(tt) =old_params.next(){
match tt {
proc_macro2::TokenTree::Ident(i) if i.to_string()=="self" =>{
new_params.push(TokenTree::Ident(i));
let extra_boxed:TokenStream2=quote!{:Box<Self>}.into(); for extra_boxed_param in extra_boxed.into_iter(){
new_params.push(extra_boxed_param);
}
},
other =>{
new_params.push(other)
}
}
}
new_params.into_iter().collect()
}
fn extract_params_without_type(params:TokenStream2) -> TokenStream2 {
let mut params_without_type=Vec::new();
let mut it=params.into_iter();
let mut last_ident:Option::<proc_macro2::Ident>=None;
let mut ident_not_found=true;
while let Some(tt) =it.next(){
match tt {
proc_macro2::TokenTree::Ident(i) =>{
last_ident=Some(i.clone());
},
proc_macro2::TokenTree::Punct(p) if p.as_char()==':' && ident_not_found =>{
if let Some(i)=&last_ident{
if i.to_string()!="self"{ ident_not_found=false;
params_without_type.push(proc_macro2::TokenTree::Ident(last_ident.take().unwrap()));
let p=proc_macro2::Punct::new(',',proc_macro2::Spacing::Alone);
params_without_type.push(proc_macro2::TokenTree::Punct(p));
}
}
},
proc_macro2::TokenTree::Punct(p) if p.as_char()==',' =>{
ident_not_found=true; }
_=>{}
}
}
params_without_type.into_iter().collect()
}