use quote::ToTokens;
use super::util::*;
use crate::{
generate::{convert_to_owned_type::convert_to_owned_type, snippets, supertrait_gen},
model::{ArgPaymentMetadata, ContractTrait, Method, MethodArgument, PublicRole},
};
pub fn proxy_arg_gen(
method_args: &[MethodArgument],
generics: &mut syn::Generics,
) -> Vec<proc_macro2::TokenStream> {
let mut args_decl = Vec::new();
for (arg_index, arg) in method_args.iter().enumerate() {
let unprocessed_attributes = &arg.unprocessed_attributes;
let pat = &arg.pat;
if arg.is_endpoint_arg() {
let mut bounds = syn::punctuated::Punctuated::new();
bounds.push(syn::TypeParamBound::Trait(syn::TraitBound {
paren_token: None,
modifier: syn::TraitBoundModifier::None,
lifetimes: None,
path: equivalent_encode_path_gen(&arg.ty),
}));
let arg_type_generated_ident = generate_proxy_type_generic(arg_index);
generics
.params
.push(syn::GenericParam::Type(syn::TypeParam {
attrs: Vec::new(),
ident: arg_type_generated_ident.clone(),
colon_token: Some(syn::token::Colon(proc_macro2::Span::call_site())),
bounds,
eq_token: None,
default: None,
}));
args_decl.push(quote! { #(#unprocessed_attributes)* #pat : #arg_type_generated_ident });
} else {
let ty = &arg.ty;
args_decl.push(quote! { #(#unprocessed_attributes)* #pat : #ty });
}
}
args_decl
}
pub fn generate_proxy_method_sig(
method: &Method,
return_type: proc_macro2::TokenStream,
) -> proc_macro2::TokenStream {
let method_name = &method.name;
let mut generics = method.generics.clone();
let generics_where = &method.generics.where_clause;
let arg_decl = proxy_arg_gen(&method.method_args, &mut generics);
let result = quote! {
fn #method_name #generics (
&mut self,
#(#arg_decl),*
) -> #return_type
#generics_where
};
result
}
fn original_type_tokens(m: &Method) -> proc_macro2::TokenStream {
match &m.return_type {
syn::ReturnType::Default => quote! { () },
syn::ReturnType::Type(_, ty) => quote! { #ty },
}
}
pub fn generate_proxy_endpoint(m: &Method, endpoint_name: String) -> proc_macro2::TokenStream {
let mut token_count = 0;
let mut token_expr =
quote! { drt_sc::types::RewaOrDctTokenIdentifier::<Self::Api>::rewa() };
let mut nonce_count = 0;
let mut nonce_expr = quote! { 0u64 };
let mut payment_count = 0;
let mut payment_expr = quote! { drt_sc::types::BigUint::<Self::Api>::zero() };
let mut multi_count = 0;
let mut multi_expr_opt = None;
let mut arg_push_snippets = Vec::<proc_macro2::TokenStream>::new();
for arg in &m.method_args {
match &arg.metadata.payment {
ArgPaymentMetadata::NotPayment => {
let pat = &arg.pat;
arg_push_snippets.push(quote! {
.argument(&#pat)
});
},
ArgPaymentMetadata::PaymentToken => {
token_count += 1;
let pat = &arg.pat;
token_expr = quote! { #pat };
},
ArgPaymentMetadata::PaymentNonce => {
nonce_count += 1;
let pat = &arg.pat;
nonce_expr = quote! { #pat };
},
ArgPaymentMetadata::PaymentAmount => {
payment_count += 1;
let pat = &arg.pat;
payment_expr = quote! { #pat };
},
ArgPaymentMetadata::PaymentMulti => {
multi_count += 1;
let pat = &arg.pat;
multi_expr_opt = Some(quote! { #pat });
},
}
}
assert!(
payment_count <= 1,
"No more than one payment argument allowed in call proxy"
);
assert!(
token_count <= 1,
"No more than one payment token argument allowed in call proxy"
);
assert!(
nonce_count <= 1,
"No more than one payment nonce argument allowed in call proxy"
);
assert!(
multi_count <= 1,
"No more than one payment multi argument allowed in call proxy"
);
let payment_type;
let payment_init;
if token_count > 0 || nonce_count > 0 || payment_count > 0 {
assert!(multi_count == 0, "#[payment_multi] cannot coexist with any other payment annotation in the same endpoint");
if token_count == 0 && nonce_count == 0 {
payment_type = quote! { drt_sc::types::RewaPayment<Self::Api> };
payment_init = quote! { .rewa(#payment_expr) };
} else {
payment_type = quote! { drt_sc::types::RewaOrDctTokenPayment<Self::Api> };
payment_init = quote! { .payment(
drt_sc::types::RewaOrDctTokenPayment::new(
#token_expr,
#nonce_expr,
#payment_expr,
)
)};
}
} else if multi_count > 0 {
let multi_expr = multi_expr_opt.unwrap();
payment_type = quote! { MultiDctPayment<Self::Api> };
payment_init = quote! { .multi_dct(#multi_expr.clone_value()) };
} else {
payment_type = quote! { () };
payment_init = quote! {};
}
let original_type = original_type_tokens(m);
let return_type = quote! {
drt_sc::types::Tx<
drt_sc::types::TxScEnv<Self::Api>,
(),
Self::To,
#payment_type,
(),
drt_sc::types::FunctionCall<Self::Api>,
drt_sc::types::OriginalResultMarker<#original_type>,
>
};
let msig = generate_proxy_method_sig(m, return_type);
let sig = quote! {
#[allow(clippy::too_many_arguments)]
#[allow(clippy::type_complexity)]
#msig {
drt_sc::types::TxBaseWithEnv::new_tx_from_sc()
.to(self.extract_proxy_to())
.original_result()
.raw_call(#endpoint_name)
#payment_init
#(#arg_push_snippets)*
}
};
sig
}
pub fn generate_proxy_deploy(init_method: &Method) -> proc_macro2::TokenStream {
let mut payment_count = 0;
let mut multi_count = 0;
let mut token_count = 0;
let mut nonce_count = 0;
let mut payment_type = quote! { () };
let mut payment_init = quote! {};
let mut arg_push_snippets = Vec::<proc_macro2::TokenStream>::new();
for arg in &init_method.method_args {
match &arg.metadata.payment {
ArgPaymentMetadata::NotPayment => {
let pat = &arg.pat;
arg_push_snippets.push(quote! {
.argument(&#pat)
});
},
ArgPaymentMetadata::PaymentToken => {
token_count += 1;
},
ArgPaymentMetadata::PaymentNonce => {
nonce_count += 1;
},
ArgPaymentMetadata::PaymentAmount => {
payment_count += 1;
let payment_expr = &arg.pat;
payment_type = quote! { drt_sc::types::RewaPayment<Self::Api> };
payment_init = quote! { .rewa(#payment_expr) };
},
ArgPaymentMetadata::PaymentMulti => {
multi_count += 1;
},
}
}
assert!(
payment_count <= 1,
"No more than one payment argument allowed in call proxy"
);
assert!(token_count == 0, "No DCT payment allowed in #[init]");
assert!(nonce_count == 0, "No SFT/NFT payment allowed in #[init]");
assert!(
multi_count == 0,
"No multi DCT payments allowed in #[init]"
);
let original_type = original_type_tokens(init_method);
let return_type = quote! {
drt_sc::types::Tx<
drt_sc::types::TxScEnv<Self::Api>,
(),
Self::To, #payment_type,
(),
drt_sc::types::DeployCall<drt_sc::types::TxScEnv<Self::Api>, ()>,
drt_sc::types::OriginalResultMarker<#original_type>,
>
};
let msig = generate_proxy_method_sig(init_method, return_type);
let sig = quote! {
#[allow(clippy::too_many_arguments)]
#[allow(clippy::type_complexity)]
#msig {
drt_sc::types::TxBaseWithEnv::new_tx_from_sc()
.raw_deploy()
#payment_init
#(#arg_push_snippets)*
.original_result()
.to(self.extract_proxy_to()) }
};
sig
}
pub fn generate_method_impl(contract_trait: &ContractTrait) -> Vec<proc_macro2::TokenStream> {
contract_trait
.methods
.iter()
.filter_map(|m| match &m.public_role {
PublicRole::Init(_) => Some(generate_proxy_deploy(m)),
PublicRole::Upgrade(_) => Some(generate_proxy_endpoint(m, "upgrade".to_string())),
PublicRole::Endpoint(endpoint_metadata) => Some(generate_proxy_endpoint(
m,
endpoint_metadata.public_name.to_string(),
)),
_ => None,
})
.collect()
}
pub fn proxy_trait(contract: &ContractTrait) -> proc_macro2::TokenStream {
let proxy_supertrait_decl =
supertrait_gen::proxy_supertrait_decl(contract.supertraits.as_slice());
let proxy_methods_impl = generate_method_impl(contract);
quote! {
pub trait ProxyTrait:
drt_sc::contract_base::ProxyObjBase
+ Sized
#(#proxy_supertrait_decl)*
{
#(#proxy_methods_impl)*
}
}
}
pub fn proxy_obj_code(contract: &ContractTrait) -> proc_macro2::TokenStream {
let proxy_object_def = snippets::proxy_object_def();
let impl_all_proxy_traits =
supertrait_gen::impl_all_proxy_traits(contract.supertraits.as_slice());
quote! {
#proxy_object_def
#(#impl_all_proxy_traits)*
}
}
fn equivalent_encode_path_gen(ty: &syn::Type) -> syn::Path {
let owned_type = convert_to_owned_type(ty);
syn::parse_str(
format!(
"drt_sc::types::ProxyArg<{}>",
owned_type.to_token_stream()
)
.as_str(),
)
.unwrap()
}