ink_macro/event/
metadata.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 ink_ir::IsDocAttribute;
16use proc_macro2::TokenStream as TokenStream2;
17use quote::quote_spanned;
18use syn::spanned::Spanned;
19
20/// Derives the `ink::Event` trait for the given `struct`.
21pub fn event_metadata_derive(mut s: synstructure::Structure) -> TokenStream2 {
22    s.bind_with(|_| synstructure::BindStyle::Move)
23        .add_bounds(synstructure::AddBounds::Fields)
24        .underscore_const(true);
25    match &s.ast().data {
26        syn::Data::Struct(_) => {
27            event_metadata_derive_struct(s).unwrap_or_else(|err| err.to_compile_error())
28        }
29        _ => {
30            syn::Error::new(
31                s.ast().span(),
32                "can only derive `EventMetadata` for Rust `struct` items",
33            )
34            .to_compile_error()
35        }
36    }
37}
38
39/// `Event` derive implementation for `struct` types.
40fn event_metadata_derive_struct(s: synstructure::Structure) -> syn::Result<TokenStream2> {
41    assert_eq!(s.variants().len(), 1, "can only operate on structs");
42    let span = s.ast().span();
43
44    let variant = &s.variants()[0];
45    let ident = variant.ast().ident;
46
47    let docs = variant
48        .ast()
49        .attrs
50        .iter()
51        .filter_map(|attr| attr.extract_docs());
52
53    let args = variant.bindings().iter().map( |field| {
54        let field_ty = &field.ast().ty;
55        let field_span = field_ty.span();
56        if let Some(field_name) = field.ast().ident.as_ref() {
57            let indexed = super::has_ink_topic_attribute(field)?;
58            let docs = field
59                .ast()
60                .attrs
61                .iter()
62                .filter_map(|attr| attr.extract_docs());
63            let ty_spec = ink_codegen::generate_type_spec(field_ty);
64            Ok(quote_spanned!(field_span =>
65                ::ink::metadata::EventParamSpec::new(::core::stringify!(#field_name))
66                    .of_type(#ty_spec)
67                    .indexed(#indexed)
68                    .docs([ #( #docs ),* ])
69                    .done()
70            ))
71        } else {
72            Err(syn::Error::new(
73                field_span,
74                "can only derive `EventMetadata` for Rust `struct` items with named fields",
75            ))
76        }
77    }).collect::<syn::Result<Vec<_>>>()?;
78
79    Ok(s.bound_impl(
80        quote_spanned!(span=> ::ink::metadata::EventMetadata),
81        quote_spanned!(span=>
82            const MODULE_PATH: &'static str = ::core::module_path!();
83
84            fn event_spec() -> ::ink::metadata::EventSpec {
85               // register this event metadata function in the distributed slice for combining all
86               // events referenced in the contract binary.
87               #[::ink::metadata::linkme::distributed_slice(::ink::metadata::EVENTS)]
88               #[linkme(crate = ::ink::metadata::linkme)]
89               static EVENT_METADATA: fn() -> ::ink::metadata::EventSpec =
90                   <#ident as ::ink::metadata::EventMetadata>::event_spec;
91
92                ::ink::metadata::EventSpec::new(::core::stringify!(#ident))
93                    .module_path(::core::module_path!())
94                    .signature_topic(
95                        <Self as ::ink::env::Event>::SIGNATURE_TOPIC
96                    )
97                    .args([
98                       #( #args ),*
99                    ])
100                    .docs([
101                       #( #docs ),*
102                    ])
103                    .done()
104            }
105        ),
106    ))
107}