use super::TraitDefinition;
use crate::{
generator,
traits::GenerateCode,
};
use derive_more::From;
use proc_macro2::{
Span,
TokenStream as TokenStream2,
};
use quote::{
quote,
quote_spanned,
};
impl<'a> TraitDefinition<'a> {
pub fn generate_message_builder(&self) -> TokenStream2 {
MessageBuilder::from(*self).generate_code()
}
pub fn message_builder_ident(&self) -> syn::Ident {
self.append_trait_suffix(MessageBuilder::SUFFIX)
}
}
#[derive(From)]
struct MessageBuilder<'a> {
trait_def: TraitDefinition<'a>,
}
impl GenerateCode for MessageBuilder<'_> {
fn generate_code(&self) -> TokenStream2 {
let struct_definition = self.generate_struct_definition();
let auxiliary_trait_impls = self.generate_auxiliary_trait_impls();
let ink_trait_impl = self.generate_ink_trait_impl();
quote! {
#struct_definition
#auxiliary_trait_impls
#ink_trait_impl
}
}
}
impl MessageBuilder<'_> {
const SUFFIX: &'static str = "TraitMessageBuilder";
fn span(&self) -> Span {
self.trait_def.span()
}
fn generate_struct_definition(&self) -> TokenStream2 {
let span = self.span();
let message_builder_ident = self.trait_def.message_builder_ident();
quote_spanned!(span =>
#[doc(hidden)]
#[allow(non_camel_case_types)]
#[::ink::scale_derive(Encode, Decode, TypeInfo)]
pub struct #message_builder_ident<E> {
marker: ::core::marker::PhantomData<fn() -> E>,
}
)
}
fn generate_auxiliary_trait_impls(&self) -> TokenStream2 {
let span = self.span();
let message_builder_ident = self.trait_def.message_builder_ident();
quote_spanned!(span=>
impl<E> ::core::default::Default for #message_builder_ident<E>
where
E: ::ink::env::Environment,
{
fn default() -> Self {
Self { marker: ::core::default::Default::default() }
}
}
)
}
fn generate_ink_trait_impl(&self) -> TokenStream2 {
let span = self.trait_def.span();
let trait_ident = self.trait_def.trait_def.item().ident();
let trait_info_ident = self.trait_def.trait_info_ident();
let message_builder_ident = self.trait_def.message_builder_ident();
let message_impls = self.generate_ink_trait_impl_messages();
quote_spanned!(span=>
impl<E> ::ink::env::ContractEnv for #message_builder_ident<E>
where
E: ::ink::env::Environment,
{
type Env = E;
}
impl<E> #trait_ident for #message_builder_ident<E>
where
E: ::ink::env::Environment,
{
#[allow(non_camel_case_types)]
type __ink_TraitInfo = #trait_info_ident<E>;
#message_impls
}
)
}
fn generate_ink_trait_impl_messages(&self) -> TokenStream2 {
let messages = self.trait_def.trait_def.item().iter_items().filter_map(
|(item, selector)| {
item.filter_map_message().map(|message| {
self.generate_ink_trait_impl_for_message(&message, selector)
})
},
);
quote! {
#( #messages )*
}
}
fn generate_ink_trait_impl_for_message(
&self,
message: &ir::InkTraitMessage,
selector: ir::Selector,
) -> TokenStream2 {
let span = message.span();
let message_ident = message.ident();
let attrs = self
.trait_def
.trait_def
.config()
.whitelisted_attributes()
.filter_attr(message.attrs());
let output_ident = generator::output_ident(message_ident);
let output = message.output();
let output_type =
output.map_or_else(|| quote! { () }, |output| quote! { #output });
let selector_bytes = selector.hex_lits();
let input_bindings = generator::input_bindings(message.inputs());
let input_types = generator::input_types(message.inputs());
let arg_list = generator::generate_argument_list(input_types.iter().cloned());
let mut_tok = message.mutates().then(|| quote! { mut });
let cfg_attrs = message.get_cfg_attrs(span);
quote_spanned!(span =>
#( #cfg_attrs )*
type #output_ident = ::ink::env::call::Execution<
#arg_list,
#output_type,
>;
#( #attrs )*
#[inline]
fn #message_ident(
& #mut_tok self
#( , #input_bindings : #input_types )*
) -> Self::#output_ident {
::ink::env::call::Execution::new(
::ink::env::call::ExecutionInput::new(
::ink::env::call::Selector::new([ #( #selector_bytes ),* ])
)
#(
.push_arg(#input_bindings)
)*
)
}
)
}
}