ink_codegen/generator/as_dependency/
contract_ref.rs1use crate::{
16 generator,
17 GenerateCode,
18};
19use derive_more::From;
20use ir::{
21 Callable,
22 IsDocAttribute as _,
23};
24use proc_macro2::TokenStream as TokenStream2;
25use quote::{
26 quote,
27 quote_spanned,
28};
29use syn::spanned::Spanned as _;
30
31#[derive(From)]
43pub struct ContractRef<'a> {
44 contract: &'a ir::Contract,
45}
46
47impl GenerateCode for ContractRef<'_> {
48 fn generate_code(&self) -> TokenStream2 {
49 let contract_ref = self.generate_struct();
50 let contract_ref_trait_impls = self.generate_contract_trait_impls();
51 let contract_ref_inherent_impls = self.generate_contract_inherent_impls();
52 let call_builder_trait_impl = self.generate_call_builder_trait_impl();
53 let auxiliary_trait_impls = self.generate_auxiliary_trait_impls();
54 quote! {
55 #contract_ref
56 #contract_ref_trait_impls
57 #contract_ref_inherent_impls
58 #call_builder_trait_impl
59 #auxiliary_trait_impls
60 }
61 }
62}
63
64impl ContractRef<'_> {
65 fn generate_contract_ref_ident(&self) -> syn::Ident {
67 quote::format_ident!("{}Ref", self.contract.module().storage().ident())
68 }
69
70 fn generate_struct(&self) -> TokenStream2 {
77 let span = self.contract.module().storage().span();
78 let doc_attrs = self
79 .contract
80 .module()
81 .storage()
82 .attrs()
83 .iter()
84 .filter(|&x| syn::Attribute::is_doc_attribute(x))
85 .cloned();
86 let storage_ident = self.contract.module().storage().ident();
87 let ref_ident = self.generate_contract_ref_ident();
88 quote_spanned!(span=>
89 #[cfg_attr(feature = "std", derive(
90 ::ink::storage::traits::StorageLayout,
91 ))]
92 #[derive(
93 ::core::fmt::Debug,
94 ::core::hash::Hash,
95 ::core::cmp::PartialEq,
96 ::core::cmp::Eq,
97 ::core::clone::Clone,
98 )]
99 #[::ink::scale_derive(Encode, Decode, TypeInfo)]
100 #( #doc_attrs )*
101 pub struct #ref_ident {
102 inner: <#storage_ident as ::ink::codegen::ContractCallBuilder>::Type,
103 }
104
105 const _: () = {
106 impl ::ink::env::ContractReference for #storage_ident {
107 type Type = #ref_ident;
108 }
109
110 impl ::ink::env::call::ConstructorReturnType<#ref_ident> for #storage_ident {
111 type Output = #ref_ident;
112 type Error = ();
113
114 fn ok(value: #ref_ident) -> Self::Output {
115 value
116 }
117 }
118
119 impl<E> ::ink::env::call::ConstructorReturnType<#ref_ident>
120 for ::core::result::Result<#storage_ident, E>
121 where
122 E: ::ink::scale::Decode
123 {
124 const IS_RESULT: bool = true;
125
126 type Output = ::core::result::Result<#ref_ident, E>;
127 type Error = E;
128
129 fn ok(value: #ref_ident) -> Self::Output {
130 ::core::result::Result::Ok(value)
131 }
132
133 fn err(err: Self::Error) -> ::core::option::Option<Self::Output> {
134 ::core::option::Option::Some(::core::result::Result::Err(err))
135 }
136 }
137
138 impl ::ink::env::ContractEnv for #ref_ident {
139 type Env = <#storage_ident as ::ink::env::ContractEnv>::Env;
140 }
141 };
142 )
143 }
144
145 fn generate_auxiliary_trait_impls(&self) -> TokenStream2 {
150 let span = self.contract.module().storage().span();
151 let storage_ident = self.contract.module().storage().ident();
152 let ref_ident = self.generate_contract_ref_ident();
153 quote_spanned!(span=>
154 impl ::ink::env::call::FromAccountId<Environment> for #ref_ident {
155 #[inline]
156 fn from_account_id(account_id: AccountId) -> Self {
157 Self { inner: <<#storage_ident
158 as ::ink::codegen::ContractCallBuilder>::Type
159 as ::ink::env::call::FromAccountId<Environment>>::from_account_id(account_id)
160 }
161 }
162 }
163
164 impl ::ink::ToAccountId<Environment> for #ref_ident {
165 #[inline]
166 fn to_account_id(&self) -> AccountId {
167 <<#storage_ident as ::ink::codegen::ContractCallBuilder>::Type
168 as ::ink::ToAccountId<Environment>>::to_account_id(&self.inner)
169 }
170 }
171
172 impl ::core::convert::AsRef<AccountId> for #ref_ident {
173 fn as_ref(&self) -> &AccountId {
174 <_ as ::core::convert::AsRef<AccountId>>::as_ref(&self.inner)
175 }
176 }
177
178 impl ::core::convert::AsMut<AccountId> for #ref_ident {
179 fn as_mut(&mut self) -> &mut AccountId {
180 <_ as ::core::convert::AsMut<AccountId>>::as_mut(&mut self.inner)
181 }
182 }
183 )
184 }
185
186 fn generate_call_builder_trait_impl(&self) -> TokenStream2 {
191 let span = self.contract.module().storage().span();
192 let ref_ident = self.generate_contract_ref_ident();
193 let storage_ident = self.contract.module().storage().ident();
194 quote_spanned!(span=>
195 const _: () = {
196 impl ::ink::codegen::TraitCallBuilder for #ref_ident {
197 type Builder = <#storage_ident as ::ink::codegen::ContractCallBuilder>::Type;
198
199 #[inline]
200 fn call(&self) -> &Self::Builder {
201 &self.inner
202 }
203
204 #[inline]
205 fn call_mut(&mut self) -> &mut Self::Builder {
206 &mut self.inner
207 }
208 }
209 };
210 )
211 }
212
213 fn generate_contract_trait_impls(&self) -> TokenStream2 {
220 self.contract
221 .module()
222 .impls()
223 .filter_map(|impl_block| {
224 impl_block.trait_path().map(|trait_path| {
226 self.generate_contract_trait_impl(trait_path, impl_block)
227 })
228 })
229 .collect()
230 }
231
232 fn generate_contract_trait_impl(
237 &self,
238 trait_path: &syn::Path,
239 impl_block: &ir::ItemImpl,
240 ) -> TokenStream2 {
241 let span = impl_block.span();
242 let attrs = impl_block.attrs();
243 let forwarder_ident = self.generate_contract_ref_ident();
244 let messages = self.generate_contract_trait_impl_messages(trait_path, impl_block);
245 quote_spanned!(span=>
246 #( #attrs )*
247 impl #trait_path for #forwarder_ident {
248 type __ink_TraitInfo = <::ink::reflect::TraitDefinitionRegistry<Environment>
249 as #trait_path>::__ink_TraitInfo;
250
251 #messages
252 }
253 )
254 }
255
256 fn generate_contract_trait_impl_messages(
259 &self,
260 trait_path: &syn::Path,
261 impl_block: &ir::ItemImpl,
262 ) -> TokenStream2 {
263 impl_block
264 .iter_messages()
265 .map(|message| {
266 self.generate_contract_trait_impl_for_message(trait_path, message)
267 })
268 .collect()
269 }
270
271 fn generate_contract_trait_impl_for_message(
274 &self,
275 trait_path: &syn::Path,
276 message: ir::CallableWithSelector<ir::Message>,
277 ) -> TokenStream2 {
278 use ir::Callable as _;
279 let span = message.span();
280 let trait_info_id = generator::generate_reference_to_trait_info(span, trait_path);
281 let message_ident = message.ident();
282 let output_ident = generator::output_ident(message_ident);
283 let call_operator = match message.receiver() {
284 ir::Receiver::Ref => quote! { call },
285 ir::Receiver::RefMut => quote! { call_mut },
286 };
287 let forward_operator = match message.receiver() {
288 ir::Receiver::Ref => quote! { forward },
289 ir::Receiver::RefMut => quote! { forward_mut },
290 };
291 let mut_token = message.receiver().is_ref_mut().then(|| quote! { mut });
292 let input_idents = generator::input_message_idents(message.inputs());
293 let input_types = message.inputs().map(|input| &input.ty).collect::<Vec<_>>();
294 let cfg_attrs = message.get_cfg_attrs(span);
295 quote_spanned!(span=>
296 #( #cfg_attrs )*
297 type #output_ident =
298 <<Self::__ink_TraitInfo as ::ink::codegen::TraitCallForwarder>::Forwarder as #trait_path>::#output_ident;
299
300 #[inline]
301 #( #cfg_attrs )*
302 fn #message_ident(
303 & #mut_token self
304 #( , #input_idents : #input_types )*
305 ) -> Self::#output_ident {
306 <_ as #trait_path>::#message_ident(
307 <_ as ::ink::codegen::TraitCallForwarderFor<{#trait_info_id}>>::#forward_operator(
308 <Self as ::ink::codegen::TraitCallBuilder>::#call_operator(self),
309 )
310 #( , #input_idents )*
311 )
312 }
313 )
314 }
315
316 fn generate_contract_inherent_impls(&self) -> TokenStream2 {
323 self.contract
324 .module()
325 .impls()
326 .filter(|impl_block| {
327 impl_block.trait_path().is_none()
329 })
330 .map(|impl_block| self.generate_contract_inherent_impl(impl_block))
331 .collect()
332 }
333
334 fn generate_contract_inherent_impl(&self, impl_block: &ir::ItemImpl) -> TokenStream2 {
343 let span = impl_block.span();
344 let attrs = impl_block.attrs();
345 let forwarder_ident = self.generate_contract_ref_ident();
346 let messages = impl_block
347 .iter_messages()
348 .map(|message| self.generate_contract_inherent_impl_for_message(message));
349 let constructors = impl_block.iter_constructors().map(|constructor| {
350 self.generate_contract_inherent_impl_for_constructor(constructor)
351 });
352 quote_spanned!(span=>
353 #( #attrs )*
354 impl #forwarder_ident {
355 #( #constructors )*
356 #( #messages )*
357 }
358 )
359 }
360
361 fn generate_contract_inherent_impl_for_message(
369 &self,
370 message: ir::CallableWithSelector<ir::Message>,
371 ) -> TokenStream2 {
372 use ir::Callable as _;
373 let span = message.span();
374 let attrs = self
375 .contract
376 .config()
377 .whitelisted_attributes()
378 .filter_attr(message.attrs().to_vec());
379 let storage_ident = self.contract.module().storage().ident();
380 let message_ident = message.ident();
381 let try_message_ident = message.try_ident();
382 let call_operator = match message.receiver() {
383 ir::Receiver::Ref => quote! { call },
384 ir::Receiver::RefMut => quote! { call_mut },
385 };
386 let mut_token = message.receiver().is_ref_mut().then(|| quote! { mut });
387 let input_idents = generator::input_message_idents(message.inputs());
388 let input_types = message.inputs().map(|input| &input.ty).collect::<Vec<_>>();
389 let output_type = message.output().map(|ty| quote! { -> #ty });
390 let wrapped_output_type = message.wrapped_output();
391 quote_spanned!(span=>
392 #( #attrs )*
393 #[inline]
394 pub fn #message_ident(
395 & #mut_token self
396 #( , #input_idents : #input_types )*
397 ) #output_type {
398 self.#try_message_ident( #( #input_idents, )* )
399 .unwrap_or_else(|error| ::core::panic!(
400 "encountered error while calling {}::{}: {:?}",
401 ::core::stringify!(#storage_ident),
402 ::core::stringify!(#message_ident),
403 error,
404 ))
405 }
406
407 #( #attrs )*
408 #[inline]
409 pub fn #try_message_ident(
410 & #mut_token self
411 #( , #input_idents : #input_types )*
412 ) -> #wrapped_output_type {
413 <Self as ::ink::codegen::TraitCallBuilder>::#call_operator(self)
414 .#message_ident( #( #input_idents ),* )
415 .try_invoke()
416 .unwrap_or_else(|error| ::core::panic!(
417 "encountered error while calling {}::{}: {:?}",
418 ::core::stringify!(#storage_ident),
419 ::core::stringify!(#message_ident),
420 error,
421 ))
422 }
423 )
424 }
425
426 fn generate_contract_inherent_impl_for_constructor(
434 &self,
435 constructor: ir::CallableWithSelector<ir::Constructor>,
436 ) -> TokenStream2 {
437 let span = constructor.span();
438 let attrs = self
439 .contract
440 .config()
441 .whitelisted_attributes()
442 .filter_attr(constructor.attrs().to_vec());
443 let constructor_ident = constructor.ident();
444 let selector_bytes = constructor.composed_selector().hex_lits();
445 let input_bindings = generator::input_bindings(constructor.inputs());
446 let input_types = generator::input_types(constructor.inputs());
447 let storage_ident = self.contract.module().storage().ident();
448 let arg_list = generator::generate_argument_list(input_types.iter().cloned());
449 let ret_type = constructor
450 .output()
451 .map(quote::ToTokens::to_token_stream)
452 .unwrap_or_else(|| quote::quote! { Self });
453 quote_spanned!(span =>
454 #( #attrs )*
455 #[inline]
456 #[allow(clippy::type_complexity)]
457 pub fn #constructor_ident(
458 #( #input_bindings : #input_types ),*
459 ) -> ::ink::env::call::CreateBuilder<
460 Environment,
461 Self,
462 ::ink::env::call::utils::Unset<Hash>,
463 ::ink::env::call::utils::Set<::ink::env::call::LimitParamsV2<<#storage_ident as ::ink::env::ContractEnv>::Env>>,
464 ::ink::env::call::utils::Unset<Balance>,
465 ::ink::env::call::utils::Set<::ink::env::call::ExecutionInput<#arg_list>>,
466 ::ink::env::call::utils::Unset<::ink::env::call::state::Salt>,
467 ::ink::env::call::utils::Set<::ink::env::call::utils::ReturnType<#ret_type>>,
468 > {
469 ::ink::env::call::build_create::<Self>()
470 .exec_input(
471 ::ink::env::call::ExecutionInput::new(
472 ::ink::env::call::Selector::new([ #( #selector_bytes ),* ])
473 )
474 #(
475 .push_arg(#input_bindings)
476 )*
477 )
478 .returns::<#ret_type>()
479 }
480 )
481 }
482}