ink_codegen/generator/
event.rs

1// Copyright (C) Use Ink (UK) Ltd.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use derive_more::From;
16#[cfg(all(feature = "std", any(ink_abi = "sol", ink_abi = "all")))]
17use ir::IsDocAttribute;
18use proc_macro2::TokenStream as TokenStream2;
19use quote::quote;
20use syn::spanned::Spanned;
21#[cfg(all(feature = "std", any(ink_abi = "sol", ink_abi = "all")))]
22use syn::Fields;
23
24#[cfg(all(feature = "std", any(ink_abi = "sol", ink_abi = "all")))]
25use crate::generator::sol;
26use crate::GenerateCode;
27
28/// Generates code for the event item.
29#[derive(From, Copy, Clone)]
30pub struct Event<'a> {
31    /// The storage item to generate code for.
32    item: &'a ir::Event,
33}
34
35impl GenerateCode for Event<'_> {
36    /// Generates ink! event item code.
37    fn generate_code(&self) -> TokenStream2 {
38        let item = self.item.item();
39        let anonymous = self
40            .item
41            .anonymous()
42            .then(|| quote::quote! { #[ink(anonymous)] });
43        let signature_topic = self
44            .item
45            .signature_topic_hex()
46            .map(|hex_s| quote::quote! { #[ink(signature_topic = #hex_s)] });
47        let cfg_attrs = self.item.get_cfg_attrs(item.span());
48
49        #[cfg(all(feature = "std", any(ink_abi = "sol", ink_abi = "all")))]
50        let sol_event_metadata = self.solidity_event_metadata();
51
52        #[cfg(not(all(feature = "std", any(ink_abi = "sol", ink_abi = "all"))))]
53        let sol_event_metadata = quote! {};
54
55        quote::quote! (
56            #( #cfg_attrs )*
57            #[cfg_attr(feature = "std", derive(::ink::EventMetadata))]
58            #[derive(::ink::Event)]
59            #[::ink::scale_derive(Encode, Decode)]
60            #anonymous
61            #signature_topic
62            #item
63
64            #sol_event_metadata
65        )
66    }
67}
68
69impl Event<'_> {
70    /// Generates Solidity ABI compatible metadata for ink! event.
71    #[cfg(all(feature = "std", any(ink_abi = "sol", ink_abi = "all")))]
72    fn solidity_event_metadata(&self) -> TokenStream2 {
73        let item = self.item.item();
74        let ident = &item.ident;
75        let name = ident.to_string();
76        let is_anonymous = self.item.anonymous();
77
78        let fields = match &item.fields {
79            Fields::Named(fields) => fields,
80            Fields::Unnamed(_) | Fields::Unit => unreachable!("Expected named fields"),
81        };
82        let params = fields.named.iter().map(|field| {
83            let ty = &field.ty;
84            let sol_ty = sol::utils::sol_type(ty);
85            let ident = field.ident.as_ref().expect("Expected a named field");
86            let name = ident.to_string();
87            let is_topic = field.attrs.iter().any(|attr| {
88                let is_topic_arg = || {
89                    attr.parse_nested_meta(|meta| {
90                        if meta.path.is_ident("topic") {
91                            Ok(())
92                        } else {
93                            Err(meta.error("Not a topic arg"))
94                        }
95                    })
96                    .is_ok()
97                };
98                attr.path().is_ident("ink") && is_topic_arg()
99            });
100            let docs = field
101                .attrs
102                .iter()
103                .filter_map(|attr| attr.extract_docs())
104                .collect::<Vec<_>>()
105                .join("\n");
106
107            quote! {
108                ::ink::metadata::sol::EventParamMetadata {
109                    name: #name.into(),
110                    ty: #sol_ty.into(),
111                    is_topic: #is_topic,
112                    docs: #docs.into(),
113                }
114            }
115        });
116
117        let docs = item
118            .attrs
119            .iter()
120            .filter_map(|attr| attr.extract_docs())
121            .collect::<Vec<_>>()
122            .join("\n");
123
124        quote! {
125            const _: () = {
126                // Register Solidity ABI compatible metadata function for event in distributed slice
127                // for collecting all events referenced in the contract binary.
128                #[::ink::linkme::distributed_slice(::ink::CONTRACT_EVENTS_SOL)]
129                #[linkme(crate = ::ink::linkme)]
130                static EVENT_METADATA_SOL: fn() -> ::ink::metadata::sol::EventMetadata = || {
131                    ::ink::metadata::sol::EventMetadata {
132                        name: #name.into(),
133                        is_anonymous: #is_anonymous,
134                        params: vec![ #( #params ),* ],
135                        docs: #docs.into(),
136                    }
137                };
138            };
139        }
140    }
141}