ink_codegen/generator/
chain_extension.rs1use crate::GenerateCode;
16use derive_more::From;
17use ir::ChainExtensionMethod;
18use proc_macro2::TokenStream as TokenStream2;
19use quote::{
20 format_ident,
21 quote_spanned,
22};
23use syn::spanned::Spanned;
24
25#[derive(From)]
27pub struct ChainExtension<'a> {
28 extension: &'a ir::ChainExtension,
29}
30
31impl ChainExtension<'_> {
32 fn generate_for_instance_method(
33 method: &ChainExtensionMethod,
34 error_code: &syn::Type,
35 ) -> TokenStream2 {
36 let span = method.span();
37 let attrs = method.attrs();
38 let ident = method.ident();
39 let id = method.id().into_u32();
40 let sig = method.sig();
41 let inputs = &sig.inputs;
42 let input_bindings = method.inputs().map(|pat_type| &pat_type.pat);
43 let input_types = method.inputs().map(|pat_type| &pat_type.ty);
44
45 let compound_input_type = match inputs.len() {
51 0 => quote_spanned!(span=> ()),
52 1 => quote_spanned!(span=> #( #input_types )* ),
53 _n => quote_spanned!(span=> ( #( #input_types ),* ) ),
54 };
55
56 let compound_input_bindings = match inputs.len() {
62 0 => quote_spanned!(span=> ()),
63 1 => quote_spanned!(span=> #( #input_bindings )* ),
64 _n => quote_spanned!(span=> ( #( #input_bindings ),* ) ),
65 };
66
67 let output = &sig.output;
68 let output_type = match output {
69 syn::ReturnType::Default => quote_spanned!(output.span()=> ()),
70 syn::ReturnType::Type(_arrow, ty) => {
71 quote_spanned!(output.span()=> #ty)
72 }
73 };
74
75 let handle_status = method.handle_status();
76
77 let handle_status_token = if handle_status {
78 quote_spanned!(span=>
79 true
80 )
81 } else {
82 quote_spanned!(span=>
83 false
84 )
85 };
86
87 let error_code_handling = if handle_status {
88 quote_spanned!(span=>
89 .handle_error_code::<#error_code>()
90 )
91 } else {
92 quote_spanned!(span=>
93 .ignore_error_code()
94 )
95 };
96
97 let return_type = quote_spanned!(span =>
98 <::ink::ValueReturned as ::ink::Output<{ ::ink::is_result_type!(#output_type) }, #handle_status_token, #output_type, #error_code>>::ReturnType
99 );
100
101 let where_output_impls_from_error_code = Some(quote_spanned!(span=>
103 <#return_type as ::ink::IsResultType>::Err: ::core::convert::From<#error_code>,
104 )).filter(|_| handle_status);
105
106 quote_spanned!(span=>
107 #( #attrs )*
108 #[inline]
109 pub fn #ident(self, #inputs) -> #return_type
110 where
111 #where_output_impls_from_error_code
112 {
113 ::ink::env::chain_extension::ChainExtensionMethod::build(#id)
114 .input::<#compound_input_type>()
115 .output::<#output_type, {::ink::is_result_type!(#output_type)}>()
116 #error_code_handling
117 .call(&#compound_input_bindings)
118 }
119 )
120 }
121}
122
123impl GenerateCode for ChainExtension<'_> {
124 fn generate_code(&self) -> TokenStream2 {
125 let span = self.extension.span();
126 let attrs = self.extension.attrs();
127 let ident = self.extension.ident();
128 let error_code = self.extension.error_code();
129 let instance_methods = self
130 .extension
131 .iter_methods()
132 .map(|method| Self::generate_for_instance_method(method, error_code));
133 let instance_ident = format_ident!("__ink_{}Instance", ident);
134 quote_spanned!(span =>
135 #(#attrs)*
136 #[::ink::scale_derive(TypeInfo)]
137 pub enum #ident {}
138
139 const _: () = {
140 #[allow(non_camel_case_types)]
141 struct __ink_Private;
142 #[allow(non_camel_case_types)]
143 pub struct #instance_ident {
144 __ink_private: __ink_Private
145 }
146
147 impl #instance_ident {
148 #( #instance_methods )*
149 }
150
151 impl ::ink::ChainExtensionInstance for #ident {
152 type Instance = #instance_ident;
153
154 fn instantiate() -> Self::Instance {
155 Self::Instance { __ink_private: __ink_Private }
156 }
157 }
158 };
159 )
160 }
161}