ink_codegen/generator/trait_def/
message_builder.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 super::TraitDefinition;
16use crate::{
17    generator,
18    traits::GenerateCode,
19};
20use derive_more::From;
21use proc_macro2::{
22    Span,
23    TokenStream as TokenStream2,
24};
25use quote::{
26    quote,
27    quote_spanned,
28};
29
30impl TraitDefinition<'_> {
31    /// Generates code for the global trait call builder for an ink! trait.
32    ///
33    /// # Note
34    ///
35    /// - The generated call builder type implements the ink! trait definition and allows
36    ///   to build up contract calls that allow for customization by the user to provide
37    ///   gas limit, endowment etc.
38    /// - The call builder is used directly by the generated call forwarder. There exists
39    ///   one global call forwarder and call builder pair for every ink! trait definition.
40    pub fn generate_message_builder(&self) -> TokenStream2 {
41        MessageBuilder::from(*self).generate_code()
42    }
43
44    /// The identifier of the ink! trait message builder.
45    pub fn message_builder_ident(&self) -> syn::Ident {
46        self.append_trait_suffix(MessageBuilder::SUFFIX)
47    }
48}
49
50/// Generates code for the global ink! trait call builder.
51#[derive(From)]
52struct MessageBuilder<'a> {
53    trait_def: TraitDefinition<'a>,
54}
55
56impl GenerateCode for MessageBuilder<'_> {
57    fn generate_code(&self) -> TokenStream2 {
58        let struct_definition = self.generate_struct_definition();
59        let auxiliary_trait_impls = self.generate_auxiliary_trait_impls();
60        let ink_trait_impl = self.generate_ink_trait_impl();
61        quote! {
62            #struct_definition
63            #auxiliary_trait_impls
64            #ink_trait_impl
65        }
66    }
67}
68
69impl MessageBuilder<'_> {
70    /// The name suffix for ink! trait message builder.
71    const SUFFIX: &'static str = "TraitMessageBuilder";
72
73    /// Returns the span of the ink! trait definition.
74    fn span(&self) -> Span {
75        self.trait_def.span()
76    }
77
78    /// Generates the struct type definition for the account wrapper type.
79    ///
80    /// This type is going to implement the trait so that invoking its trait
81    /// methods will return an `Execution` instance populated with the message selector,
82    /// arguments and return type, which can then be executed via the `exec` method.
83    fn generate_struct_definition(&self) -> TokenStream2 {
84        let span = self.span();
85        let message_builder_ident = self.trait_def.message_builder_ident();
86        quote_spanned!(span =>
87            /// The global call builder type for all trait implementers.
88            ///
89            /// All calls to types (contracts) implementing the trait will be built by this type.
90            #[doc(hidden)]
91            #[allow(non_camel_case_types)]
92            #[::ink::scale_derive(Encode, Decode, TypeInfo)]
93            pub struct #message_builder_ident<E> {
94                marker: ::core::marker::PhantomData<fn() -> E>,
95            }
96        )
97    }
98
99    /// Generates trait implementations for auxiliary traits for the message builder.
100    ///
101    /// # Note
102    ///
103    /// Auxiliary traits currently include:
104    ///
105    /// - `Default`: To allow initializing a contract message builder.
106    fn generate_auxiliary_trait_impls(&self) -> TokenStream2 {
107        let span = self.span();
108        let message_builder_ident = self.trait_def.message_builder_ident();
109        quote_spanned!(span=>
110            impl<E> ::core::default::Default for #message_builder_ident<E>
111            where
112                E: ::ink::env::Environment,
113            {
114                fn default() -> Self {
115                    Self { marker: ::core::default::Default::default() }
116                }
117            }
118        )
119    }
120
121    /// Generates the implementation of the associated ink! trait definition.
122    ///
123    /// # Note
124    ///
125    /// The implemented messages create an instance of the `Execution` type which
126    /// encapsulates the execution input (the selector and arguments) and the output
127    /// type of the message.
128    fn generate_ink_trait_impl(&self) -> TokenStream2 {
129        let span = self.trait_def.span();
130        let trait_ident = self.trait_def.trait_def.item().ident();
131        let trait_info_ident = self.trait_def.trait_info_ident();
132        let message_builder_ident = self.trait_def.message_builder_ident();
133        let message_impls = self.generate_ink_trait_impl_messages();
134        quote_spanned!(span=>
135            impl<E> ::ink::env::ContractEnv for #message_builder_ident<E>
136            where
137                E: ::ink::env::Environment,
138            {
139                type Env = E;
140            }
141
142            impl<E> #trait_ident for #message_builder_ident<E>
143            where
144                E: ::ink::env::Environment,
145            {
146                #[allow(non_camel_case_types)]
147                type __ink_TraitInfo = #trait_info_ident<E>;
148
149                #message_impls
150            }
151        )
152    }
153
154    /// Generate the code for all ink! trait messages implemented by the trait call
155    /// builder.
156    fn generate_ink_trait_impl_messages(&self) -> TokenStream2 {
157        let messages = self.trait_def.trait_def.item().iter_items().filter_map(
158            |(item, selector)| {
159                item.filter_map_message().map(|message| {
160                    self.generate_ink_trait_impl_for_message(&message, selector)
161                })
162            },
163        );
164        quote! {
165            #( #messages )*
166        }
167    }
168
169    /// Generate the code for a single ink! trait message implemented by the trait call
170    /// builder.
171    fn generate_ink_trait_impl_for_message(
172        &self,
173        message: &ir::InkTraitMessage,
174        selector: ir::Selector,
175    ) -> TokenStream2 {
176        let span = message.span();
177        let message_ident = message.ident();
178        let attrs = self
179            .trait_def
180            .trait_def
181            .config()
182            .whitelisted_attributes()
183            .filter_attr(message.attrs());
184        let output_ident = generator::output_ident(message_ident);
185        let output = message.output();
186        let output_type =
187            output.map_or_else(|| quote! { () }, |output| quote! { #output });
188        let selector_bytes = selector.hex_lits();
189        let input_bindings = generator::input_bindings(message.inputs());
190        let input_types = generator::input_types(message.inputs());
191        let encoding_strategy = quote!(::ink::reflect::ScaleEncoding);
192        let arg_list = generator::generate_argument_list(
193            input_types.iter().cloned(),
194            encoding_strategy.clone(),
195        );
196        let mut_tok = message.mutates().then(|| quote! { mut });
197        let cfg_attrs = message.get_cfg_attrs(span);
198        quote_spanned!(span =>
199            #( #cfg_attrs )*
200            type #output_ident = ::ink::env::call::Execution<
201                #arg_list,
202                #output_type,
203                #encoding_strategy
204            >;
205
206            #( #attrs )*
207            #[inline]
208            fn #message_ident(
209                & #mut_tok self
210                #( , #input_bindings : #input_types )*
211            ) -> Self::#output_ident {
212                ::ink::env::call::Execution::new(
213                    ::ink::env::call::ExecutionInput::new(
214                        ::ink::env::call::Selector::new([ #( #selector_bytes ),* ])
215                    )
216                    #(
217                        .push_arg(#input_bindings)
218                    )*
219                )
220            }
221        )
222    }
223}