use crate::GenerateCode;
use derive_more::From;
use proc_macro2::{
Span,
TokenStream as TokenStream2,
};
use quote::{
quote,
quote_spanned,
};
use syn::spanned::Spanned as _;
#[derive(From)]
pub struct Events<'a> {
contract: &'a ir::Contract,
}
impl_as_ref_for_generator!(Events);
impl GenerateCode for Events<'_> {
fn generate_code(&self) -> TokenStream2 {
if self.contract.module().events().next().is_none() {
return TokenStream2::new()
}
let emit_event_trait_impl = self.generate_emit_event_trait_impl();
let event_base = self.generate_event_base();
let topic_guards = self.generate_topic_guards();
let topics_impls = self.generate_topics_impls();
let event_structs = self.generate_event_structs();
quote! {
#emit_event_trait_impl
#event_base
#( #topic_guards )*
#( #event_structs )*
#( #topics_impls )*
}
}
}
impl<'a> Events<'a> {
fn generate_emit_event_trait_impl(&self) -> TokenStream2 {
let storage_ident = &self.contract.module().storage().ident();
quote! {
const _: () = {
impl<'a> ::ink::codegen::EmitEvent<#storage_ident> for ::ink::EnvAccess<'a, Environment> {
fn emit_event<E>(self, event: E)
where
E: Into<<#storage_ident as ::ink::reflect::ContractEventBase>::Type>,
{
::ink::env::emit_event::<
Environment,
<#storage_ident as ::ink::reflect::ContractEventBase>::Type
>(event.into());
}
}
};
}
}
fn generate_event_base(&self) -> TokenStream2 {
let storage_ident = &self.contract.module().storage().ident();
let event_idents = self
.contract
.module()
.events()
.map(|event| event.ident())
.collect::<Vec<_>>();
let base_event_ident =
proc_macro2::Ident::new("__ink_EventBase", Span::call_site());
quote! {
#[allow(non_camel_case_types)]
#[derive(::scale::Encode, ::scale::Decode)]
#[cfg(not(feature = "__ink_dylint_EventBase"))]
pub enum #base_event_ident {
#( #event_idents(#event_idents), )*
}
const _: () = {
impl ::ink::reflect::ContractEventBase for #storage_ident {
type Type = #base_event_ident;
}
};
#(
const _: () = {
impl From<#event_idents> for #base_event_ident {
fn from(event: #event_idents) -> Self {
Self::#event_idents(event)
}
}
};
)*
const _: () = {
pub enum __ink_UndefinedAmountOfTopics {}
impl ::ink::env::topics::EventTopicsAmount for __ink_UndefinedAmountOfTopics {
const AMOUNT: usize = 0;
}
impl ::ink::env::Topics for #base_event_ident {
type RemainingTopics = __ink_UndefinedAmountOfTopics;
fn topics<E, B>(
&self,
builder: ::ink::env::topics::TopicsBuilder<::ink::env::topics::state::Uninit, E, B>,
) -> <B as ::ink::env::topics::TopicsBuilderBackend<E>>::Output
where
E: ::ink::env::Environment,
B: ::ink::env::topics::TopicsBuilderBackend<E>,
{
match self {
#(
Self::#event_idents(event) => {
<#event_idents as ::ink::env::Topics>::topics::<E, B>(event, builder)
}
)*
}
}
}
};
}
}
fn generate_topics_guard(&self, event: &ir::Event) -> TokenStream2 {
let span = event.span();
let storage_ident = self.contract.module().storage().ident();
let event_ident = event.ident();
let len_topics = event.fields().filter(|event| event.is_topic).count();
let max_len_topics = quote_spanned!(span=>
<<#storage_ident as ::ink::env::ContractEnv>::Env
as ::ink::env::Environment>::MAX_EVENT_TOPICS
);
quote_spanned!(span=>
impl ::ink::codegen::EventLenTopics for #event_ident {
type LenTopics = ::ink::codegen::EventTopics<#len_topics>;
}
const _: () = ::ink::codegen::utils::consume_type::<
::ink::codegen::EventRespectsTopicLimit<
#event_ident,
{ #max_len_topics },
>
>();
)
}
fn generate_topic_guards(&'a self) -> impl Iterator<Item = TokenStream2> + 'a {
self.contract.module().events().map(move |event| {
let span = event.span();
let topics_guard = self.generate_topics_guard(event);
quote_spanned!(span =>
#topics_guard
)
})
}
fn generate_topics_impls(&'a self) -> impl Iterator<Item = TokenStream2> + 'a {
let contract_ident = self.contract.module().storage().ident();
self.contract.module().events().map(move |event| {
let span = event.span();
let event_ident = event.ident();
let event_signature = syn::LitByteStr::new(
format!("{}::{}", contract_ident, event_ident
).as_bytes(), span);
let len_event_signature = event_signature.value().len();
let len_topics = event.fields().filter(|field| field.is_topic).count();
let topic_impls = event
.fields()
.enumerate()
.filter(|(_, field)| field.is_topic)
.map(|(n, topic_field)| {
let span = topic_field.span();
let field_ident = topic_field
.ident()
.map(quote::ToTokens::into_token_stream)
.unwrap_or_else(|| quote_spanned!(span => #n));
let field_type = topic_field.ty();
let signature = syn::LitByteStr::new(
format!("{}::{}::{}", contract_ident, event_ident,
field_ident
).as_bytes(), span);
quote_spanned!(span =>
.push_topic::<::ink::env::topics::PrefixedValue<#field_type>>(
&::ink::env::topics::PrefixedValue { value: &self.#field_ident, prefix: #signature }
)
)
});
let event_signature_topic = match event.anonymous {
true => None,
false => Some(quote_spanned!(span=>
.push_topic::<::ink::env::topics::PrefixedValue<[u8; #len_event_signature]>>(
&::ink::env::topics::PrefixedValue { value: #event_signature, prefix: b"" }
)
))
};
let anonymous_topics_offset = usize::from(!event.anonymous);
let remaining_topics_ty = match len_topics + anonymous_topics_offset {
0 => quote_spanned!(span=> ::ink::env::topics::state::NoRemainingTopics),
n => quote_spanned!(span=> [::ink::env::topics::state::HasRemainingTopics; #n]),
};
quote_spanned!(span =>
const _: () = {
impl ::ink::env::Topics for #event_ident {
type RemainingTopics = #remaining_topics_ty;
fn topics<E, B>(
&self,
builder: ::ink::env::topics::TopicsBuilder<::ink::env::topics::state::Uninit, E, B>,
) -> <B as ::ink::env::topics::TopicsBuilderBackend<E>>::Output
where
E: ::ink::env::Environment,
B: ::ink::env::topics::TopicsBuilderBackend<E>,
{
builder
.build::<Self>()
#event_signature_topic
#(
#topic_impls
)*
.finish()
}
}
};
)
})
}
fn generate_event_structs(&'a self) -> impl Iterator<Item = TokenStream2> + 'a {
self.contract.module().events().map(move |event| {
let span = event.span();
let ident = event.ident();
let attrs = event.attrs();
let fields = event.fields().map(|event_field| {
let span = event_field.span();
let attrs = event_field.attrs();
let vis = event_field.vis();
let ident = event_field.ident();
let ty = event_field.ty();
quote_spanned!(span=>
#( #attrs )*
#vis #ident : #ty
)
});
quote_spanned!(span =>
#( #attrs )*
#[derive(scale::Encode, scale::Decode)]
pub struct #ident {
#( #fields ),*
}
)
})
}
}