ink_codegen/generator/trait_def/
call_forwarder.rs1use 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 pub fn generate_call_forwarder(&self) -> TokenStream2 {
42 CallForwarder::from(*self).generate_code()
43 }
44
45 pub fn call_forwarder_ident(&self) -> syn::Ident {
47 self.append_trait_suffix(CallForwarder::SUFFIX)
48 }
49}
50
51#[derive(From)]
53struct CallForwarder<'a> {
54 trait_def: TraitDefinition<'a>,
55}
56
57impl GenerateCode for CallForwarder<'_> {
58 fn generate_code(&self) -> TokenStream2 {
59 let struct_definition = self.generate_struct_definition();
60 let storage_layout_impl = self.generate_storage_layout_impl();
61 let auxiliary_trait_impls = self.generate_auxiliary_trait_impls();
62 let to_from_addr_impls = self.generate_to_from_addr_impls();
63 let call_builder_impl = self.generate_call_builder_trait_impl();
64 let ink_trait_impl = self.generate_ink_trait_impl();
65 quote! {
66 #struct_definition
67 #storage_layout_impl
68 #auxiliary_trait_impls
69 #to_from_addr_impls
70 #call_builder_impl
71 #ink_trait_impl
72 }
73 }
74}
75
76impl CallForwarder<'_> {
77 const SUFFIX: &'static str = "TraitCallForwarder";
79
80 fn span(&self) -> Span {
82 self.trait_def.span()
83 }
84
85 fn ident(&self) -> syn::Ident {
87 self.trait_def.call_forwarder_ident()
88 }
89
90 fn generate_struct_definition(&self) -> TokenStream2 {
103 let span = self.span();
104 let call_forwarder_ident = self.ident();
105 quote_spanned!(span =>
106 #[doc(hidden)]
111 #[allow(non_camel_case_types)]
112 #[::ink::scale_derive(Encode, Decode)]
113 #[repr(transparent)]
114 pub struct #call_forwarder_ident<E, Abi>
115 where
116 E: ::ink::env::Environment,
117 {
118 builder: <Self as ::ink::codegen::TraitCallBuilder>::Builder,
119 _marker: ::core::marker::PhantomData<fn() -> Abi>,
120 }
121 )
122 }
123
124 fn generate_storage_layout_impl(&self) -> TokenStream2 {
132 let span = self.span();
133 let call_forwarder_ident = self.ident();
134 quote_spanned!(span=>
135 #[cfg(feature = "std")]
136 impl<E, Abi> ::ink::storage::traits::StorageLayout
137 for #call_forwarder_ident<E, Abi>
138 where
139 E: ::ink::env::Environment,
140 ::ink::Address: ::ink::storage::traits::StorageLayout,
141 {
142 fn layout(
143 __key: &::ink::primitives::Key,
144 ) -> ::ink::metadata::layout::Layout {
145 <<Self as ::ink::codegen::TraitCallBuilder>::Builder
146 as ::ink::storage::traits::StorageLayout>::layout(__key)
147 }
148 }
149 )
150 }
151
152 fn generate_auxiliary_trait_impls(&self) -> TokenStream2 {
161 let span = self.span();
162 let call_forwarder_ident = self.ident();
163 let sol_codec = if cfg!(any(ink_abi = "sol", ink_abi = "all")) {
164 quote_spanned!(span=>
167 impl<E, Abi> ::ink::SolDecode for #call_forwarder_ident<E, Abi>
168 where
169 E: ::ink::env::Environment,
170 {
171 type SolType = ::ink::Address;
172
173 fn from_sol_type(value: Self::SolType) -> ::core::result::Result<Self, ::ink::sol::Error> {
174 Ok(Self {
175 builder: <<Self as ::ink::codegen::TraitCallBuilder>::Builder
176 as ::ink::env::call::FromAddr>::from_addr(value),
177 _marker: ::core::marker::PhantomData,
178 })
179 }
180 }
181
182 impl<'a, E, Abi> ::ink::SolEncode<'a> for #call_forwarder_ident<E, Abi>
183 where
184 E: ::ink::env::Environment,
185 {
186 type SolType = &'a ::ink::Address;
187
188 fn to_sol_type(&'a self) -> Self::SolType {
189 self.as_ref()
190 }
191 }
192 )
193 } else {
194 quote!()
195 };
196 quote_spanned!(span=>
197 impl<E, Abi> ::core::clone::Clone for #call_forwarder_ident<E, Abi>
199 where
200 E: ::ink::env::Environment,
201 ::ink::Address: ::core::clone::Clone,
202 {
203 #[inline]
204 fn clone(&self) -> Self {
205 Self {
206 builder: <<Self as ::ink::codegen::TraitCallBuilder>::Builder
207 as ::core::clone::Clone>::clone(&self.builder),
208 _marker: self._marker,
209 }
210 }
211 }
212
213 impl<E, Abi> ::core::fmt::Debug for #call_forwarder_ident<E, Abi>
215 where
216 E: ::ink::env::Environment,
217 ::ink::Address: ::core::fmt::Debug,
218 {
219 fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
220 f.debug_struct(::core::stringify!(#call_forwarder_ident))
221 .field("addr", &self.builder.addr)
222 .finish()
223 }
224 }
225
226 #[cfg(feature = "std")]
227 impl<E, Abi> ::ink::scale_info::TypeInfo for #call_forwarder_ident<E, Abi>
229 where
230 E: ::ink::env::Environment,
231 ::ink::Address: ::ink::scale_info::TypeInfo + 'static,
232 {
233 type Identity = <
234 <Self as ::ink::codegen::TraitCallBuilder>::Builder as ::ink::scale_info::TypeInfo
235 >::Identity;
236
237 fn type_info() -> ::ink::scale_info::Type {
238 <
239 <Self as ::ink::codegen::TraitCallBuilder>::Builder as ::ink::scale_info::TypeInfo
240 >::type_info()
241 }
242 }
243
244 #sol_codec
245 )
246 }
247
248 fn generate_to_from_addr_impls(&self) -> TokenStream2 {
256 let span = self.span();
257 let call_forwarder_ident = self.ident();
258 quote_spanned!(span=>
259 impl<E, Abi> ::ink::env::call::FromAddr
260 for #call_forwarder_ident<E, Abi>
261 where
262 E: ::ink::env::Environment,
263 {
264 #[inline]
265 fn from_addr(addr: ::ink::Address) -> Self {
266 Self {
267 builder: <<Self as ::ink::codegen::TraitCallBuilder>::Builder
268 as ::ink::env::call::FromAddr>::from_addr(addr),
269 _marker: ::core::default::Default::default(),
270 }
271 }
272 }
273
274 impl<E, Abi> ::core::convert::From<::ink::Address> for #call_forwarder_ident<E, Abi>
275 where
276 E: ::ink::env::Environment,
277 {
278 fn from(addr: ::ink::Address) -> Self {
279 <Self as ::ink::env::call::FromAddr>::from_addr(addr)
280 }
281 }
282
283 impl<E, Abi> ::ink::ToAddr for #call_forwarder_ident<E, Abi>
284 where
285 E: ::ink::env::Environment,
286 {
287 #[inline]
288 fn to_addr(&self) -> ::ink::Address {
289 <<Self as ::ink::codegen::TraitCallBuilder>::Builder
290 as ::ink::ToAddr>::to_addr(&self.builder)
291 }
292 }
293
294 impl<E, Abi> ::core::convert::AsRef<::ink::Address> for #call_forwarder_ident<E, Abi>
295 where
296 E: ::ink::env::Environment,
297 {
298 fn as_ref(&self) -> &::ink::Address {
299 <_ as ::core::convert::AsRef<::ink::Address>>::as_ref(&self.builder)
300 }
301 }
302
303 impl<E, Abi> ::core::convert::AsMut<::ink::Address> for #call_forwarder_ident<E, Abi>
304 where
305 E: ::ink::env::Environment,
306 {
307 fn as_mut(&mut self) -> &mut ::ink::Address {
308 <_ as ::core::convert::AsMut<::ink::Address>>::as_mut(&mut self.builder)
309 }
310 }
311
312 impl<E, Abi> ::ink::env::ContractEnv for #call_forwarder_ident<E, Abi>
313 where
314 E: ::ink::env::Environment,
315 {
316 type Env = E;
317 }
318 )
319 }
320
321 fn generate_call_builder_trait_impl(&self) -> TokenStream2 {
329 let span = self.trait_def.span();
330 let call_forwarder_ident = self.ident();
331 let call_builder_ident = self.trait_def.call_builder_ident();
332 quote_spanned!(span=>
333 impl<E, Abi> ::ink::codegen::TraitCallBuilder for #call_forwarder_ident<E, Abi>
339 where
340 E: ::ink::env::Environment,
341 {
342 type Builder = #call_builder_ident<E, Abi>;
343
344 #[inline]
345 fn call(&self) -> &<Self as ::ink::codegen::TraitCallBuilder>::Builder {
346 &self.builder
347 }
348
349 #[inline]
350 fn call_mut(&mut self) -> &mut <Self as ::ink::codegen::TraitCallBuilder>::Builder {
351 &mut self.builder
352 }
353 }
354 )
355 }
356
357 fn generate_ink_trait_impl(&self) -> TokenStream2 {
364 let span = self.trait_def.span();
365 let trait_ident = self.trait_def.trait_def.item().ident();
366 let trait_info_ident = self.trait_def.trait_info_ident();
367 let forwarder_ident = self.ident();
368 let message_impls = self.generate_ink_trait_impl_messages();
369 generate_abi_impls!(@tokens |abi| quote_spanned!(span=>
370 impl<E> #trait_ident for #forwarder_ident<E, #abi>
371 where
372 E: ::ink::env::Environment,
373 {
374 #[allow(non_camel_case_types)]
375 type __ink_TraitInfo = #trait_info_ident<E>;
376
377 #message_impls
378 }
379 ))
380 }
381
382 fn generate_ink_trait_impl_messages(&self) -> TokenStream2 {
385 let messages =
386 self.trait_def
387 .trait_def
388 .item()
389 .iter_items()
390 .filter_map(|(item, _)| {
391 item.filter_map_message()
392 .map(|message| self.generate_ink_trait_impl_for_message(&message))
393 });
394 quote! {
395 #( #messages )*
396 }
397 }
398
399 fn generate_ink_trait_impl_for_message(
402 &self,
403 message: &ir::InkTraitMessage,
404 ) -> TokenStream2 {
405 let span = message.span();
406 let trait_ident = self.trait_def.trait_def.item().ident();
407 let forwarder_ident = self.ident();
408 let message_ident = message.ident();
409 let attrs = self
410 .trait_def
411 .trait_def
412 .config()
413 .whitelisted_attributes()
414 .filter_attr(message.attrs());
415 let output_ident = generator::output_ident(message_ident);
416 let output_type = message
417 .output()
418 .cloned()
419 .unwrap_or_else(|| syn::parse_quote!(()));
420 let input_bindings = message.inputs().map(|input| &input.pat).collect::<Vec<_>>();
421 let input_types = message.inputs().map(|input| &input.ty).collect::<Vec<_>>();
422 let call_op = match message.receiver() {
423 ir::Receiver::Ref => quote! { call },
424 ir::Receiver::RefMut => quote! { call_mut },
425 };
426 let mut_tok = message.mutates().then(|| quote! { mut });
427 let panic_str = format!(
428 "encountered error while calling <{forwarder_ident} as {trait_ident}>::{message_ident}",
429 );
430 let cfg_attrs = message.get_cfg_attrs(span);
431 quote_spanned!(span =>
432 #( #cfg_attrs )*
433 type #output_ident = #output_type;
434
435 #( #attrs )*
436 #[inline]
437 fn #message_ident(
438 & #mut_tok self
439 #( , #input_bindings : #input_types )*
440 ) -> Self::#output_ident {
441 <<Self as ::ink::codegen::TraitCallBuilder>::Builder as #trait_ident>::#message_ident(
442 <Self as ::ink::codegen::TraitCallBuilder>::#call_op(self)
443 #(
444 , #input_bindings
445 )*
446 )
447 .try_invoke()
448 .unwrap_or_else(|env_err| ::core::panic!("{}: {:?}", #panic_str, env_err))
449 .unwrap_or_else(|lang_err| ::core::panic!("{}: {:?}", #panic_str, lang_err))
450 }
451 )
452 }
453}