use super::arg_def::*;
use super::arg_extract::*;
use super::arg_regular::*;
use super::contract_gen_finish::*;
use super::contract_gen_payable::*;
use super::parse_attr::*;
use super::util::*;
use super::reserved;
#[derive(Clone, Debug)]
pub enum MethodMetadata {
Public(PublicMethodMetadata),
Private(),
Event(Vec<u8>),
Callback(),
CallbackRaw(),
}
#[derive(Clone, Debug)]
pub struct PublicMethodMetadata {
pub payable: bool,
}
#[derive(Clone, Debug)]
pub struct Method {
pub metadata: MethodMetadata,
pub name: syn::Ident,
pub method_args: Vec<MethodArg>,
pub return_type: syn::ReturnType,
pub body: Option<syn::Block>,
}
fn extract_metadata(m: &syn::TraitItemMethod) -> MethodMetadata {
let payable = is_payable(m);
let private = is_private(m);
let callback = is_callback_decl(m);
let callback_raw = is_callback_raw_decl(m);
let event_opt = EventAttribute::parse(m);
if let Some(event_attr) = event_opt {
if payable {
panic!("Events cannot be payable.");
}
if private {
panic!("Events cannot be marked private, they are private by definition.");
}
if callback {
panic!("Events cannot be callbacks.");
}
if let Some(_) = m.default {
panic!("Events cannot have provided implementations in the trait.");
}
MethodMetadata::Event(event_attr.identifier)
} else if callback || callback_raw {
if payable {
panic!("Callback methods cannot be marked payable.");
}
if private {
panic!("Callbacks cannot be marked private, they are private by definition.");
}
if m.default == None {
panic!("Callback methods need an implementation.");
}
if callback && callback_raw {
panic!("It is either the default callback, or regular callback, not both.");
}
if callback_raw {
MethodMetadata::CallbackRaw()
} else {
MethodMetadata::Callback()
}
} else if private {
if payable {
panic!("Private methods cannot be marked payable.");
}
if m.default == None {
panic!("Private methods need an implementation.");
}
MethodMetadata::Private()
} else {
if m.default == None {
panic!("Public methods need an implementation.");
}
let fn_name_str = &m.sig.ident.to_string();
if reserved::is_reserved(fn_name_str) {
panic!("Cannot declare public method with name '{}', because that name is reserved by the Arwen API.", fn_name_str);
}
MethodMetadata::Public(PublicMethodMetadata{
payable: payable,
})
}
}
impl Method {
pub fn parse(m: &syn::TraitItemMethod) -> Method {
let metadata = extract_metadata(m);
let allow_callback_args = if let MethodMetadata::Callback() = metadata { true } else { false };
let method_args = extract_method_args(m, is_payable(m), allow_callback_args);
Method {
metadata: metadata,
name: m.sig.ident.clone(),
method_args: method_args,
return_type: m.sig.output.clone(),
body: m.default.clone(),
}
}
}
pub fn arg_declarations(method_args: &Vec<MethodArg>) -> Vec<proc_macro2::TokenStream> {
method_args
.iter()
.map(|arg| {
let pat = &arg.pat;
let ty = &arg.ty;
quote!{#pat : #ty }
})
.collect()
}
impl Method {
pub fn generate_sig(&self) -> proc_macro2::TokenStream {
let method_name = &self.name;
let arg_decl = arg_declarations(&self.method_args);
let ret_tok = match &self.return_type {
syn::ReturnType::Default => quote!{},
syn::ReturnType::Type(_, ty) => quote!{ -> #ty },
};
let result = quote!{ fn #method_name ( &self , #(#arg_decl),* ) #ret_tok };
result
}
pub fn generate_call_to_method(&self) -> proc_macro2::TokenStream {
let fn_ident = &self.name;
let arg_values: Vec<proc_macro2::TokenStream> = self.method_args
.iter()
.map(|arg| generate_arg_call_name(arg))
.collect();
quote! {
self.#fn_ident (#(#arg_values),*)
}
}
pub fn generate_call_method(&self) -> proc_macro2::TokenStream {
let has_variable_nr_args =
self.method_args.iter()
.any(|arg| {
match &arg.metadata {
ArgMetadata::Multi(_) => true,
_ => false,
}
});
if has_variable_nr_args {
self.generate_call_method_variable_nr_args()
} else {
self.generate_call_method_fixed_args()
}
}
pub fn generate_call_method_fixed_args(&self) -> proc_macro2::TokenStream {
let payable_snippet = generate_payable_snippet(self);
let mut arg_index = -1i32;
let arg_init_snippets: Vec<proc_macro2::TokenStream> =
self.method_args
.iter()
.map(|arg| {
if arg.is_callback_arg {
panic!("callback args not allowed in public functions");
}
match &arg.metadata {
ArgMetadata::Single => {
arg_index += 1;
let pat = &arg.pat;
let arg_get = arg_regular(arg, "e!{ #arg_index });
quote! {
let #pat = #arg_get;
}
},
ArgMetadata::Payment =>
generate_payment_snippet(arg), ArgMetadata::Multi(_) =>
panic!("multi args not accepted in function generate_call_method_fixed_args"),
}
})
.collect();
let call_method_ident = generate_call_method_name(&self.name);
let call = self.generate_call_to_method();
let body_with_result = generate_body_with_result(&self.return_type, &call);
let nr_args = arg_index + 1;
quote! {
#[inline]
fn #call_method_ident (&self) {
#payable_snippet
if !self.api.check_num_arguments(#nr_args) {
return;
}
#(#arg_init_snippets)*
#body_with_result
}
}
}
fn generate_call_method_variable_nr_args(&self) -> proc_macro2::TokenStream {
let payable_snippet = generate_payable_snippet(self);
let arg_expr = quote!{
{
if ___current_arg >= ___nr_args {
self.api.signal_error(err_msg::ARG_WRONG_NUMBER);
}
___current_arg += 1;
___current_arg - 1
}
};
let arg_init_snippets: Vec<proc_macro2::TokenStream> =
self.method_args
.iter()
.map(|arg| {
if arg.is_callback_arg {
panic!("callback args not allowed in public functions");
}
match &arg.metadata {
ArgMetadata::Single => {
let pat = &arg.pat;
let arg_get = arg_regular(arg, &arg_expr);
quote! {
let #pat = #arg_get;
}
},
ArgMetadata::Payment => generate_payment_snippet(arg), ArgMetadata::Multi(multi_attr) => { let pat = &arg.pat;
let count_expr = &multi_attr.count_expr;
let push_snippet = arg_regular_multi(&arg, &arg_expr);
quote! {
let mut #pat = Vec::with_capacity #count_expr ;
for _ in 0..#pat.capacity() {
#push_snippet
}
}
}
}
})
.collect();
let call_method_ident = generate_call_method_name(&self.name);
let call = self.generate_call_to_method();
let body_with_result = generate_body_with_result(&self.return_type, &call);
quote! {
#[inline]
fn #call_method_ident (&self) {
#payable_snippet
let ___nr_args = self.api.get_num_arguments();
let mut ___current_arg = 0i32;
#(#arg_init_snippets)*
match ___nr_args - ___current_arg {
0 => {},
1 => {
let callback_name_arg = self.api.get_argument_vec(___nr_args - 1);
self.api.finish_slice_u8(&callback_name_arg.as_slice()); },
_ => {
self.api.signal_error(err_msg::ARG_WRONG_NUMBER);
}
}
#body_with_result
}
}
}
}