ink_codegen/generator/trait_def/
message_builder.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_message_builder(&self) -> TokenStream2 {
41 MessageBuilder::from(*self).generate_code()
42 }
43
44 pub fn message_builder_ident(&self) -> syn::Ident {
46 self.append_trait_suffix(MessageBuilder::SUFFIX)
47 }
48}
49
50#[derive(From)]
52struct MessageBuilder<'a> {
53 trait_def: TraitDefinition<'a>,
54}
55
56impl GenerateCode for MessageBuilder<'_> {
57 fn generate_code(&self) -> TokenStream2 {
58 let struct_definition = self.generate_struct_definition();
59 let auxiliary_trait_impls = self.generate_auxiliary_trait_impls();
60 let ink_trait_impl = self.generate_ink_trait_impl();
61 quote! {
62 #struct_definition
63 #auxiliary_trait_impls
64 #ink_trait_impl
65 }
66 }
67}
68
69impl MessageBuilder<'_> {
70 const SUFFIX: &'static str = "TraitMessageBuilder";
72
73 fn span(&self) -> Span {
75 self.trait_def.span()
76 }
77
78 fn generate_struct_definition(&self) -> TokenStream2 {
84 let span = self.span();
85 let message_builder_ident = self.trait_def.message_builder_ident();
86 quote_spanned!(span =>
87 #[doc(hidden)]
91 #[allow(non_camel_case_types)]
92 #[::ink::scale_derive(Encode, Decode, TypeInfo)]
93 pub struct #message_builder_ident<E> {
94 marker: ::core::marker::PhantomData<fn() -> E>,
95 }
96 )
97 }
98
99 fn generate_auxiliary_trait_impls(&self) -> TokenStream2 {
107 let span = self.span();
108 let message_builder_ident = self.trait_def.message_builder_ident();
109 quote_spanned!(span=>
110 impl<E> ::core::default::Default for #message_builder_ident<E>
111 where
112 E: ::ink::env::Environment,
113 {
114 fn default() -> Self {
115 Self { marker: ::core::default::Default::default() }
116 }
117 }
118 )
119 }
120
121 fn generate_ink_trait_impl(&self) -> TokenStream2 {
129 let span = self.trait_def.span();
130 let trait_ident = self.trait_def.trait_def.item().ident();
131 let trait_info_ident = self.trait_def.trait_info_ident();
132 let message_builder_ident = self.trait_def.message_builder_ident();
133 let message_impls = self.generate_ink_trait_impl_messages();
134 quote_spanned!(span=>
135 impl<E> ::ink::env::ContractEnv for #message_builder_ident<E>
136 where
137 E: ::ink::env::Environment,
138 {
139 type Env = E;
140 }
141
142 impl<E> #trait_ident for #message_builder_ident<E>
143 where
144 E: ::ink::env::Environment,
145 {
146 #[allow(non_camel_case_types)]
147 type __ink_TraitInfo = #trait_info_ident<E>;
148
149 #message_impls
150 }
151 )
152 }
153
154 fn generate_ink_trait_impl_messages(&self) -> TokenStream2 {
157 let messages = self.trait_def.trait_def.item().iter_items().filter_map(
158 |(item, selector)| {
159 item.filter_map_message().map(|message| {
160 self.generate_ink_trait_impl_for_message(&message, selector)
161 })
162 },
163 );
164 quote! {
165 #( #messages )*
166 }
167 }
168
169 fn generate_ink_trait_impl_for_message(
172 &self,
173 message: &ir::InkTraitMessage,
174 selector: ir::Selector,
175 ) -> TokenStream2 {
176 let span = message.span();
177 let message_ident = message.ident();
178 let attrs = self
179 .trait_def
180 .trait_def
181 .config()
182 .whitelisted_attributes()
183 .filter_attr(message.attrs());
184 let output_ident = generator::output_ident(message_ident);
185 let output = message.output();
186 let output_type =
187 output.map_or_else(|| quote! { () }, |output| quote! { #output });
188 let selector_bytes = selector.hex_lits();
189 let input_bindings = generator::input_bindings(message.inputs());
190 let input_types = generator::input_types(message.inputs());
191 let encoding_strategy = quote!(::ink::reflect::ScaleEncoding);
192 let arg_list = generator::generate_argument_list(
193 input_types.iter().cloned(),
194 encoding_strategy.clone(),
195 );
196 let mut_tok = message.mutates().then(|| quote! { mut });
197 let cfg_attrs = message.get_cfg_attrs(span);
198 quote_spanned!(span =>
199 #( #cfg_attrs )*
200 type #output_ident = ::ink::env::call::Execution<
201 #arg_list,
202 #output_type,
203 #encoding_strategy
204 >;
205
206 #( #attrs )*
207 #[inline]
208 fn #message_ident(
209 & #mut_tok self
210 #( , #input_bindings : #input_types )*
211 ) -> Self::#output_ident {
212 ::ink::env::call::Execution::new(
213 ::ink::env::call::ExecutionInput::new(
214 ::ink::env::call::Selector::new([ #( #selector_bytes ),* ])
215 )
216 #(
217 .push_arg(#input_bindings)
218 )*
219 )
220 }
221 )
222 }
223}