ink_codegen/generator/as_dependency/
call_builder.rs1use derive_more::From;
16use ink_primitives::abi::Abi;
17use ir::Callable;
18use proc_macro2::TokenStream as TokenStream2;
19use quote::{
20    format_ident,
21    quote,
22    quote_spanned,
23};
24use syn::spanned::Spanned as _;
25
26use crate::{
27    GenerateCode,
28    generator,
29    generator::sol,
30};
31
32#[derive(From)]
44pub struct CallBuilder<'a> {
45    contract: &'a ir::Contract,
46}
47
48impl GenerateCode for CallBuilder<'_> {
49    fn generate_code(&self) -> TokenStream2 {
50        let call_builder_struct = self.generate_struct();
51        let auxiliary_trait_impls = self.generate_auxiliary_trait_impls();
52        let call_builder_impls = self.generate_call_forwarder_impls();
53        let call_builder_inherent_impls = self.generate_call_builder_inherent_impls();
54        quote! {
55            const _: () = {
56                #call_builder_struct
57                #auxiliary_trait_impls
58                #call_builder_impls
59                #call_builder_inherent_impls
60            };
61        }
62    }
63}
64
65impl CallBuilder<'_> {
66    fn call_builder_ident() -> syn::Ident {
75        format_ident!("CallBuilder")
76    }
77
78    fn generate_struct(&self) -> TokenStream2 {
79        let span = self.contract.module().storage().span();
80        let storage_ident = self.contract.module().storage().ident();
81        let cb_ident = Self::call_builder_ident();
82        let sol_codec = if cfg!(any(ink_abi = "sol", ink_abi = "all")) {
83            quote_spanned!(span=>
86                impl<Abi> ::ink::SolDecode for #cb_ident<Abi> {
87                    type SolType = ::ink::Address;
88
89                    fn from_sol_type(value: Self::SolType) -> ::core::result::Result<Self, ::ink::sol::Error> {
90                        Ok(Self {
91                            addr: value,
92                            _marker: ::core::marker::PhantomData,
93                        })
94                    }
95                }
96
97                impl<'a, Abi> ::ink::SolEncode<'a> for #cb_ident<Abi> {
98                    type SolType = &'a ::ink::Address;
99
100                    fn to_sol_type(&'a self) -> Self::SolType {
101                        &self.addr
102                    }
103                }
104            )
105        } else {
106            quote!()
107        };
108        quote_spanned!(span=>
109            #[repr(transparent)]
114            #[derive(
115                ::core::fmt::Debug,
116                ::core::hash::Hash,
117                ::core::cmp::PartialEq,
118                ::core::cmp::Eq,
119                ::core::clone::Clone,
120            )]
121            #[::ink::scale_derive(Encode, Decode)]
122            pub struct #cb_ident<Abi> {
123                addr: ::ink::Address,
124                _marker: core::marker::PhantomData<Abi>,
125            }
126
127            const _: () = {
128                impl ::ink::codegen::ContractCallBuilder for #storage_ident {
129                    type Type<Abi> = #cb_ident<Abi>;
130                }
131
132                impl<Abi> ::ink::env::ContractEnv for #cb_ident<Abi> {
133                    type Env = <#storage_ident as ::ink::env::ContractEnv>::Env;
134                }
135
136                #[cfg(feature = "std")]
137                impl<Abi> ::ink::storage::traits::StorageLayout for #cb_ident<Abi> {
138                    fn layout(
139                        __key: &::ink::primitives::Key,
140                    ) -> ::ink::metadata::layout::Layout {
141                        ::ink::metadata::layout::Layout::Struct(
142                            ::ink::metadata::layout::StructLayout::new(
143                                ::core::stringify!(#cb_ident),
144                                [
145                                    ::ink::metadata::layout::FieldLayout::new(
146                                        "addr",
147                                        <::ink::Address
148                                            as ::ink::storage::traits::StorageLayout>::layout(__key)
149                                    )
150                                ]
151                            )
152                        )
153                    }
154                }
155
156                #[cfg(feature = "std")]
157                impl<Abi> ::ink::scale_info::TypeInfo for #cb_ident<Abi>
159                where
160                    ::ink::Address: ::ink::scale_info::TypeInfo + 'static,
161                {
162                    type Identity = ::ink::Address;
163
164                    fn type_info() -> ::ink::scale_info::Type {
165                        <::ink::Address as ::ink::scale_info::TypeInfo>::type_info()
166                    }
167                }
168
169                #sol_codec
170            };
171        )
172    }
173
174    fn generate_auxiliary_trait_impls(&self) -> TokenStream2 {
179        let span = self.contract.module().storage().span();
180        let cb_ident = Self::call_builder_ident();
181        quote_spanned!(span=>
182            impl<Abi> ::ink::env::call::FromAddr for #cb_ident<Abi> {
183                #[inline]
184                fn from_addr(addr: ::ink::Address) -> Self {
185                    Self { addr, _marker: ::core::default::Default::default(), }
186                }
187            }
188
189            impl<Abi> ::ink::ToAddr for #cb_ident<Abi> {
190                #[inline]
191                fn to_addr(&self) -> ::ink::Address {
192                    <::ink::Address as ::core::clone::Clone>::clone(&self.addr)
193                }
194            }
195
196            impl<Abi> ::core::convert::AsRef<::ink::Address> for #cb_ident<Abi> {
197                fn as_ref(&self) -> &::ink::Address {
198                    &self.addr
199                }
200            }
201
202            impl<Abi> ::core::convert::AsMut<::ink::Address> for #cb_ident<Abi> {
203                fn as_mut(&mut self) -> &mut ::ink::Address {
204                    &mut self.addr
205                }
206            }
207        )
208    }
209
210    fn generate_call_forwarder_impls(&self) -> TokenStream2 {
218        self.contract
219            .module()
220            .impls()
221            .filter_map(|impl_block| {
222                impl_block.trait_path().map(|trait_path| {
224                    self.generate_code_for_trait_impl(trait_path, impl_block)
225                })
226            })
227            .collect()
228    }
229
230    fn generate_code_for_trait_impl(
233        &self,
234        trait_path: &syn::Path,
235        impl_block: &ir::ItemImpl,
236    ) -> TokenStream2 {
237        let call_forwarder_impl =
238            self.generate_call_forwarder_for_trait_impl(trait_path, impl_block);
239        let ink_trait_impl = self.generate_ink_trait_impl(trait_path, impl_block);
240        quote! {
241            #call_forwarder_impl
242            #ink_trait_impl
243        }
244    }
245
246    fn generate_call_forwarder_for_trait_impl(
249        &self,
250        trait_path: &syn::Path,
251        impl_block: &ir::ItemImpl,
252    ) -> TokenStream2 {
253        let span = impl_block.span();
254        let cb_ident = Self::call_builder_ident();
255        let trait_info_id = generator::generate_reference_to_trait_info(span, trait_path);
256        generate_abi_impls!(@tokens |abi| quote_spanned!(span=>
257            #[doc(hidden)]
258            impl ::ink::codegen::TraitCallForwarderFor<{#trait_info_id}> for #cb_ident<#abi> {
259                type Forwarder = <<Self as #trait_path>::__ink_TraitInfo as ::ink::codegen::TraitCallForwarder>::Forwarder<#abi>;
260
261                #[inline]
262                fn forward(&self) -> &Self::Forwarder {
263                    unsafe {
271                        &*(&self.addr as *const ::ink::Address as *const Self::Forwarder)
272                    }
273                }
274
275                #[inline]
276                fn forward_mut(&mut self) -> &mut Self::Forwarder {
277                    unsafe {
284                        &mut *(&mut self.addr as *mut ::ink::Address as *mut Self::Forwarder)
285                    }
286                }
287
288                #[inline]
289                fn build(&self) -> &<Self::Forwarder as ::ink::codegen::TraitCallBuilder>::Builder {
290                    <_ as ::ink::codegen::TraitCallBuilder>::call(
291                        <Self as ::ink::codegen::TraitCallForwarderFor<{#trait_info_id}>>::forward(self)
292                    )
293                }
294
295                #[inline]
296                fn build_mut(&mut self)
297                    -> &mut <Self::Forwarder as ::ink::codegen::TraitCallBuilder>::Builder
298                {
299                    <_ as ::ink::codegen::TraitCallBuilder>::call_mut(
300                        <Self as ::ink::codegen::TraitCallForwarderFor<{#trait_info_id}>>::forward_mut(self)
301                    )
302                }
303            }
304        ))
305    }
306
307    fn generate_ink_trait_impl(
309        &self,
310        trait_path: &syn::Path,
311        impl_block: &ir::ItemImpl,
312    ) -> TokenStream2 {
313        let span = impl_block.span();
314        let cb_ident = Self::call_builder_ident();
315        let messages = impl_block
316            .iter_messages()
317            .map(|message| self.generate_ink_trait_impl_for_message(trait_path, message));
318        let messages = quote! {
319            #( #messages )*
320        };
321        generate_abi_impls!(@tokens |abi| quote_spanned!(span=>
322            impl #trait_path for #cb_ident<#abi> {
323                type __ink_TraitInfo = <::ink::reflect::TraitDefinitionRegistry<Environment>
324                    as #trait_path>::__ink_TraitInfo;
325
326                #messages
327            }
328        ))
329    }
330
331    fn generate_ink_trait_impl_for_message(
334        &self,
335        trait_path: &syn::Path,
336        message: ir::CallableWithSelector<ir::Message>,
337    ) -> TokenStream2 {
338        use ir::Callable as _;
339        let span = message.span();
340        let message_ident = message.ident();
341        let output_ident = generator::output_ident(message_ident);
342        let cfg_attrs = message.get_cfg_attrs(span);
343        let trait_info_id = generator::generate_reference_to_trait_info(span, trait_path);
344        let (input_bindings, input_types): (Vec<_>, Vec<_>) = message
345            .callable()
346            .inputs()
347            .map(|input| (&input.pat, &input.ty))
348            .unzip();
349        let mut_token = message
350            .receiver()
351            .is_ref_mut()
352            .then(|| Some(quote! { mut }));
353        let build_cmd = match message.receiver() {
354            ir::Receiver::Ref => quote! { build },
355            ir::Receiver::RefMut => quote! { build_mut },
356        };
357        let attrs = self
358            .contract
359            .config()
360            .whitelisted_attributes()
361            .filter_attr(message.attrs().to_vec());
362        quote_spanned!(span=>
363            #( #cfg_attrs )*
364            type #output_ident = <<<
365                Self
366                as ::ink::codegen::TraitCallForwarderFor<{#trait_info_id}>>::Forwarder
367                as ::ink::codegen::TraitCallBuilder>::Builder
368                as #trait_path>::#output_ident;
369
370            #[inline]
371            #( #attrs )*
372            fn #message_ident(
373                & #mut_token self
374                #( , #input_bindings: #input_types )*
375            ) -> Self::#output_ident {
376                <_ as #trait_path>::#message_ident(
377                    <Self as ::ink::codegen::TraitCallForwarderFor<{#trait_info_id}>>::#build_cmd(self)
378                    #( , #input_bindings )*
379                )
380            }
381        )
382    }
383
384    fn generate_call_builder_inherent_impls(&self) -> TokenStream2 {
391        self.contract
392            .module()
393            .impls()
394            .filter(|impl_block| impl_block.trait_path().is_none())
395            .map(|impl_block| self.generate_call_builder_inherent_impl(impl_block))
396            .collect()
397    }
398
399    fn generate_call_builder_inherent_impl(
407        &self,
408        impl_block: &ir::ItemImpl,
409    ) -> TokenStream2 {
410        let span = impl_block.span();
411        let cb_ident = Self::call_builder_ident();
412        generate_abi_impls!(@type |abi| {
413            let abi_ty = match abi {
414                Abi::Ink => quote!(::ink::abi::Ink),
415                Abi::Sol => quote!(::ink::abi::Sol),
416            };
417            let messages = impl_block.iter_messages().map(|message| {
418                self.generate_call_builder_inherent_impl_for_message(message, abi)
419            });
420            quote_spanned!(span=>
421                impl #cb_ident<#abi_ty> {
422                    #( #messages )*
423                }
424            )
425        })
426    }
427
428    fn generate_call_builder_inherent_impl_for_message(
435        &self,
436        message: ir::CallableWithSelector<ir::Message>,
437        abi: Abi,
438    ) -> TokenStream2 {
439        let span = message.span();
440        let callable = message.callable();
441        let message_ident = message.ident();
442        let attrs = self
443            .contract
444            .config()
445            .whitelisted_attributes()
446            .filter_attr(message.attrs().to_vec());
447        let input_bindings = generator::input_bindings(callable.inputs());
448        let input_types = generator::input_types(message.inputs());
449        let mut_tok = callable.receiver().is_ref_mut().then(|| quote! { mut });
450        let return_type = message
451            .output()
452            .map(quote::ToTokens::to_token_stream)
453            .unwrap_or_else(|| quote::quote! { () });
454        let output_span = return_type.span();
455        let (selector_bytes, abi_ty, build_call_fn) = match abi {
456            Abi::Ink => {
457                let selector = message.composed_selector();
458                let selector_bytes = selector.hex_lits();
459                (
460                    quote!([ #( #selector_bytes ),* ]),
461                    quote!(::ink::abi::Ink),
462                    quote!(build_call_ink),
463                )
464            }
465            Abi::Sol => {
466                (
467                    sol::utils::selector(&message),
468                    quote!(::ink::abi::Sol),
469                    quote!(build_call_sol),
470                )
471            }
472        };
473        let arg_list = generator::generate_argument_list(
474            input_types.iter().cloned(),
475            abi_ty.clone(),
476        );
477        let output_type = quote_spanned!(output_span=>
478            ::ink::env::call::CallBuilder<
479                Environment,
480                ::ink::env::call::utils::Set< ::ink::env::call::Call >,
481                ::ink::env::call::utils::Set< ::ink::env::call::ExecutionInput<#arg_list, #abi_ty> >,
482                ::ink::env::call::utils::Set< ::ink::env::call::utils::ReturnType<#return_type> >,
483            >
484        );
485        quote_spanned!(span=>
486            #( #attrs )*
487            #[allow(clippy::type_complexity)]
488            #[inline]
489            pub fn #message_ident(
490                & #mut_tok self
491                #( , #input_bindings : #input_types )*
492            ) -> #output_type {
493                ::ink::env::call::#build_call_fn::<Environment>()
494                    .call(::ink::ToAddr::to_addr(self))
495                    .exec_input(
496                        ::ink::env::call::ExecutionInput::new(
497                            ::ink::env::call::Selector::new(#selector_bytes)
498                        )
499                        #(
500                            .push_arg(#input_bindings)
501                        )*
502                    )
503                    .returns::<#return_type>()
504            }
505        )
506    }
507}