ink_codegen/generator/trait_def/
message_builder.rs1use derive_more::From;
16use ink_primitives::abi::Abi;
17use proc_macro2::{
18 Span,
19 TokenStream as TokenStream2,
20};
21use quote::{
22 quote,
23 quote_spanned,
24};
25
26use super::TraitDefinition;
27use crate::{
28 generator,
29 generator::sol,
30 traits::GenerateCode,
31};
32
33impl TraitDefinition<'_> {
34 pub fn generate_message_builder(&self) -> TokenStream2 {
44 MessageBuilder::from(*self).generate_code()
45 }
46
47 pub fn message_builder_ident(&self) -> syn::Ident {
49 self.append_trait_suffix(MessageBuilder::SUFFIX)
50 }
51}
52
53#[derive(From)]
55struct MessageBuilder<'a> {
56 trait_def: TraitDefinition<'a>,
57}
58
59impl GenerateCode for MessageBuilder<'_> {
60 fn generate_code(&self) -> TokenStream2 {
61 let struct_definition = self.generate_struct_definition();
62 let auxiliary_trait_impls = self.generate_auxiliary_trait_impls();
63 let ink_trait_impl = self.generate_ink_trait_impl();
64 quote! {
65 #struct_definition
66 #auxiliary_trait_impls
67 #ink_trait_impl
68 }
69 }
70}
71
72impl MessageBuilder<'_> {
73 const SUFFIX: &'static str = "TraitMessageBuilder";
75
76 fn span(&self) -> Span {
78 self.trait_def.span()
79 }
80
81 fn generate_struct_definition(&self) -> TokenStream2 {
87 let span = self.span();
88 let message_builder_ident = self.trait_def.message_builder_ident();
89 quote_spanned!(span =>
90 #[doc(hidden)]
94 #[allow(non_camel_case_types)]
95 #[::ink::scale_derive(Encode, Decode, TypeInfo)]
96 pub struct #message_builder_ident<E, Abi> {
97 _marker: ::core::marker::PhantomData<fn() -> (E, Abi)>,
98 }
99 )
100 }
101
102 fn generate_auxiliary_trait_impls(&self) -> TokenStream2 {
110 let span = self.span();
111 let message_builder_ident = self.trait_def.message_builder_ident();
112 let sol_codec = if cfg!(any(ink_abi = "sol", ink_abi = "all")) {
113 quote_spanned!(span=>
116 impl<E, Abi> ::ink::SolDecode for #message_builder_ident<E, Abi>
117 where
118 E: ::ink::env::Environment,
119 {
120 type SolType = ();
121
122 fn from_sol_type(_: Self::SolType) -> ::core::result::Result<Self, ::ink::sol::Error> {
123 Ok(Self {
124 _marker: ::core::marker::PhantomData,
125 })
126 }
127 }
128
129 impl<'a, E, Abi> ::ink::SolEncode<'a> for #message_builder_ident<E, Abi>
130 where
131 E: ::ink::env::Environment,
132 {
133 type SolType = ();
134
135 fn to_sol_type(&'a self) {}
136 }
137 )
138 } else {
139 quote!()
140 };
141 quote_spanned!(span=>
142 impl<E, Abi> ::core::default::Default for #message_builder_ident<E, Abi>
143 where
144 E: ::ink::env::Environment,
145 {
146 fn default() -> Self {
147 Self {
148 _marker: ::core::default::Default::default()
149 }
150 }
151 }
152
153 impl<E, Abi> ::ink::env::ContractEnv for #message_builder_ident<E, Abi>
154 where
155 E: ::ink::env::Environment,
156 {
157 type Env = E;
158 }
159
160 #sol_codec
161 )
162 }
163
164 fn generate_ink_trait_impl(&self) -> TokenStream2 {
172 let span = self.trait_def.span();
173 let trait_ident = self.trait_def.trait_def.item().ident();
174 let trait_info_ident = self.trait_def.trait_info_ident();
175 let message_builder_ident = self.trait_def.message_builder_ident();
176 generate_abi_impls!(@type |abi| {
177 let abi_ty = match abi {
178 Abi::Ink => quote!(::ink::abi::Ink),
179 Abi::Sol => quote!(::ink::abi::Sol),
180 };
181 let message_impls = self.generate_ink_trait_impl_messages(abi);
182 quote_spanned!(span=>
183 impl<E> #trait_ident for #message_builder_ident<E, #abi_ty>
184 where
185 E: ::ink::env::Environment,
186 {
187 #[allow(non_camel_case_types)]
188 type __ink_TraitInfo = #trait_info_ident<E>;
189
190 #message_impls
191 }
192 )
193 })
194 }
195
196 fn generate_ink_trait_impl_messages(&self, abi: Abi) -> TokenStream2 {
199 let messages = self.trait_def.trait_def.item().iter_items().filter_map(
200 |(item, selector)| {
201 item.filter_map_message().map(|message| {
202 let (selector_bytes, abi_ty) = match abi {
203 Abi::Ink => {
204 let selector_bytes = selector.hex_lits();
205 (quote!([ #( #selector_bytes ),* ]), quote!(::ink::abi::Ink))
206 }
207 Abi::Sol => {
208 let message_ident = message.ident();
209 let signature = sol::utils::call_signature(
210 message_ident.to_string(),
211 message.inputs(),
212 );
213 (
214 quote!(::ink::codegen::sol::selector_bytes(#signature)),
215 quote!(::ink::abi::Sol),
216 )
217 }
218 };
219 self.generate_ink_trait_impl_for_message(
220 &message,
221 selector_bytes,
222 abi_ty,
223 )
224 })
225 },
226 );
227 quote! {
228 #( #messages )*
229 }
230 }
231
232 fn generate_ink_trait_impl_for_message(
235 &self,
236 message: &ir::InkTraitMessage,
237 selector_bytes: TokenStream2,
238 abi: TokenStream2,
239 ) -> TokenStream2 {
240 let span = message.span();
241 let message_ident = message.ident();
242 let attrs = self
243 .trait_def
244 .trait_def
245 .config()
246 .whitelisted_attributes()
247 .filter_attr(message.attrs());
248 let output_ident = generator::output_ident(message_ident);
249 let output = message.output();
250 let output_type =
251 output.map_or_else(|| quote! { () }, |output| quote! { #output });
252 let input_bindings = generator::input_bindings(message.inputs());
253 let input_types = generator::input_types(message.inputs());
254 let mut_tok = message.mutates().then(|| quote! { mut });
255 let arg_list =
256 generator::generate_argument_list(input_types.iter().cloned(), abi.clone());
257 let cfg_attrs = message.get_cfg_attrs(span);
258 quote_spanned!(span =>
259 #( #cfg_attrs )*
260 type #output_ident = ::ink::env::call::Execution<
261 #arg_list,
262 #output_type,
263 #abi
264 >;
265
266 #( #attrs )*
267 #[inline]
268 fn #message_ident(
269 & #mut_tok self
270 #( , #input_bindings : #input_types )*
271 ) -> Self::#output_ident {
272 ::ink::env::call::Execution::new(
273 ::ink::env::call::ExecutionInput::new(
274 ::ink::env::call::Selector::new(#selector_bytes)
275 )
276 #(
277 .push_arg(#input_bindings)
278 )*
279 )
280 }
281 )
282 }
283}