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