1use crate::contract::common::{derive_ident_metadata, extract_ident_from_type};
2use ident_case::RenameRule;
3use proc_macro2::{Ident, Literal, Span, TokenStream};
4use quote::{format_ident, quote, quote_spanned};
5use syn::spanned::Spanned;
6use syn::{
7 Attribute, Error, GenericArgument, Meta, Pat, PatType, PathArguments, ReturnType, Signature,
8 Token, Type, TypeTuple,
9};
10
11#[derive(Copy, Clone)]
12pub(super) enum MethodType {
13 Init,
14 Update,
15 View,
16}
17
18impl MethodType {
19 fn attr_str(self) -> &'static str {
20 match self {
21 MethodType::Init => "init",
22 MethodType::Update => "update",
23 MethodType::View => "view",
24 }
25 }
26}
27
28#[derive(Clone)]
29struct Env {
30 arg_name: Ident,
31 mutability: Option<Token![mut]>,
32}
33
34#[derive(Clone)]
35struct Tmp {
36 type_name: Type,
37 arg_name: Ident,
38 mutability: Option<Token![mut]>,
39}
40
41#[derive(Clone)]
42struct Slot {
43 with_address_arg: bool,
44 type_name: Type,
45 arg_name: Ident,
46 mutability: Option<Token![mut]>,
47}
48
49#[derive(Clone)]
50struct Input {
51 type_name: Type,
52 arg_name: Ident,
53}
54
55#[derive(Clone)]
56struct Output {
57 type_name: Type,
58 arg_name: Ident,
59 has_self: bool,
60}
61
62enum MethodReturnType {
63 Unit(Type),
65 Regular(Type),
67 ResultUnit(Type),
69 Result(Type),
71}
72
73impl MethodReturnType {
74 fn unit_type() -> Type {
75 Type::Tuple(TypeTuple {
76 paren_token: Default::default(),
77 elems: Default::default(),
78 })
79 }
80
81 fn unit_return_type(&self) -> bool {
82 match self {
83 Self::Unit(_) | Self::ResultUnit(_) => true,
84 Self::Regular(_) | Self::Result(_) => false,
85 }
86 }
87
88 fn return_type(&self) -> &Type {
89 match self {
90 Self::Unit(ty) | Self::Regular(ty) | Self::ResultUnit(ty) | Self::Result(ty) => ty,
91 }
92 }
93}
94
95#[derive(Default)]
96pub(super) struct ExtTraitComponents {
97 pub(super) definition: TokenStream,
98 pub(super) r#impl: TokenStream,
99}
100
101pub(super) struct MethodDetails {
102 method_type: MethodType,
103 self_type: Type,
104 state: Option<Option<Token![mut]>>,
105 env: Option<Env>,
106 tmp: Option<Tmp>,
107 slots: Vec<Slot>,
108 inputs: Vec<Input>,
109 outputs: Vec<Output>,
110 return_type: MethodReturnType,
111}
112
113impl MethodDetails {
114 pub(super) fn new(method_type: MethodType, self_type: Type) -> Self {
115 Self {
116 method_type,
117 self_type,
118 state: None,
119 env: None,
120 tmp: None,
121 slots: Vec::new(),
122 inputs: Vec::new(),
123 outputs: Vec::new(),
124 return_type: MethodReturnType::Unit(MethodReturnType::unit_type()),
125 }
126 }
127
128 pub(super) fn tmp_type<'a, I>(iter: I) -> Option<Type>
130 where
131 I: Iterator<Item = &'a Self> + 'a,
132 {
133 let mut tmp_type = None;
134 for slot in iter.flat_map(|method_details| &method_details.tmp) {
135 match &tmp_type {
136 Some(tmp_type) => {
137 if tmp_type != &slot.type_name {
138 return None;
139 }
140 }
141 None => {
142 tmp_type.replace(slot.type_name.clone());
143 }
144 }
145 }
146
147 Some(tmp_type.unwrap_or_else(MethodReturnType::unit_type))
148 }
149
150 pub(super) fn slot_type<'a, I>(iter: I) -> Option<Type>
152 where
153 I: Iterator<Item = &'a Self> + 'a,
154 {
155 let mut slot_type = None;
156 for slot in iter.flat_map(|method_details| &method_details.slots) {
157 match &slot_type {
158 Some(slot_type) => {
159 if slot_type != &slot.type_name {
160 return None;
161 }
162 }
163 None => {
164 slot_type.replace(slot.type_name.clone());
165 }
166 }
167 }
168
169 Some(slot_type.unwrap_or_else(MethodReturnType::unit_type))
170 }
171
172 pub(super) fn process_env_arg_ro(
173 &mut self,
174 input_span: Span,
175 pat_type: &PatType,
176 ) -> Result<(), Error> {
177 self.process_env_arg(input_span, pat_type, false)
178 }
179
180 pub(super) fn process_env_arg_rw(
181 &mut self,
182 input_span: Span,
183 pat_type: &PatType,
184 ) -> Result<(), Error> {
185 self.process_env_arg(input_span, pat_type, true)
186 }
187
188 fn process_env_arg(
189 &mut self,
190 input_span: Span,
191 pat_type: &PatType,
192 allow_mut: bool,
193 ) -> Result<(), Error> {
194 if self.env.is_some()
195 || self.tmp.is_some()
196 || !(self.inputs.is_empty() && self.outputs.is_empty())
197 {
198 return Err(Error::new(
199 input_span,
200 "`#[env]` must be the first non-Self argument and only appear once",
201 ));
202 }
203
204 if let Type::Reference(type_reference) = &*pat_type.ty
205 && let Type::Path(_type_path) = &*type_reference.elem
206 && let Pat::Ident(pat_ident) = &*pat_type.pat
207 {
208 if type_reference.mutability.is_some() && !allow_mut {
209 return Err(Error::new(
210 input_span,
211 "`#[env]` is not allowed to mutate data here",
212 ));
213 }
214
215 self.env.replace(Env {
216 arg_name: pat_ident.ident.clone(),
217 mutability: type_reference.mutability,
218 });
219 Ok(())
220 } else {
221 Err(Error::new(
222 pat_type.span(),
223 "`#[env]` must be a reference to `Env` type (can be shared or exclusive)",
224 ))
225 }
226 }
227
228 pub(super) fn process_state_arg_ro(
229 &mut self,
230 input_span: Span,
231 ty: &Type,
232 ) -> Result<(), Error> {
233 self.process_state_arg(input_span, ty, false)
234 }
235
236 pub(super) fn process_state_arg_rw(
237 &mut self,
238 input_span: Span,
239 ty: &Type,
240 ) -> Result<(), Error> {
241 self.process_state_arg(input_span, ty, true)
242 }
243
244 fn process_state_arg(
245 &mut self,
246 input_span: Span,
247 ty: &Type,
248 allow_mut: bool,
249 ) -> Result<(), Error> {
250 if let Type::Reference(type_reference) = ty
252 && let Type::Path(type_path) = &*type_reference.elem
253 && type_path.path.is_ident("Self")
254 {
255 if type_reference.mutability.is_some() && !allow_mut {
256 return Err(Error::new(
257 input_span,
258 "`#[arg]` is not allowed to mutate data here",
259 ));
260 }
261
262 self.state.replace(type_reference.mutability);
263 Ok(())
264 } else {
265 Err(Error::new(
266 ty.span(),
267 "Can't consume `Self`, use `&self` or `&mut self` instead",
268 ))
269 }
270 }
271
272 pub(super) fn process_tmp_arg(
273 &mut self,
274 input_span: Span,
275 pat_type: &PatType,
276 ) -> Result<(), Error> {
277 if self.tmp.is_some() || !(self.inputs.is_empty() && self.outputs.is_empty()) {
278 return Err(Error::new(
279 input_span,
280 "`#[tmp]` must appear only once before any `#[input]` or `#[output]`",
281 ));
282 }
283
284 if let Type::Reference(type_reference) = &*pat_type.ty {
286 let Some(arg_name) = extract_arg_name(&pat_type.pat) else {
287 return Err(Error::new(
288 pat_type.span(),
289 "`#[tmp]` argument name must be either a simple variable or a reference",
290 ));
291 };
292
293 self.tmp.replace(Tmp {
294 type_name: type_reference.elem.as_ref().clone(),
295 arg_name,
296 mutability: type_reference.mutability,
297 });
298
299 return Ok(());
300 }
301
302 Err(Error::new(
303 pat_type.span(),
304 "`#[tmp]` must be a reference to a type implementing `IoTypeOptional` (can be \
305 shared or exclusive) like `&MaybeData<Slot>` or `&mut VariableBytes<1024>`",
306 ))
307 }
308
309 pub(super) fn process_slot_arg_ro(
310 &mut self,
311 input_span: Span,
312 pat_type: &PatType,
313 ) -> Result<(), Error> {
314 self.process_slot_arg(input_span, pat_type, false)
315 }
316
317 pub(super) fn process_slot_arg_rw(
318 &mut self,
319 input_span: Span,
320 pat_type: &PatType,
321 ) -> Result<(), Error> {
322 self.process_slot_arg(input_span, pat_type, true)
323 }
324
325 fn process_slot_arg(
326 &mut self,
327 input_span: Span,
328 pat_type: &PatType,
329 allow_mut: bool,
330 ) -> Result<(), Error> {
331 if !(self.inputs.is_empty() && self.outputs.is_empty()) {
332 return Err(Error::new(
333 input_span,
334 "`#[slot]` must appear before any `#[input]` or `#[output]`",
335 ));
336 }
337
338 match &*pat_type.ty {
339 Type::Reference(type_reference) => {
341 if type_reference.mutability.is_some() && !allow_mut {
342 return Err(Error::new(
343 input_span,
344 "`#[slot]` is not allowed to mutate data here",
345 ));
346 }
347
348 let Some(arg_name) = extract_arg_name(&pat_type.pat) else {
349 return Err(Error::new(
350 pat_type.span(),
351 "`#[slot]` argument name must be either a simple variable or a reference",
352 ));
353 };
354
355 self.slots.push(Slot {
356 with_address_arg: false,
357 type_name: type_reference.elem.as_ref().clone(),
358 arg_name,
359 mutability: type_reference.mutability,
360 });
361 return Ok(());
362 }
363 Type::Tuple(type_tuple) => {
365 if type_tuple.elems.len() == 2
366 && let Type::Reference(address_type) =
367 type_tuple.elems.first().expect("Checked above; qed")
368 && address_type.mutability.is_none()
369 && let Type::Reference(outer_slot_type) =
370 type_tuple.elems.last().expect("Checked above; qed")
371 && let Pat::Tuple(pat_tuple) = &*pat_type.pat
372 && pat_tuple.elems.len() == 2
373 && let Some(slot_arg) = extract_arg_name(&pat_tuple.elems[1])
374 {
375 if outer_slot_type.mutability.is_some() && !allow_mut {
376 return Err(Error::new(
377 input_span,
378 "`#[slot]` is not allowed to mutate data here",
379 ));
380 }
381
382 self.slots.push(Slot {
383 with_address_arg: true,
384 type_name: outer_slot_type.elem.as_ref().clone(),
385 arg_name: slot_arg,
386 mutability: outer_slot_type.mutability,
387 });
388 return Ok(());
389 }
390
391 return Err(Error::new(
392 pat_type.span(),
393 "`#[slot]` with address must be a tuple of arguments, each of which is \
394 either a simple variable or a reference",
395 ));
396 }
397 _ => {
398 }
400 }
401
402 Err(Error::new(
403 pat_type.span(),
404 "`#[slot]` must be a reference to a type implementing `IoTypeOptional` (can be \
405 shared or exclusive) like `&MaybeData<Slot>` or a tuple of references to address and \
406 to slot type like `(&Address, &mut VariableBytes<1024>)`",
407 ))
408 }
409
410 pub(super) fn process_input_arg(
411 &mut self,
412 input_span: Span,
413 pat_type: &PatType,
414 ) -> Result<(), Error> {
415 if !self.outputs.is_empty() {
416 return Err(Error::new(
417 input_span,
418 "`#[input]` must appear before any `#[output]`",
419 ));
420 }
421
422 if let Type::Reference(type_reference) = &*pat_type.ty {
424 let Some(arg_name) = extract_arg_name(&pat_type.pat) else {
425 return Err(Error::new(
426 pat_type.span(),
427 "`#[input]` argument name must be either a simple variable or a reference",
428 ));
429 };
430 if type_reference.mutability.is_some() {
431 return Err(Error::new(
432 input_span,
433 "`#[input]` must be a shared reference",
434 ));
435 }
436
437 self.inputs.push(Input {
438 type_name: type_reference.elem.as_ref().clone(),
439 arg_name,
440 });
441
442 Ok(())
443 } else {
444 Err(Error::new(
445 pat_type.span(),
446 "`#[input]` must be a shared reference to a type",
447 ))
448 }
449 }
450
451 pub(super) fn process_output_arg(
452 &mut self,
453 _input_span: Span,
454 pat_type: &PatType,
455 ) -> Result<(), Error> {
456 if let Type::Reference(type_reference) = &*pat_type.ty
458 && type_reference.mutability.is_some()
459 {
460 let Pat::Ident(pat_ident) = &*pat_type.pat else {
461 return Err(Error::new(
462 pat_type.span(),
463 "`#[output]` argument name must be an exclusive reference",
464 ));
465 };
466
467 let mut type_name = type_reference.elem.as_ref().clone();
468 let mut has_self = false;
469
470 if let Type::Path(type_path) = &mut type_name
472 && let Some(path_segment) = type_path.path.segments.first_mut()
473 && let PathArguments::AngleBracketed(generic_arguments) =
474 &mut path_segment.arguments
475 && let Some(GenericArgument::Type(first_generic_argument)) =
476 generic_arguments.args.first_mut()
477 && let Type::Path(type_path) = &first_generic_argument
478 && type_path.path.is_ident("Self")
479 {
480 *first_generic_argument = self.self_type.clone();
481 has_self = true;
482 }
483
484 self.outputs.push(Output {
485 type_name,
486 arg_name: pat_ident.ident.clone(),
487 has_self,
488 });
489 Ok(())
490 } else {
491 Err(Error::new(
492 pat_type.span(),
493 "`#[output]` must be an exclusive reference to a type implementing \
494 `IoTypeOptional`, likely `MaybeData` container",
495 ))
496 }
497 }
498
499 pub(super) fn process_return(&mut self, output: &ReturnType) -> Result<(), Error> {
500 let error_message = format!(
502 "`#[{}]` must return `()` or `T` or `Result<T, ContractError>",
503 self.method_type.attr_str()
504 );
505 match output {
506 ReturnType::Default => {
507 self.set_return_type(MethodReturnType::Unit(MethodReturnType::unit_type()));
508 }
509 ReturnType::Type(_r_arrow, return_type) => match return_type.as_ref() {
510 Type::Array(_type_array) => {
511 self.set_return_type(MethodReturnType::Regular(return_type.as_ref().clone()));
512 }
513 Type::Path(type_path) => {
514 let Some(last_path_segment) = type_path.path.segments.last() else {
516 self.set_return_type(MethodReturnType::Regular(
517 return_type.as_ref().clone(),
518 ));
519 return Ok(());
520 };
521
522 if last_path_segment.ident == "Result" {
524 if let PathArguments::AngleBracketed(result_arguments) =
525 &last_path_segment.arguments
526 && result_arguments.args.len() == 2
527 && let GenericArgument::Type(ok_type) = &result_arguments.args[0]
528 && let GenericArgument::Type(error_type) = &result_arguments.args[1]
529 && let Type::Path(error_path) = error_type
530 && error_path
531 .path
532 .segments
533 .last()
534 .is_some_and(|s| s.ident == "ContractError")
535 {
536 if let Type::Path(ok_path) = ok_type
537 && ok_path
538 .path
539 .segments
540 .first()
541 .is_some_and(|s| s.ident == "Self")
542 {
543 self.set_return_type(MethodReturnType::Result(
545 self.self_type.clone(),
546 ));
547 } else {
548 self.set_return_type(MethodReturnType::Result(ok_type.clone()));
549 }
550 } else {
551 return Err(Error::new(return_type.span(), error_message));
552 }
553 } else if last_path_segment.ident == "Self" {
554 self.set_return_type(MethodReturnType::Regular(self.self_type.clone()));
556 } else {
557 self.set_return_type(MethodReturnType::Regular(
558 return_type.as_ref().clone(),
559 ));
560 }
561 }
562 return_type => {
563 return Err(Error::new(return_type.span(), error_message));
564 }
565 },
566 }
567
568 Ok(())
569 }
570
571 fn set_return_type(&mut self, return_type: MethodReturnType) {
572 let unit_type = MethodReturnType::unit_type();
573 self.return_type = match return_type {
574 MethodReturnType::Unit(ty) => MethodReturnType::Unit(ty),
575 MethodReturnType::Regular(ty) => {
576 if ty == unit_type {
577 MethodReturnType::Unit(ty)
578 } else {
579 MethodReturnType::Regular(ty)
580 }
581 }
582 MethodReturnType::ResultUnit(ty) => MethodReturnType::ResultUnit(ty),
583 MethodReturnType::Result(ty) => {
584 if ty == unit_type {
585 MethodReturnType::ResultUnit(ty)
586 } else {
587 MethodReturnType::Result(ty)
588 }
589 }
590 };
591 }
592
593 pub(super) fn generate_guest_ffi(
594 &self,
595 fn_sig: &Signature,
596 trait_name: Option<&Ident>,
597 ) -> Result<TokenStream, Error> {
598 let self_type = &self.self_type;
599 if matches!(self.method_type, MethodType::Init) {
600 let self_return_type = self.return_type.return_type() == self_type;
601 let self_last_output_type = self.outputs.last().is_some_and(|output| output.has_self);
602
603 if !(self_return_type || self_last_output_type) {
604 return Err(Error::new(
605 fn_sig.span(),
606 "`#[init]` must have `Self` as either return type or last `#[output]` \
607 argument",
608 ));
609 }
610 }
611
612 let original_method_name = &fn_sig.ident;
613
614 let guest_fn = self.generate_guest_fn(fn_sig, trait_name)?;
615 let external_args_struct = self.generate_external_args_struct(fn_sig, trait_name)?;
616 let metadata = self.generate_metadata(fn_sig, trait_name)?;
617
618 Ok(quote_spanned! {fn_sig.span() =>
619 pub mod #original_method_name {
620 use super::*;
621
622 #guest_fn
623 #external_args_struct
624 #metadata
625 }
626 })
627 }
628
629 pub(super) fn generate_guest_trait_ffi(
630 &self,
631 fn_sig: &Signature,
632 trait_name: Option<&Ident>,
633 ) -> Result<TokenStream, Error> {
634 let original_method_name = &fn_sig.ident;
635
636 let external_args_struct = self.generate_external_args_struct(fn_sig, trait_name)?;
637 let metadata = self.generate_metadata(fn_sig, trait_name)?;
638
639 Ok(quote_spanned! {fn_sig.span() =>
640 pub mod #original_method_name {
641 use super::*;
642
643 #external_args_struct
644 #metadata
645 }
646 })
647 }
648
649 pub(super) fn generate_guest_fn(
650 &self,
651 fn_sig: &Signature,
652 trait_name: Option<&Ident>,
653 ) -> Result<TokenStream, Error> {
654 let self_type = &self.self_type;
655
656 let mut internal_args_pointers = Vec::new();
658 let mut preparation = Vec::new();
660 let mut original_fn_args = Vec::new();
662
663 if let Some(mutability) = self.state {
665 internal_args_pointers.push(quote! {
666 pub self_ptr: ::core::ptr::NonNull<
667 <#self_type as ::ab_contracts_macros::__private::IoType>::PointerType,
668 >,
669 pub self_size: ::core::primitive::u32,
671 pub self_capacity: ::core::primitive::u32,
673 });
674
675 if mutability.is_some() {
676 original_fn_args.push(quote! {&mut *{
677 const {
680 const fn assert_impl_io_type<T>()
681 where
682 T: ::ab_contracts_macros::__private::IoType,
683 {}
684 assert_impl_io_type::<#self_type>();
685 }
686
687 <#self_type as ::ab_contracts_macros::__private::IoType>::from_mut_ptr(
688 &mut args.self_ptr,
689 &mut args.self_size,
690 args.self_capacity,
691 )
692 }});
693 } else {
694 original_fn_args.push(quote! {&*{
695 const {
698 const fn assert_impl_io_type<T>()
699 where
700 T: ::ab_contracts_macros::__private::IoType,
701 {}
702 assert_impl_io_type::<#self_type>();
703 }
704
705 <#self_type as ::ab_contracts_macros::__private::IoType>::from_ptr(
706 &args.self_ptr,
707 &args.self_size,
708 args.self_capacity,
709 )
710 }});
711 }
712 }
713
714 if let Some(env) = &self.env {
716 let env_field = &env.arg_name;
717 let mutability = env.mutability;
718
719 internal_args_pointers.push(quote! {
720 pub #env_field: &'internal_args #mutability ::ab_contracts_macros::__private::Env<'internal_args>,
722 });
723
724 original_fn_args.push(quote! { args.#env_field });
725 }
726
727 if let Some(tmp) = &self.tmp {
729 let type_name = &tmp.type_name;
730 let mutability = tmp.mutability;
731 let ptr_field = format_ident!("{}_ptr", tmp.arg_name);
732 let size_field = format_ident!("{}_size", tmp.arg_name);
733 let size_doc = format!("Size of the contents `{ptr_field}` points to");
734 let capacity_field = format_ident!("{}_capacity", tmp.arg_name);
735 let capacity_doc = format!("Capacity of the allocated memory `{ptr_field}` points to");
736
737 internal_args_pointers.push(quote! {
738 pub #ptr_field: ::core::ptr::NonNull<
739 <
740 <#self_type as ::ab_contracts_macros::__private::Contract>::Tmp as ::ab_contracts_macros::__private::IoType
742 >::PointerType,
743 >,
744 #[doc = #size_doc]
745 pub #size_field: ::core::primitive::u32,
746 #[doc = #capacity_doc]
747 pub #capacity_field: ::core::primitive::u32,
748 });
749
750 if mutability.is_some() {
751 original_fn_args.push(quote! {&mut *{
752 const {
756 const fn assert_impl_io_type_optional<T>()
757 where
758 T: ::ab_contracts_macros::__private::IoTypeOptional,
759 {}
760 assert_impl_io_type_optional::<#type_name>();
761 }
762
763 <#type_name as ::ab_contracts_macros::__private::IoType>::from_mut_ptr(
764 &mut args.#ptr_field,
765 &mut args.#size_field,
766 args.#capacity_field,
767 )
768 }});
769 } else {
770 original_fn_args.push(quote! {&*{
771 const {
775 const fn assert_impl_io_type_optional<T>()
776 where
777 T: ::ab_contracts_macros::__private::IoTypeOptional,
778 {}
779 assert_impl_io_type_optional::<#type_name>();
780 }
781
782 <#type_name as ::ab_contracts_macros::__private::IoType>::from_ptr(
783 &args.#ptr_field,
784 &args.#size_field,
785 args.#capacity_field,
786 )
787 }});
788 }
789 }
790
791 for slot in &self.slots {
795 let type_name = &slot.type_name;
796 let mutability = slot.mutability;
797 let address_field = format_ident!("{}_address", slot.arg_name);
798 let ptr_field = format_ident!("{}_ptr", slot.arg_name);
799 let size_field = format_ident!("{}_size", slot.arg_name);
800 let size_doc = format!("Size of the contents `{ptr_field}` points to");
801 let capacity_field = format_ident!("{}_capacity", slot.arg_name);
802 let capacity_doc = format!("Capacity of the allocated memory `{ptr_field}` points to");
803
804 internal_args_pointers.push(quote! {
805 pub #address_field: &'internal_args ::ab_contracts_macros::__private::Address,
808 pub #ptr_field: ::core::ptr::NonNull<
809 <
810 <#self_type as ::ab_contracts_macros::__private::Contract>::Slot as ::ab_contracts_macros::__private::IoType
812 >::PointerType,
813 >,
814 #[doc = #size_doc]
815 pub #size_field: ::core::primitive::u32,
816 #[doc = #capacity_doc]
817 pub #capacity_field: ::core::primitive::u32,
818 });
819
820 let arg_extraction = if mutability.is_some() {
821 quote! {&mut *{
822 const {
827 const fn assert_impl_io_type_optional<T>()
828 where
829 T: ::ab_contracts_macros::__private::IoTypeOptional,
830 {}
831 assert_impl_io_type_optional::<#type_name>();
832 }
833
834 <#type_name as ::ab_contracts_macros::__private::IoType>::from_mut_ptr(
835 &mut args.#ptr_field,
836 &mut args.#size_field,
837 args.#capacity_field,
838 )
839 }}
840 } else {
841 quote! {&*{
842 const {
847 const fn assert_impl_io_type_optional<T>()
848 where
849 T: ::ab_contracts_macros::__private::IoTypeOptional,
850 {}
851 assert_impl_io_type_optional::<#type_name>();
852 }
853
854 <#type_name as ::ab_contracts_macros::__private::IoType>::from_ptr(
855 &args.#ptr_field,
856 &args.#size_field,
857 args.#capacity_field,
858 )
859 }}
860 };
861
862 if slot.with_address_arg {
863 original_fn_args.push(quote! {
864 (
865 args.#address_field,
866 #arg_extraction,
867 )
868 });
869 } else {
870 original_fn_args.push(arg_extraction);
871 }
872 }
873
874 for input in &self.inputs {
876 let type_name = &input.type_name;
877 let arg_name = &input.arg_name;
878 let ptr_field = format_ident!("{arg_name}_ptr");
879 let size_field = format_ident!("{arg_name}_size");
880 let size_doc = format!("Size of the contents `{ptr_field}` points to");
881 let capacity_field = format_ident!("{arg_name}_capacity");
882 let capacity_doc = format!("Capacity of the allocated memory `{ptr_field}` points to");
883
884 internal_args_pointers.push(quote! {
885 pub #ptr_field: ::core::ptr::NonNull<
886 <#type_name as ::ab_contracts_macros::__private::IoType>::PointerType,
887 >,
888 #[doc = #size_doc]
889 pub #size_field: ::core::primitive::u32,
890 #[doc = #capacity_doc]
891 pub #capacity_field: ::core::primitive::u32,
892 });
893
894 original_fn_args.push(quote! {&*{
895 const {
898 const fn assert_impl_io_type<T>()
899 where
900 T: ::ab_contracts_macros::__private::IoType,
901 {}
902 assert_impl_io_type::<#type_name>();
903 }
904
905 <#type_name as ::ab_contracts_macros::__private::IoType>::from_ptr(
906 &args.#ptr_field,
907 &args.#size_field,
908 args.#capacity_field,
909 )
910 }});
911 }
912
913 for output in &self.outputs {
915 let type_name = &output.type_name;
916 let arg_name = &output.arg_name;
917 let ptr_field = format_ident!("{arg_name}_ptr");
918 let size_field = format_ident!("{arg_name}_size");
919 let size_doc = format!("Size of the contents `{ptr_field}` points to");
920 let capacity_field = format_ident!("{arg_name}_capacity");
921 let capacity_doc = format!("Capacity of the allocated memory `{ptr_field}` points to");
922
923 internal_args_pointers.push(quote! {
924 pub #ptr_field: ::core::ptr::NonNull<
925 <#type_name as ::ab_contracts_macros::__private::IoType>::PointerType,
926 >,
927 #[doc = #size_doc]
928 pub #size_field: ::core::primitive::u32,
929 #[doc = #capacity_doc]
930 pub #capacity_field: ::core::primitive::u32,
931 });
932
933 original_fn_args.push(quote! {&mut *{
934 const {
937 const fn assert_impl_io_type<T>()
938 where
939 T: ::ab_contracts_macros::__private::IoType,
940 {}
941 assert_impl_io_type::<#type_name>();
942 }
943
944 <#type_name as ::ab_contracts_macros::__private::IoType>::from_mut_ptr(
945 &mut args.#ptr_field,
946 &mut args.#size_field,
947 args.#capacity_field,
948 )
949 }});
950 }
951
952 let original_method_name = &fn_sig.ident;
953 let ffi_fn_name = derive_ffi_fn_name(self_type, trait_name, original_method_name)?;
954 let return_type = self.return_type.return_type();
955
956 let internal_args_struct = {
957 if !self.return_type.unit_return_type() {
959 internal_args_pointers.push(quote! {
960 pub ok_result: &'internal_args mut ::core::mem::MaybeUninit<#return_type>,
961 });
962
963 preparation.push(quote! {
964 const {
969 const fn assert_impl_trivial_type<T>()
970 where
971 T: ::ab_contracts_macros::__private::IoType,
972 {}
973 assert_impl_trivial_type::<#return_type>();
974 }
975 });
976 }
977 let args_struct_doc = format!(
978 "Data structure containing expected input to [`{ffi_fn_name}()`], it is used \
979 internally by the contract, there should be no need to construct it explicitly \
980 except maybe in contract's own tests"
981 );
982 quote_spanned! {fn_sig.span() =>
983 #[doc = #args_struct_doc]
984 #[derive(::core::fmt::Debug)]
985 #[repr(C)]
986 pub struct InternalArgs<'internal_args>
987 {
988 #( #internal_args_pointers )*
989 _phantom: ::core::marker::PhantomData<&'internal_args ()>,
990 }
991 }
992 };
993
994 let result_var_name = format_ident!("result");
995 let guest_fn = {
996 let result_handling = match &self.return_type {
999 MethodReturnType::Unit(_) => {
1000 quote! {
1001 ::ab_contracts_macros::__private::ExitCode::ok()
1003 }
1004 }
1005 MethodReturnType::Regular(_) => {
1006 quote! {
1007 args.ok_result.write(#result_var_name);
1008 ::ab_contracts_macros::__private::ExitCode::ok()
1010 }
1011 }
1012 MethodReturnType::ResultUnit(_) => {
1013 quote! {
1014 match #result_var_name {
1016 Ok(()) => ::ab_contracts_macros::__private::ExitCode::ok(),
1017 Err(error) => error.exit_code(),
1018 }
1019 }
1020 }
1021 MethodReturnType::Result(_) => {
1022 quote! {
1023 match #result_var_name {
1025 Ok(result) => {
1026 args.ok_result.write(result);
1027 ::ab_contracts_macros::__private::ExitCode::ok()
1029 }
1030 Err(error) => error.exit_code(),
1031 }
1032 }
1033 }
1034 };
1035
1036 let full_struct_name = if let Some(trait_name) = trait_name {
1037 quote! { <#self_type as #trait_name> }
1038 } else {
1039 quote! { #self_type }
1040 };
1041
1042 quote_spanned! {fn_sig.span() =>
1044 #[cfg_attr(feature = "guest", unsafe(no_mangle))]
1053 #[allow(clippy::new_ret_no_self, reason = "Method was re-written for FFI purposes without `Self`")]
1054 #[allow(clippy::absurd_extreme_comparisons, reason = "Macro-generated code doesn't know the size upfront")]
1055 pub unsafe extern "C" fn #ffi_fn_name(
1056 args: &mut InternalArgs<'_>,
1057 ) -> ::ab_contracts_macros::__private::ExitCode {
1058 #( #preparation )*
1059
1060 #[allow(
1062 unused_variables,
1063 reason = "Sometimes result is `()`"
1064 )]
1065 #[allow(
1066 clippy::let_unit_value,
1067 reason = "Sometimes result is `()`"
1068 )]
1069 let #result_var_name = #full_struct_name::#original_method_name(
1070 #( #original_fn_args, )*
1071 );
1072
1073 #result_handling
1074 }
1075 }
1076 };
1077
1078 let fn_pointer_static = {
1079 let adapter_ffi_fn_name = format_ident!("{ffi_fn_name}_adapter");
1080 let args_struct_name =
1081 derive_external_args_struct_name(self_type, trait_name, original_method_name)?;
1082
1083 quote! {
1084 #[doc(hidden)]
1085 pub mod fn_pointer {
1086 use super::*;
1087
1088 unsafe extern "C" fn #adapter_ffi_fn_name(
1089 args_ptr: ::core::ptr::NonNull<::core::ffi::c_void>,
1090 ) -> ::ab_contracts_macros::__private::ExitCode {
1091 unsafe {
1094 let mut args = args_ptr.cast::<InternalArgs<'_>>();
1095 debug_assert!(args.is_aligned(), "`args` pointer is misaligned");
1096 let args = args.as_mut();
1097 #ffi_fn_name(args)
1098 }
1099 }
1100
1101 pub const METHOD_FN_POINTER: ::ab_contracts_macros::__private::NativeExecutorContactMethod =
1102 ::ab_contracts_macros::__private::NativeExecutorContactMethod {
1103 method_fingerprint: &<#args_struct_name<'_> as ::ab_contracts_macros::__private::ExternalArgs>::FINGERPRINT,
1104 method_metadata: METADATA,
1105 ffi_fn: #adapter_ffi_fn_name,
1106 };
1107 }
1108 }
1109 };
1110
1111 Ok(quote! {
1112 #internal_args_struct
1113 #guest_fn
1114 #fn_pointer_static
1115 })
1116 }
1117
1118 fn generate_external_args_struct(
1119 &self,
1120 fn_sig: &Signature,
1121 trait_name: Option<&Ident>,
1122 ) -> Result<TokenStream, Error> {
1123 let self_type = &self.self_type;
1124 let original_method_name = &fn_sig.ident;
1125
1126 let args_struct_name =
1127 derive_external_args_struct_name(self_type, trait_name, original_method_name)?;
1128 let mut external_args_fields = Vec::new();
1130 let mut method_args = Vec::new();
1132 let mut method_args_fields = Vec::new();
1134
1135 for slot in &self.slots {
1137 let arg_name = &slot.arg_name;
1138 let ptr_field = format_ident!("{arg_name}_ptr");
1139
1140 external_args_fields.push(quote! {
1141 pub #ptr_field: ::core::ptr::NonNull<::ab_contracts_macros::__private::Address>,
1142 });
1143
1144 method_args.push(quote! {
1145 #arg_name: &'external_args ::ab_contracts_macros::__private::Address,
1146 });
1147 method_args_fields.push(quote! {
1148 #ptr_field: ::core::ptr::NonNull::from_ref(#arg_name),
1149 });
1150 }
1151
1152 for input in &self.inputs {
1154 let type_name = &input.type_name;
1155 let arg_name = &input.arg_name;
1156 let ptr_field = format_ident!("{arg_name}_ptr");
1157 let size_field = format_ident!("{arg_name}_size");
1158 let size_doc = format!("Size of the contents `{ptr_field}` points to");
1159 let capacity_field = format_ident!("{arg_name}_capacity");
1160 let capacity_doc = format!("Capacity of the contents `{ptr_field}` points to");
1161
1162 external_args_fields.push(quote! {
1163 pub #ptr_field: ::core::ptr::NonNull<
1164 <#type_name as ::ab_contracts_macros::__private::IoType>::PointerType,
1165 >,
1166 #[doc = #size_doc]
1167 pub #size_field: ::core::primitive::u32,
1168 #[doc = #capacity_doc]
1169 pub #capacity_field: ::core::primitive::u32,
1170 });
1171
1172 method_args.push(quote! {
1173 #arg_name: &'external_args #type_name,
1174 });
1175 method_args_fields.push(quote! {
1176 #ptr_field: unsafe {
1180 *::ab_contracts_macros::__private::IoType::as_ptr(#arg_name)
1181 },
1182 #size_field: ::ab_contracts_macros::__private::IoType::size(#arg_name),
1183 #capacity_field: ::ab_contracts_macros::__private::IoType::capacity(#arg_name),
1184 });
1185 }
1186
1187 let mut outputs_iter = self.outputs.iter().peekable();
1189 while let Some(output) = outputs_iter.next() {
1190 let type_name = &output.type_name;
1191 let arg_name = &output.arg_name;
1192 let ptr_field = format_ident!("{arg_name}_ptr");
1193 let size_field = format_ident!("{arg_name}_size");
1194 let size_doc = format!("Size of the contents `{ptr_field}` points to");
1195 let capacity_field = format_ident!("{arg_name}_capacity");
1196 let capacity_doc = format!("Capacity of the allocated memory `{ptr_field}` points to");
1197
1198 if outputs_iter.is_empty()
1201 && self.return_type.unit_return_type()
1202 && matches!(self.method_type, MethodType::Init)
1203 {
1204 continue;
1205 }
1206
1207 external_args_fields.push(quote! {
1208 pub #ptr_field: ::core::ptr::NonNull<
1209 <#type_name as ::ab_contracts_macros::__private::IoType>::PointerType,
1210 >,
1211 #[doc = #size_doc]
1212 pub #size_field: ::core::primitive::u32,
1213 #[doc = #capacity_doc]
1214 pub #capacity_field: ::core::primitive::u32,
1215 });
1216
1217 method_args.push(quote! {
1218 #arg_name: &'external_args mut #type_name,
1219 });
1220 method_args_fields.push(quote! {
1221 #ptr_field: unsafe {
1225 *::ab_contracts_macros::__private::IoType::as_mut_ptr(#arg_name)
1226 },
1227 #size_field: ::ab_contracts_macros::__private::IoType::size(#arg_name),
1228 #capacity_field: ::ab_contracts_macros::__private::IoType::capacity(#arg_name),
1229 });
1230 }
1231
1232 let ffi_fn_name = derive_ffi_fn_name(self_type, trait_name, original_method_name)?;
1233
1234 if !(matches!(self.method_type, MethodType::Init) || self.return_type.unit_return_type()) {
1238 let return_type = &self.return_type.return_type();
1239
1240 external_args_fields.push(quote! {
1241 pub ok_result: &'external_args mut ::core::mem::MaybeUninit<#return_type>,
1242 });
1243
1244 method_args.push(quote! {
1245 ok_result: &'external_args mut ::core::mem::MaybeUninit<#return_type>,
1246 });
1247 method_args_fields.push(quote! {
1248 ok_result,
1249 });
1250 }
1251 let args_struct_doc = format!(
1252 "Data structure containing expected input for external method invocation, eventually \
1253 calling `{ffi_fn_name}()` on the other side by the host.\n\n\
1254 This can be used with [`Env`](::ab_contracts_macros::__private::Env), though there are \
1255 helper methods on this provided by extension trait that allow not dealing with this \
1256 struct directly in simpler cases."
1257 );
1258
1259 Ok(quote_spanned! {fn_sig.span() =>
1260 #[doc = #args_struct_doc]
1261 #[derive(::core::fmt::Debug)]
1262 #[repr(C)]
1263 pub struct #args_struct_name<'external_args> {
1264 #( #external_args_fields )*
1265 lifetime: ::core::marker::PhantomData<&'external_args ()>,
1266 }
1267
1268 #[automatically_derived]
1269 unsafe impl ::ab_contracts_macros::__private::ExternalArgs for #args_struct_name<'_> {
1270 const FINGERPRINT: ::ab_contracts_macros::__private::MethodFingerprint =
1271 ::ab_contracts_macros::__private::MethodFingerprint::new(METADATA)
1272 .expect("Metadata is statically correct; qed");
1273 const METADATA: &'static [::core::primitive::u8] = METADATA;
1274 }
1275
1276 impl<'external_args> #args_struct_name<'external_args> {
1277 #[allow(
1281 clippy::new_without_default,
1282 reason = "Do not want `Default` in auto-generated code"
1283 )]
1284 pub fn new(
1285 #( #method_args )*
1286 ) -> Self {
1287 Self {
1288 #( #method_args_fields )*
1289 lifetime: ::core::marker::PhantomData,
1290 }
1291 }
1292 }
1293 })
1294 }
1295
1296 fn generate_metadata(
1297 &self,
1298 fn_sig: &Signature,
1299 trait_name: Option<&Ident>,
1300 ) -> Result<TokenStream, Error> {
1301 let self_type = &self.self_type;
1302 let mut method_metadata = Vec::new();
1305
1306 if let Some(env) = &self.env {
1307 let env_metadata_type = if env.mutability.is_some() {
1308 "EnvRw"
1309 } else {
1310 "EnvRo"
1311 };
1312
1313 let env_metadata_type = format_ident!("{env_metadata_type}");
1314 method_metadata.push(quote! {
1315 &[::ab_contracts_macros::__private::ContractMetadataKind::#env_metadata_type as ::core::primitive::u8],
1316 });
1317 }
1318
1319 if let Some(tmp) = &self.tmp {
1320 let tmp_metadata_type = if tmp.mutability.is_some() {
1321 "TmpRw"
1322 } else {
1323 "TmpRo"
1324 };
1325
1326 let tmp_metadata_type = format_ident!("{tmp_metadata_type}");
1327 let arg_name_metadata = derive_ident_metadata(&tmp.arg_name)?;
1328 method_metadata.push(quote! {
1329 &[::ab_contracts_macros::__private::ContractMetadataKind::#tmp_metadata_type as ::core::primitive::u8],
1330 #arg_name_metadata,
1331 });
1332 }
1333
1334 for slot in &self.slots {
1335 let slot_metadata_type = if slot.mutability.is_some() {
1336 "SlotRw"
1337 } else {
1338 "SlotRo"
1339 };
1340
1341 let slot_metadata_type = format_ident!("{slot_metadata_type}");
1342 let arg_name_metadata = derive_ident_metadata(&slot.arg_name)?;
1343 method_metadata.push(quote! {
1344 &[::ab_contracts_macros::__private::ContractMetadataKind::#slot_metadata_type as ::core::primitive::u8],
1345 #arg_name_metadata,
1346 });
1347 }
1348
1349 for input in &self.inputs {
1350 let io_metadata_type = format_ident!("Input");
1351 let arg_name_metadata = derive_ident_metadata(&input.arg_name)?;
1352 let type_name = &input.type_name;
1353
1354 method_metadata.push(quote! {
1355 &[::ab_contracts_macros::__private::ContractMetadataKind::#io_metadata_type as ::core::primitive::u8],
1356 #arg_name_metadata,
1357 <#type_name as ::ab_contracts_macros::__private::IoType>::METADATA,
1358 });
1359 }
1360
1361 let mut outputs_iter = self.outputs.iter().peekable();
1362 while let Some(output) = outputs_iter.next() {
1363 let io_metadata_type = "Output";
1364
1365 let io_metadata_type = format_ident!("{io_metadata_type}");
1366 let arg_name_metadata = derive_ident_metadata(&output.arg_name)?;
1367 let with_type_metadata = if outputs_iter.is_empty()
1369 && self.return_type.unit_return_type()
1370 && matches!(self.method_type, MethodType::Init)
1371 {
1372 None
1373 } else {
1374 let type_name = &output.type_name;
1375 Some(quote! {
1376 <#type_name as ::ab_contracts_macros::__private::IoType>::METADATA,
1377 })
1378 };
1379 method_metadata.push(quote! {
1380 &[::ab_contracts_macros::__private::ContractMetadataKind::#io_metadata_type as ::core::primitive::u8],
1381 #arg_name_metadata,
1382 #with_type_metadata
1383 });
1384 }
1385
1386 if !self.return_type.unit_return_type() {
1388 let arg_name_metadata = Literal::u8_unsuffixed(0);
1390 let with_type_metadata = if matches!(self.method_type, MethodType::Init) {
1392 None
1393 } else {
1394 let return_type = self.return_type.return_type();
1395 Some(quote! {
1396 <#return_type as ::ab_contracts_macros::__private::IoType>::METADATA,
1397 })
1398 };
1399 method_metadata.push(quote! {
1400 &[
1401 ::ab_contracts_macros::__private::ContractMetadataKind::Return as ::core::primitive::u8,
1402 #arg_name_metadata,
1403 ],
1404 #with_type_metadata
1405 });
1406 }
1407
1408 let method_type = match self.method_type {
1409 MethodType::Init => "Init",
1410 MethodType::Update => {
1411 if let Some(mutable) = &self.state {
1412 if mutable.is_some() {
1413 "UpdateStatefulRw"
1414 } else {
1415 "UpdateStatefulRo"
1416 }
1417 } else {
1418 "UpdateStateless"
1419 }
1420 }
1421 MethodType::View => {
1422 if let Some(mutable) = &self.state {
1423 if mutable.is_some() {
1424 return Err(Error::new(
1425 fn_sig.span(),
1426 "Stateful view methods are not supported",
1427 ));
1428 }
1429
1430 "ViewStateful"
1431 } else {
1432 "ViewStateless"
1433 }
1434 }
1435 };
1436
1437 let method_type = format_ident!("{method_type}");
1438 let number_of_arguments = u8::try_from(method_metadata.len()).map_err(|_error| {
1439 Error::new(
1440 fn_sig.span(),
1441 format!("Number of arguments must not be more than {}", u8::MAX),
1442 )
1443 })?;
1444 let total_number_of_arguments =
1445 Literal::u8_unsuffixed(number_of_arguments.saturating_add(1));
1446 let number_of_arguments = Literal::u8_unsuffixed(number_of_arguments);
1447
1448 let original_method_name = &fn_sig.ident;
1449 let ffi_fn_name = derive_ffi_fn_name(self_type, trait_name, original_method_name)?;
1450 let method_name_metadata = derive_ident_metadata(&ffi_fn_name)?;
1451 Ok(quote_spanned! {fn_sig.span() =>
1452 #[expect(
1453 clippy::assertions_on_constants,
1454 reason = "Auto-generated compile-time check"
1455 )]
1456 const fn metadata()
1457 -> ([::core::primitive::u8; ::ab_contracts_macros::__private::MAX_METADATA_CAPACITY], usize)
1458 {
1459 assert!(
1460 #total_number_of_arguments <= ::ab_contracts_macros::__private::MAX_TOTAL_METHOD_ARGS,
1461 "Too many arguments"
1462 );
1463 ::ab_contracts_macros::__private::concat_metadata_sources(&[
1464 &[::ab_contracts_macros::__private::ContractMetadataKind::#method_type as ::core::primitive::u8],
1465 #method_name_metadata,
1466 &[#number_of_arguments],
1467 #( #method_metadata )*
1468 ])
1469 }
1470
1471 pub const METADATA: &[::core::primitive::u8] =
1476 metadata()
1477 .0
1478 .split_at(metadata().1)
1479 .0;
1480 })
1481 }
1482
1483 pub(super) fn generate_trait_ext_components(
1484 &self,
1485 fn_sig: &Signature,
1486 fn_attrs: &[Attribute],
1487 trait_name: Option<&Ident>,
1488 ) -> Result<ExtTraitComponents, Error> {
1489 let self_type = &self.self_type;
1490
1491 let mut preparation = Vec::new();
1492 let mut method_args = Vec::new();
1493 let mut external_args_args = Vec::new();
1494 let mut result_processing_before = Vec::new();
1495 let mut result_processing_after = Vec::new();
1496
1497 method_args.push(quote_spanned! {fn_sig.span() =>
1499 contract: ::ab_contracts_macros::__private::Address,
1500 });
1501
1502 for slot in &self.slots {
1504 let arg_name = &slot.arg_name;
1505
1506 method_args.push(quote_spanned! {fn_sig.span() =>
1507 #arg_name: &::ab_contracts_macros::__private::Address,
1508 });
1509 external_args_args.push(quote_spanned! {fn_sig.span() => #arg_name });
1510 }
1511
1512 for input in &self.inputs {
1514 let type_name = &input.type_name;
1515 let arg_name = &input.arg_name;
1516
1517 method_args.push(quote_spanned! {fn_sig.span() =>
1518 #arg_name: &#type_name,
1519 });
1520 external_args_args.push(quote_spanned! {fn_sig.span() => #arg_name });
1521 }
1522
1523 let mut outputs_iter = self.outputs.iter().peekable();
1525 while let Some(output) = outputs_iter.next() {
1526 let type_name = &output.type_name;
1527 let arg_name = &output.arg_name;
1528 let size_var = format_ident!("macro_{arg_name}_size");
1529 let size_field = format_ident!("{arg_name}_size");
1530
1531 if outputs_iter.is_empty()
1534 && self.return_type.unit_return_type()
1535 && matches!(self.method_type, MethodType::Init)
1536 {
1537 continue;
1538 }
1539
1540 method_args.push(quote_spanned! {fn_sig.span() =>
1541 #arg_name: &mut #type_name,
1542 });
1543 external_args_args.push(quote_spanned! {fn_sig.span() => #arg_name });
1544 result_processing_before.push(quote_spanned! {fn_sig.span() =>
1545 let #size_var = args.#size_field;
1546 });
1547 result_processing_after.push(quote_spanned! {fn_sig.span() =>
1548 unsafe {
1550 ::ab_contracts_macros::__private::IoType::set_size(#arg_name, #size_var);
1551 }
1552 });
1553 }
1554
1555 let original_method_name = &fn_sig.ident;
1556 let ext_method_name = derive_ffi_fn_name(self_type, trait_name, original_method_name)?;
1557 let env_self = if matches!(self.method_type, MethodType::View) {
1559 quote_spanned! {fn_sig.span() => &self }
1560 } else {
1561 quote_spanned! {fn_sig.span() => &mut self }
1562 };
1563 let method_context_arg = (!matches!(self.method_type, MethodType::View)).then(|| {
1565 quote_spanned! {fn_sig.span() =>
1566 method_context: ::ab_contracts_macros::__private::MethodContext,
1567 }
1568 });
1569 let method_signature = if matches!(self.method_type, MethodType::Init)
1573 || self.return_type.unit_return_type()
1574 {
1575 quote_spanned! {fn_sig.span() =>
1576 #[allow(dead_code, reason = "Macro-generated")]
1577 fn #ext_method_name(
1578 #env_self,
1579 #method_context_arg
1580 #( #method_args )*
1581 ) -> ::core::result::Result<(), ::ab_contracts_macros::__private::ContractError>
1582 }
1583 } else {
1584 let return_type = self.return_type.return_type();
1585
1586 preparation.push(quote_spanned! {fn_sig.span() =>
1587 const {
1592 const fn assert_impl_trivial_type<T>()
1593 where
1594 T: ::ab_contracts_macros::__private::IoType,
1595 {}
1596 assert_impl_trivial_type::<#return_type>();
1597 }
1598
1599 let mut ok_result = ::core::mem::MaybeUninit::uninit();
1600 });
1601 external_args_args.push(quote_spanned! {fn_sig.span() =>
1602 &mut ok_result
1603 });
1604 result_processing_after.push(quote_spanned! {fn_sig.span() =>
1605 unsafe {
1607 ok_result.assume_init()
1608 }
1609 });
1610
1611 quote_spanned! {fn_sig.span() =>
1612 #[allow(dead_code, reason = "Macro-generated")]
1613 fn #ext_method_name(
1614 #env_self,
1615 #method_context_arg
1616 #( #method_args )*
1617 ) -> ::core::result::Result<
1618 #return_type,
1619 ::ab_contracts_macros::__private::ContractError,
1620 >
1621 }
1622 };
1623
1624 let attrs = fn_attrs.iter().filter(|attr| {
1625 let path = match &attr.meta {
1626 Meta::Path(path) => path,
1627 Meta::List(list) => &list.path,
1628 Meta::NameValue(name_value) => &name_value.path,
1629 };
1630
1631 if let Some(ident) = path.get_ident() {
1632 ident == "doc" || ident == "allow" || ident == "expect"
1633 } else {
1634 false
1635 }
1636 });
1637 let definition = quote_spanned! {fn_sig.span() =>
1638 #[allow(
1639 clippy::too_many_arguments,
1640 reason = "Generated code may have more arguments that source code"
1641 )]
1642 #( #attrs )*
1643 #method_signature;
1644 };
1645
1646 let args_struct_name =
1647 derive_external_args_struct_name(self_type, trait_name, original_method_name)?;
1648 let method_context_value = if matches!(self.method_type, MethodType::View) {
1650 quote_spanned! {fn_sig.span() =>
1651 ::ab_contracts_macros::__private::MethodContext::Reset
1652 }
1653 } else {
1654 quote_spanned! {fn_sig.span() =>
1655 method_context
1656 }
1657 };
1658 let r#impl = quote_spanned! {fn_sig.span() =>
1659 #[inline]
1660 #method_signature {
1661 #( #preparation )*
1662
1663 let mut args = #original_method_name::#args_struct_name::new(
1664 #( #external_args_args, )*
1665 );
1666
1667 self.call(contract, &mut args, #method_context_value)?;
1668
1669 #( #result_processing_before )*
1670
1671 #[allow(
1672 clippy::let_unit_value,
1673 reason = "Sometimes there is no result to process and block is empty"
1674 )]
1675 let result = {
1676 #( #result_processing_after )*
1677 };
1678
1679 Ok(result)
1680 }
1681 };
1682
1683 Ok(ExtTraitComponents { definition, r#impl })
1684 }
1685}
1686
1687fn extract_arg_name(mut pat: &Pat) -> Option<Ident> {
1688 loop {
1689 match pat {
1690 Pat::Ident(pat_ident) => {
1691 return Some(pat_ident.ident.clone());
1692 }
1693 Pat::Reference(pat_reference) => {
1694 pat = &pat_reference.pat;
1695 }
1696 _ => {
1697 return None;
1698 }
1699 }
1700 }
1701}
1702
1703fn derive_ffi_fn_name(
1704 type_name: &Type,
1705 trait_name: Option<&Ident>,
1706 method_name: &Ident,
1707) -> Result<Ident, Error> {
1708 let type_name = extract_ident_from_type(type_name).ok_or_else(|| {
1709 Error::new(
1710 type_name.span(),
1711 "`#[contract]` must be applied to a simple struct without generics",
1712 )
1713 })?;
1714 let ffi_fn_prefix =
1715 RenameRule::SnakeCase.apply_to_variant(trait_name.unwrap_or(type_name).to_string());
1716
1717 Ok(format_ident!("{ffi_fn_prefix}_{method_name}"))
1718}
1719
1720fn derive_external_args_struct_name(
1721 type_name: &Type,
1722 trait_name: Option<&Ident>,
1723 method_name: &Ident,
1724) -> Result<Ident, Error> {
1725 let type_name = extract_ident_from_type(type_name).ok_or_else(|| {
1726 Error::new(
1727 type_name.span(),
1728 "`#[contract]` must be applied to a simple struct without generics",
1729 )
1730 })?;
1731 Ok(format_ident!(
1732 "{}{}Args",
1733 trait_name.unwrap_or(type_name),
1734 RenameRule::PascalCase.apply_to_field(method_name.to_string())
1735 ))
1736}