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;
20#[cfg(all(feature = "std", any(ink_abi = "sol", ink_abi = "all")))]
21use syn::Fields;
22use syn::spanned::Spanned;
23
24use crate::GenerateCode;
25#[cfg(all(feature = "std", any(ink_abi = "sol", ink_abi = "all")))]
26use crate::generator::sol;
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()
46            .as_ref()
47            .map(ToString::to_string)
48            .map(|hex_s| quote::quote! { #[ink(signature_topic = #hex_s)] });
49        let name_override = self
50            .item
51            .name()
52            .map(|name| quote::quote! { #[ink(name = #name)] });
53        let cfg_attrs = self.item.get_cfg_attrs(item.span());
54
55        // SCALE Codec and ink! metadata derives.
56        #[cfg(not(ink_abi = "sol"))]
57        let (scale_derive, ink_event_metadata_derive) =
58            (quote! { #[::ink::scale_derive(Encode, Decode)] }, {
59                #[cfg(feature = "std")]
60                quote! { #[derive(::ink::EventMetadata)] }
61                #[cfg(not(feature = "std"))]
62                quote! {}
63            });
64        #[cfg(ink_abi = "sol")]
65        let (scale_derive, ink_event_metadata_derive) = (quote! {}, quote! {});
66
67        // Solidity ABI encoding/decoding derives and metadata implementation.
68        #[cfg(not(any(ink_abi = "sol", ink_abi = "all")))]
69        let (sol_codec_derive, sol_event_metadata) = (quote! {}, quote! {});
70        #[cfg(any(ink_abi = "sol", ink_abi = "all"))]
71        let (sol_codec_derive, sol_event_metadata) =
72            (quote! { #[derive(::ink::SolEncode, ::ink::SolDecode)] }, {
73                #[cfg(feature = "std")]
74                {
75                    self.solidity_event_metadata()
76                }
77                #[cfg(not(feature = "std"))]
78                quote! {}
79            });
80
81        quote::quote! (
82            #( #cfg_attrs )*
83            #ink_event_metadata_derive
84            #[derive(::ink::Event)]
85            #scale_derive
86            #sol_codec_derive
87            #anonymous
88            #signature_topic
89            #name_override
90            #item
91
92            #sol_event_metadata
93        )
94    }
95}
96
97impl Event<'_> {
98    /// Generates Solidity ABI compatible metadata for ink! event.
99    #[cfg(all(feature = "std", any(ink_abi = "sol", ink_abi = "all")))]
100    fn solidity_event_metadata(&self) -> TokenStream2 {
101        let item = self.item.item();
102        let ident = &item.ident;
103        let name = self
104            .item
105            .name()
106            .map(ToString::to_string)
107            .unwrap_or_else(|| ident.to_string());
108        let is_anonymous = self.item.anonymous();
109
110        let fields = match &item.fields {
111            Fields::Named(fields) => fields,
112            Fields::Unnamed(_) | Fields::Unit => unreachable!("Expected named fields"),
113        };
114        let params = fields.named.iter().map(|field| {
115            let ty = &field.ty;
116            let sol_ty = sol::utils::sol_type(ty);
117            let ident = field.ident.as_ref().expect("Expected a named field");
118            let name = ident.to_string();
119            let is_topic = field.attrs.iter().any(|attr| {
120                let is_topic_arg = || {
121                    attr.parse_nested_meta(|meta| {
122                        if meta.path.is_ident("topic") {
123                            Ok(())
124                        } else {
125                            Err(meta.error("Not a topic arg"))
126                        }
127                    })
128                    .is_ok()
129                };
130                attr.path().is_ident("ink") && is_topic_arg()
131            });
132            let docs = field
133                .attrs
134                .iter()
135                .filter_map(|attr| attr.extract_docs())
136                .collect::<Vec<_>>()
137                .join("\n");
138
139            quote! {
140                ::ink::metadata::sol::EventParamMetadata {
141                    name: #name.into(),
142                    ty: #sol_ty.into(),
143                    is_topic: #is_topic,
144                    docs: #docs.into(),
145                }
146            }
147        });
148
149        let docs = item
150            .attrs
151            .iter()
152            .filter_map(|attr| attr.extract_docs())
153            .collect::<Vec<_>>()
154            .join("\n");
155
156        quote! {
157            const _: () = {
158                // Register Solidity ABI compatible metadata function for event in distributed slice
159                // for collecting all events referenced in the contract binary.
160                #[::ink::linkme::distributed_slice(::ink::CONTRACT_EVENTS_SOL)]
161                #[linkme(crate = ::ink::linkme)]
162                static EVENT_METADATA_SOL: fn() -> ::ink::metadata::sol::EventMetadata = || {
163                    ::ink::metadata::sol::EventMetadata {
164                        name: #name.into(),
165                        is_anonymous: #is_anonymous,
166                        params: vec![ #( #params ),* ],
167                        docs: #docs.into(),
168                    }
169                };
170            };
171        }
172    }
173}