mod codegen;
mod fast;
mod parse;
mod types;
use proc_macro::TokenStream;
use quote::quote;
use syn::{FnArg, ItemFn, Pat, ReturnType, parse_macro_input};
use codegen::{
generate_arg_extractions, generate_call_and_return, generate_state_extraction,
generate_state_template,
};
use fast::generate_fast_api_code;
use parse::MethodAttrs;
use types::is_result_type;
#[proc_macro_attribute]
pub fn method(attr: TokenStream, item: TokenStream) -> TokenStream {
let attrs = MethodAttrs::parse(attr);
let input_fn = parse_macro_input!(item as ItemFn);
let fn_name = &input_fn.sig.ident;
let _js_name = attrs.js_name.unwrap_or_else(|| fn_name.to_string());
let wrapper_name = syn::Ident::new(&format!("{}_v8", fn_name), fn_name.span());
let mut has_scope = false;
let mut has_state = false;
let params: Vec<_> = input_fn
.sig
.inputs
.iter()
.filter_map(|arg| {
if let FnArg::Typed(pat_type) = arg {
if let Pat::Ident(pat_ident) = &*pat_type.pat {
let name = &pat_ident.ident;
let ty = &pat_type.ty;
let name_str = name.to_string();
if name_str == "scope" || name_str == "_scope" {
has_scope = true;
return None;
}
if name_str == "state" {
has_state = true;
return None;
}
return Some((name.clone(), ty.clone()));
}
}
None
})
.collect();
let arg_extractions = generate_arg_extractions(¶ms);
let state_extraction = generate_state_extraction(has_state, &attrs.state_type);
let call_args: Vec<_> = {
let mut args = Vec::new();
if has_scope {
args.push(quote! { scope });
}
if has_state {
args.push(quote! { &state });
}
for (name, _) in ¶ms {
args.push(quote! { #name });
}
args
};
let has_return = !matches!(input_fn.sig.output, ReturnType::Default);
let returns_result = if let ReturnType::Type(_, ty) = &input_fn.sig.output {
is_result_type(ty)
} else {
false
};
let call_and_return = generate_call_and_return(
fn_name,
&call_args,
has_return,
returns_result,
attrs.promise,
);
let template_fn_name = syn::Ident::new(&format!("{}_v8_template", fn_name), fn_name.span());
let expanded = if attrs.fast {
generate_fast_api_code(
&input_fn,
fn_name,
&wrapper_name,
¶ms,
has_scope,
has_state,
&attrs.state_type,
&state_extraction,
&arg_extractions,
&call_and_return,
)
} else if has_state {
let state_type = attrs
.state_type
.as_ref()
.expect("Function has 'state' parameter but no state type specified");
let template_fn = generate_state_template(&wrapper_name, &template_fn_name, state_type);
quote! {
#input_fn
pub fn #wrapper_name(
scope: &mut v8::PinScope,
args: v8::FunctionCallbackArguments,
mut rv: v8::ReturnValue,
) {
#state_extraction
#(#arg_extractions)*
#call_and_return
}
#template_fn
}
} else {
quote! {
#input_fn
pub fn #wrapper_name(
scope: &mut v8::PinScope,
args: v8::FunctionCallbackArguments,
mut rv: v8::ReturnValue,
) {
#state_extraction
#(#arg_extractions)*
#call_and_return
}
}
};
TokenStream::from(expanded)
}