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 });
670
671 if mutability.is_some() {
672 internal_args_pointers.push(quote! {
673 pub self_size: *mut ::core::primitive::u32,
675 pub self_capacity: ::core::ptr::NonNull<::core::primitive::u32>,
677 });
678
679 original_fn_args.push(quote! {&mut *{
680 const _: () = {
683 const fn assert_impl_io_type<T>()
684 where
685 T: ::ab_contracts_macros::__private::IoType,
686 {}
687 assert_impl_io_type::<#self_type>();
688 };
689
690 <#self_type as ::ab_contracts_macros::__private::IoType>::from_mut_ptr(
691 &mut args.self_ptr,
692 &mut args.self_size,
693 args.self_capacity.read(),
694 )
695 }});
696 } else {
697 internal_args_pointers.push(quote! {
698 pub self_size: ::core::ptr::NonNull<::core::primitive::u32>,
700 });
701
702 original_fn_args.push(quote! {&*{
703 const _: () = {
706 const fn assert_impl_io_type<T>()
707 where
708 T: ::ab_contracts_macros::__private::IoType,
709 {}
710 assert_impl_io_type::<#self_type>();
711 };
712
713 <#self_type as ::ab_contracts_macros::__private::IoType>::from_ptr(
714 &args.self_ptr,
715 args.self_size.as_ref(),
716 args.self_size.read(),
718 )
719 }});
720 }
721 }
722
723 if let Some(env) = &self.env {
725 let ptr_field = format_ident!("{}_ptr", env.arg_name);
726 let assert_msg = format!("`{ptr_field}` pointer is misaligned");
727 let mutability = env.mutability;
728
729 internal_args_pointers.push(quote! {
730 pub #ptr_field: ::core::ptr::NonNull<::ab_contracts_macros::__private::Env<'internal_args>>,
732 });
733
734 if mutability.is_some() {
735 original_fn_args.push(quote! {{
736 debug_assert!(args.#ptr_field.is_aligned(), #assert_msg);
737 args.#ptr_field.as_mut()
738 }});
739 } else {
740 original_fn_args.push(quote! {{
741 debug_assert!(args.#ptr_field.is_aligned(), #assert_msg);
742 args.#ptr_field.as_ref()
743 }});
744 }
745 }
746
747 if let Some(tmp) = &self.tmp {
751 let type_name = &tmp.type_name;
752 let mutability = tmp.mutability;
753 let ptr_field = format_ident!("{}_ptr", tmp.arg_name);
754 let size_field = format_ident!("{}_size", tmp.arg_name);
755 let size_doc = format!("Size of the contents `{ptr_field}` points to");
756 let capacity_field = format_ident!("{}_capacity", tmp.arg_name);
757 let capacity_doc = format!("Capacity of the allocated memory `{ptr_field}` points to");
758
759 internal_args_pointers.push(quote! {
760 pub #ptr_field: ::core::ptr::NonNull<
761 <
762 <#self_type as ::ab_contracts_macros::__private::Contract>::Tmp as ::ab_contracts_macros::__private::IoType
764 >::PointerType,
765 >,
766 });
767
768 if mutability.is_some() {
769 internal_args_pointers.push(quote! {
770 #[doc = #size_doc]
771 pub #size_field: *mut ::core::primitive::u32,
772 #[doc = #capacity_doc]
773 pub #capacity_field: ::core::ptr::NonNull<::core::primitive::u32>,
774 });
775
776 original_fn_args.push(quote! {&mut *{
777 const _: () = {
781 const fn assert_impl_io_type_optional<T>()
782 where
783 T: ::ab_contracts_macros::__private::IoTypeOptional,
784 {}
785 assert_impl_io_type_optional::<#type_name>();
786 };
787
788 <#type_name as ::ab_contracts_macros::__private::IoType>::from_mut_ptr(
789 &mut args.#ptr_field,
790 &mut args.#size_field,
791 args.#capacity_field.read(),
792 )
793 }});
794 } else {
795 internal_args_pointers.push(quote! {
796 #[doc = #size_doc]
797 pub #size_field: ::core::ptr::NonNull<::core::primitive::u32>,
798 });
799
800 original_fn_args.push(quote! {&*{
801 const _: () = {
805 const fn assert_impl_io_type_optional<T>()
806 where
807 T: ::ab_contracts_macros::__private::IoTypeOptional,
808 {}
809 assert_impl_io_type_optional::<#type_name>();
810 };
811
812 <#type_name as ::ab_contracts_macros::__private::IoType>::from_ptr(
813 &args.#ptr_field,
814 args.#size_field.as_ref(),
815 args.#size_field.read(),
817 )
818 }});
819 }
820 }
821
822 for slot in &self.slots {
829 let type_name = &slot.type_name;
830 let mutability = slot.mutability;
831 let address_ptr_field = format_ident!("{}_address_ptr", slot.arg_name);
832 let ptr_field = format_ident!("{}_ptr", slot.arg_name);
833 let size_field = format_ident!("{}_size", slot.arg_name);
834 let size_doc = format!("Size of the contents `{ptr_field}` points to");
835 let capacity_field = format_ident!("{}_capacity", slot.arg_name);
836 let capacity_doc = format!("Capacity of the allocated memory `{ptr_field}` points to");
837
838 internal_args_pointers.push(quote! {
839 pub #address_ptr_field: ::core::ptr::NonNull<::ab_contracts_macros::__private::Address>,
841 pub #ptr_field: ::core::ptr::NonNull<
842 <
843 <#self_type as ::ab_contracts_macros::__private::Contract>::Slot as ::ab_contracts_macros::__private::IoType
845 >::PointerType,
846 >,
847 });
848
849 let arg_extraction = if mutability.is_some() {
850 internal_args_pointers.push(quote! {
851 #[doc = #size_doc]
852 pub #size_field: *mut ::core::primitive::u32,
853 #[doc = #capacity_doc]
854 pub #capacity_field: ::core::ptr::NonNull<::core::primitive::u32>,
855 });
856
857 quote! {&mut *{
858 const _: () = {
862 const fn assert_impl_io_type_optional<T>()
863 where
864 T: ::ab_contracts_macros::__private::IoTypeOptional,
865 {}
866 assert_impl_io_type_optional::<#type_name>();
867 };
868
869 <#type_name as ::ab_contracts_macros::__private::IoType>::from_mut_ptr(
870 &mut args.#ptr_field,
871 &mut args.#size_field,
872 args.#capacity_field.read(),
873 )
874 }}
875 } else {
876 internal_args_pointers.push(quote! {
877 #[doc = #size_doc]
878 pub #size_field: ::core::ptr::NonNull<::core::primitive::u32>,
879 });
880
881 quote! {&*{
882 const _: () = {
886 const fn assert_impl_io_type_optional<T>()
887 where
888 T: ::ab_contracts_macros::__private::IoTypeOptional,
889 {}
890 assert_impl_io_type_optional::<#type_name>();
891 };
892
893 <#type_name as ::ab_contracts_macros::__private::IoType>::from_ptr(
894 &args.#ptr_field,
895 args.#size_field.as_ref(),
896 args.#size_field.read(),
898 )
899 }}
900 };
901
902 if slot.with_address_arg {
903 original_fn_args.push(quote! {
904 (
905 &<::ab_contracts_macros::__private::Address as ::ab_contracts_macros::__private::IoType>::from_ptr(
906 &args.#address_ptr_field,
907 &<::ab_contracts_macros::__private::Address as ::ab_contracts_macros::__private::TrivialType>::SIZE,
908 <::ab_contracts_macros::__private::Address as ::ab_contracts_macros::__private::TrivialType>::SIZE,
909 ),
910 #arg_extraction,
911 )
912 });
913 } else {
914 original_fn_args.push(arg_extraction);
915 }
916 }
917
918 for input in &self.inputs {
921 let type_name = &input.type_name;
922 let arg_name = &input.arg_name;
923 let ptr_field = format_ident!("{arg_name}_ptr");
924 let size_field = format_ident!("{arg_name}_size");
925 let size_doc = format!("Size of the contents `{ptr_field}` points to");
926
927 internal_args_pointers.push(quote! {
928 pub #ptr_field: ::core::ptr::NonNull<
929 <#type_name as ::ab_contracts_macros::__private::IoType>::PointerType,
930 >,
931 #[doc = #size_doc]
932 pub #size_field: ::core::ptr::NonNull<::core::primitive::u32>,
933 });
934
935 original_fn_args.push(quote! {&*{
936 const _: () = {
939 const fn assert_impl_io_type<T>()
940 where
941 T: ::ab_contracts_macros::__private::IoType,
942 {}
943 assert_impl_io_type::<#type_name>();
944 };
945
946 <#type_name as ::ab_contracts_macros::__private::IoType>::from_ptr(
947 &args.#ptr_field,
948 args.#size_field.as_ref(),
949 args.#size_field.read(),
951 )
952 }});
953 }
954
955 for output in &self.outputs {
958 let type_name = &output.type_name;
959 let arg_name = &output.arg_name;
960 let ptr_field = format_ident!("{arg_name}_ptr");
961 let size_field = format_ident!("{arg_name}_size");
962 let size_doc = format!("Size of the contents `{ptr_field}` points to");
963 let capacity_field = format_ident!("{arg_name}_capacity");
964 let capacity_doc = format!("Capacity of the allocated memory `{ptr_field}` points to");
965
966 internal_args_pointers.push(quote! {
967 pub #ptr_field: ::core::ptr::NonNull<
968 <#type_name as ::ab_contracts_macros::__private::IoType>::PointerType,
969 >,
970 #[doc = #size_doc]
971 pub #size_field: *mut ::core::primitive::u32,
972 #[doc = #capacity_doc]
973 pub #capacity_field: ::core::ptr::NonNull<::core::primitive::u32>,
974 });
975
976 original_fn_args.push(quote! {&mut *{
977 const _: () = {
981 const fn assert_impl_io_type_optional<T>()
982 where
983 T: ::ab_contracts_macros::__private::IoTypeOptional,
984 {}
985 assert_impl_io_type_optional::<#type_name>();
986 };
987
988 <#type_name as ::ab_contracts_macros::__private::IoType>::from_mut_ptr(
989 &mut args.#ptr_field,
990 &mut args.#size_field,
991 args.#capacity_field.read(),
992 )
993 }});
994 }
995
996 let original_method_name = &fn_sig.ident;
997 let ffi_fn_name = derive_ffi_fn_name(self_type, trait_name, original_method_name)?;
998 let return_type = self.return_type.return_type();
999
1000 let internal_args_struct = {
1001 if !self.return_type.unit_return_type() {
1004 internal_args_pointers.push(quote! {
1005 pub ok_result_ptr: ::core::ptr::NonNull<#return_type>,
1006 pub ok_result_size: *mut ::core::primitive::u32,
1008 pub ok_result_capacity: ::core::ptr::NonNull<::core::primitive::u32>,
1010 });
1011
1012 preparation.push(quote! {
1016 debug_assert!(
1017 args.ok_result_ptr.is_aligned(),
1018 "`ok_result_ptr` pointer is misaligned"
1019 );
1020 if !args.ok_result_size.is_null() {
1021 debug_assert_eq!(
1022 args.ok_result_size.read(),
1023 0,
1024 "`ok_result_size` must be zero initially",
1025 );
1026 }
1027 debug_assert!(
1028 args.ok_result_capacity.read() >=
1029 <#return_type as ::ab_contracts_macros::__private::TrivialType>::SIZE,
1030 "`ok_result_capacity` specified is invalid",
1031 );
1032 });
1033 }
1034 let args_struct_doc = format!(
1035 "Data structure containing expected input to [`{ffi_fn_name}()`], it is used \
1036 internally by the contract, there should be no need to construct it explicitly \
1037 except maybe in contract's own tests"
1038 );
1039 quote_spanned! {fn_sig.span() =>
1040 #[doc = #args_struct_doc]
1041 #[derive(::core::fmt::Debug)]
1042 #[repr(C)]
1043 pub struct InternalArgs<'internal_args>
1044 {
1045 #( #internal_args_pointers )*
1046 _phantom: ::core::marker::PhantomData<&'internal_args ()>,
1047 }
1048 }
1049 };
1050
1051 let result_var_name = format_ident!("result");
1052 let guest_fn = {
1053 let result_handling = match &self.return_type {
1056 MethodReturnType::Unit(_) => {
1057 quote! {
1058 ::ab_contracts_macros::__private::ExitCode::ok()
1060 }
1061 }
1062 MethodReturnType::Regular(_) => {
1063 quote! {
1064 if !args.ok_result_size.is_null() {
1066 args.ok_result_size.write(
1067 <#return_type as ::ab_contracts_macros::__private::TrivialType>::SIZE,
1068 );
1069 }
1070 args.ok_result_ptr.write(#result_var_name);
1071 ::ab_contracts_macros::__private::ExitCode::ok()
1073 }
1074 }
1075 MethodReturnType::ResultUnit(_) => {
1076 quote! {
1077 match #result_var_name {
1079 Ok(()) => ::ab_contracts_macros::__private::ExitCode::ok(),
1080 Err(error) => error.exit_code(),
1081 }
1082 }
1083 }
1084 MethodReturnType::Result(_) => {
1085 quote! {
1086 match #result_var_name {
1088 Ok(result) => {
1089 if !args.ok_result_size.is_null() {
1091 args.ok_result_size.write(
1092 <#return_type as ::ab_contracts_macros::__private::TrivialType>::SIZE,
1093 );
1094 }
1095 args.ok_result_ptr.write(result);
1096 ::ab_contracts_macros::__private::ExitCode::ok()
1098 }
1099 Err(error) => error.exit_code(),
1100 }
1101 }
1102 }
1103 };
1104
1105 let full_struct_name = if let Some(trait_name) = trait_name {
1106 quote! { <#self_type as #trait_name> }
1107 } else {
1108 quote! { #self_type }
1109 };
1110
1111 quote_spanned! {fn_sig.span() =>
1114 #[cfg_attr(feature = "guest", unsafe(no_mangle))]
1123 #[allow(clippy::new_ret_no_self, reason = "Method was re-written for FFI purposes without `Self`")]
1124 #[allow(clippy::absurd_extreme_comparisons, reason = "Macro-generated code doesn't know the size upfront")]
1125 pub unsafe extern "C" fn #ffi_fn_name(
1126 mut args: ::core::ptr::NonNull<InternalArgs<'_>>,
1127 ) -> ::ab_contracts_macros::__private::ExitCode {
1128 unsafe {
1130 debug_assert!(args.is_aligned(), "`args` pointer is misaligned");
1131 let args = args.as_mut();
1132
1133 #( #preparation )*
1134
1135 #[allow(
1137 unused_variables,
1138 reason = "Sometimes result is `()`"
1139 )]
1140 #[allow(
1141 clippy::let_unit_value,
1142 reason = "Sometimes result is `()`"
1143 )]
1144 let #result_var_name = #full_struct_name::#original_method_name(
1145 #( #original_fn_args, )*
1146 );
1147
1148 #result_handling
1149 }
1150 }
1151 }
1152 };
1153
1154 let fn_pointer_static = {
1155 let adapter_ffi_fn_name = format_ident!("{ffi_fn_name}_adapter");
1156 let args_struct_name =
1157 derive_external_args_struct_name(self_type, trait_name, original_method_name)?;
1158
1159 quote! {
1160 #[doc(hidden)]
1161 pub mod fn_pointer {
1162 use super::*;
1163
1164 unsafe extern "C" fn #adapter_ffi_fn_name(
1165 ptr: ::core::ptr::NonNull<::core::ptr::NonNull<::core::ffi::c_void>>,
1166 ) -> ::ab_contracts_macros::__private::ExitCode {
1167 unsafe { #ffi_fn_name(ptr.cast::<InternalArgs<'_>>()) }
1170 }
1171
1172 pub const METHOD_FN_POINTER: ::ab_contracts_macros::__private::NativeExecutorContactMethod =
1173 ::ab_contracts_macros::__private::NativeExecutorContactMethod {
1174 method_fingerprint: &<#args_struct_name as ::ab_contracts_macros::__private::ExternalArgs>::FINGERPRINT,
1175 method_metadata: METADATA,
1176 ffi_fn: #adapter_ffi_fn_name,
1177 };
1178 }
1179 }
1180 };
1181
1182 Ok(quote! {
1183 #internal_args_struct
1184 #guest_fn
1185 #fn_pointer_static
1186 })
1187 }
1188
1189 fn generate_external_args_struct(
1190 &self,
1191 fn_sig: &Signature,
1192 trait_name: Option<&Ident>,
1193 ) -> Result<TokenStream, Error> {
1194 let self_type = &self.self_type;
1195 let original_method_name = &fn_sig.ident;
1196
1197 let args_struct_name =
1198 derive_external_args_struct_name(self_type, trait_name, original_method_name)?;
1199 let mut external_args_fields = Vec::new();
1201 let mut method_args = Vec::new();
1203 let mut method_args_fields = Vec::new();
1205
1206 for slot in &self.slots {
1208 let arg_name = &slot.arg_name;
1209 let ptr_field = format_ident!("{arg_name}_ptr");
1210
1211 external_args_fields.push(quote! {
1212 pub #ptr_field: ::core::ptr::NonNull<::ab_contracts_macros::__private::Address>,
1213 });
1214
1215 method_args.push(quote! {
1216 #arg_name: &::ab_contracts_macros::__private::Address,
1217 });
1218 method_args_fields.push(quote! {
1219 #ptr_field: ::core::ptr::NonNull::from_ref(#arg_name),
1220 });
1221 }
1222
1223 for input in &self.inputs {
1225 let type_name = &input.type_name;
1226 let arg_name = &input.arg_name;
1227 let ptr_field = format_ident!("{arg_name}_ptr");
1228 let size_field = format_ident!("{arg_name}_size");
1229 let size_doc = format!("Size of the contents `{ptr_field}` points to");
1230
1231 external_args_fields.push(quote! {
1232 pub #ptr_field: ::core::ptr::NonNull<
1233 <#type_name as ::ab_contracts_macros::__private::IoType>::PointerType,
1234 >,
1235 #[doc = #size_doc]
1236 pub #size_field: ::core::ptr::NonNull<::core::primitive::u32>,
1237 });
1238
1239 method_args.push(quote! {
1240 #arg_name: &#type_name,
1241 });
1242 method_args_fields.push(quote! {
1243 #ptr_field: unsafe {
1247 *::ab_contracts_macros::__private::IoType::as_ptr(#arg_name)
1248 },
1249 #size_field: unsafe {
1253 *::ab_contracts_macros::__private::IoType::size_ptr(#arg_name)
1254 },
1255 });
1256 }
1257
1258 let mut outputs_iter = self.outputs.iter().peekable();
1260 while let Some(output) = outputs_iter.next() {
1261 let type_name = &output.type_name;
1262 let arg_name = &output.arg_name;
1263 let ptr_field = format_ident!("{arg_name}_ptr");
1264 let size_field = format_ident!("{arg_name}_size");
1265 let size_doc = format!("Size of the contents `{ptr_field}` points to");
1266 let capacity_field = format_ident!("{arg_name}_capacity");
1267 let capacity_doc = format!("Capacity of the allocated memory `{ptr_field}` points to");
1268
1269 if outputs_iter.is_empty()
1272 && self.return_type.unit_return_type()
1273 && matches!(self.method_type, MethodType::Init)
1274 {
1275 continue;
1276 }
1277
1278 external_args_fields.push(quote! {
1279 pub #ptr_field: ::core::ptr::NonNull<
1280 <#type_name as ::ab_contracts_macros::__private::IoType>::PointerType,
1281 >,
1282 #[doc = #size_doc]
1283 pub #size_field: *mut ::core::primitive::u32,
1284 #[doc = #capacity_doc]
1285 pub #capacity_field: ::core::ptr::NonNull<::core::primitive::u32>,
1286 });
1287
1288 method_args.push(quote! {
1289 #arg_name: &mut #type_name,
1290 });
1291 method_args_fields.push(quote! {
1292 #ptr_field: unsafe {
1296 *::ab_contracts_macros::__private::IoType::as_mut_ptr(#arg_name)
1297 },
1298 #size_field: unsafe {
1302 *::ab_contracts_macros::__private::IoType::size_mut_ptr(#arg_name)
1303 },
1304 #capacity_field: unsafe {
1308 *::ab_contracts_macros::__private::IoType::capacity_ptr(#arg_name)
1309 },
1310 });
1311 }
1312
1313 let ffi_fn_name = derive_ffi_fn_name(self_type, trait_name, original_method_name)?;
1314
1315 if !(matches!(self.method_type, MethodType::Init) || self.return_type.unit_return_type()) {
1319 let return_type = &self.return_type.return_type();
1320
1321 external_args_fields.push(quote! {
1322 pub ok_result_ptr: ::core::ptr::NonNull<#return_type>,
1323 pub ok_result_size: *mut ::core::primitive::u32,
1325 pub ok_result_capacity: ::core::ptr::NonNull<::core::primitive::u32>,
1327 });
1328
1329 method_args.push(quote! {
1330 ok_result: &mut ::core::mem::MaybeUninit<#return_type>,
1331 ok_result_size: &mut ::core::primitive::u32,
1332 });
1333 method_args_fields.push(quote! {
1334 ok_result_ptr: unsafe {
1336 ::core::ptr::NonNull::new_unchecked(ok_result.as_mut_ptr())
1337 },
1338 ok_result_size: ::core::ptr::from_mut(ok_result_size),
1339 ok_result_capacity: ::core::ptr::NonNull::from_ref(
1341 &<#return_type as ::ab_contracts_macros::__private::TrivialType>::SIZE,
1342 ),
1343 });
1344 }
1345 let args_struct_doc = format!(
1346 "Data structure containing expected input for external method invocation, eventually \
1347 calling `{ffi_fn_name}()` on the other side by the host.\n\n\
1348 This can be used with [`Env`](::ab_contracts_macros::__private::Env), though there are \
1349 helper methods on this provided by extension trait that allow not dealing with this \
1350 struct directly in simpler cases."
1351 );
1352
1353 Ok(quote_spanned! {fn_sig.span() =>
1354 #[doc = #args_struct_doc]
1355 #[derive(::core::fmt::Debug)]
1356 #[repr(C)]
1357 pub struct #args_struct_name {
1358 #( #external_args_fields )*
1359 }
1360
1361 #[automatically_derived]
1362 unsafe impl ::ab_contracts_macros::__private::ExternalArgs for #args_struct_name {
1363 const FINGERPRINT: ::ab_contracts_macros::__private::MethodFingerprint =
1364 ::ab_contracts_macros::__private::MethodFingerprint::new(METADATA)
1365 .expect("Metadata is statically correct; qed");
1366 const METADATA: &[::core::primitive::u8] = METADATA;
1367 }
1368
1369 impl #args_struct_name {
1370 #[allow(
1372 clippy::new_without_default,
1373 reason = "Do not want `Default` in auto-generated code"
1374 )]
1375 pub fn new(
1376 #( #method_args )*
1377 ) -> Self {
1378 Self {
1379 #( #method_args_fields )*
1380 }
1381 }
1382 }
1383 })
1384 }
1385
1386 fn generate_metadata(
1387 &self,
1388 fn_sig: &Signature,
1389 trait_name: Option<&Ident>,
1390 ) -> Result<TokenStream, Error> {
1391 let self_type = &self.self_type;
1392 let mut method_metadata = Vec::new();
1395
1396 if let Some(env) = &self.env {
1397 let env_metadata_type = if env.mutability.is_some() {
1398 "EnvRw"
1399 } else {
1400 "EnvRo"
1401 };
1402
1403 let env_metadata_type = format_ident!("{env_metadata_type}");
1404 method_metadata.push(quote! {
1405 &[::ab_contracts_macros::__private::ContractMetadataKind::#env_metadata_type as ::core::primitive::u8],
1406 });
1407 }
1408
1409 if let Some(tmp) = &self.tmp {
1410 let tmp_metadata_type = if tmp.mutability.is_some() {
1411 "TmpRw"
1412 } else {
1413 "TmpRo"
1414 };
1415
1416 let tmp_metadata_type = format_ident!("{tmp_metadata_type}");
1417 let arg_name_metadata = derive_ident_metadata(&tmp.arg_name)?;
1418 method_metadata.push(quote! {
1419 &[::ab_contracts_macros::__private::ContractMetadataKind::#tmp_metadata_type as ::core::primitive::u8],
1420 #arg_name_metadata,
1421 });
1422 }
1423
1424 for slot in &self.slots {
1425 let slot_metadata_type = if slot.mutability.is_some() {
1426 "SlotRw"
1427 } else {
1428 "SlotRo"
1429 };
1430
1431 let slot_metadata_type = format_ident!("{slot_metadata_type}");
1432 let arg_name_metadata = derive_ident_metadata(&slot.arg_name)?;
1433 method_metadata.push(quote! {
1434 &[::ab_contracts_macros::__private::ContractMetadataKind::#slot_metadata_type as ::core::primitive::u8],
1435 #arg_name_metadata,
1436 });
1437 }
1438
1439 for input in &self.inputs {
1440 let io_metadata_type = format_ident!("Input");
1441 let arg_name_metadata = derive_ident_metadata(&input.arg_name)?;
1442 let type_name = &input.type_name;
1443
1444 method_metadata.push(quote! {
1445 &[::ab_contracts_macros::__private::ContractMetadataKind::#io_metadata_type as ::core::primitive::u8],
1446 #arg_name_metadata,
1447 <#type_name as ::ab_contracts_macros::__private::IoType>::METADATA,
1448 });
1449 }
1450
1451 let mut outputs_iter = self.outputs.iter().peekable();
1452 while let Some(output) = outputs_iter.next() {
1453 let io_metadata_type = "Output";
1454
1455 let io_metadata_type = format_ident!("{io_metadata_type}");
1456 let arg_name_metadata = derive_ident_metadata(&output.arg_name)?;
1457 let with_type_metadata = if outputs_iter.is_empty()
1459 && self.return_type.unit_return_type()
1460 && matches!(self.method_type, MethodType::Init)
1461 {
1462 None
1463 } else {
1464 let type_name = &output.type_name;
1465 Some(quote! {
1466 <#type_name as ::ab_contracts_macros::__private::IoType>::METADATA,
1467 })
1468 };
1469 method_metadata.push(quote! {
1470 &[::ab_contracts_macros::__private::ContractMetadataKind::#io_metadata_type as ::core::primitive::u8],
1471 #arg_name_metadata,
1472 #with_type_metadata
1473 });
1474 }
1475
1476 if !self.return_type.unit_return_type() {
1478 let arg_name_metadata = Literal::u8_unsuffixed(0);
1480 let with_type_metadata = if matches!(self.method_type, MethodType::Init) {
1482 None
1483 } else {
1484 let return_type = self.return_type.return_type();
1485 Some(quote! {
1486 <#return_type as ::ab_contracts_macros::__private::IoType>::METADATA,
1487 })
1488 };
1489 method_metadata.push(quote! {
1490 &[
1491 ::ab_contracts_macros::__private::ContractMetadataKind::Output as ::core::primitive::u8,
1492 #arg_name_metadata,
1493 ],
1494 #with_type_metadata
1495 });
1496 }
1497
1498 let method_type = match self.method_type {
1499 MethodType::Init => "Init",
1500 MethodType::Update => {
1501 if let Some(mutable) = &self.state {
1502 if mutable.is_some() {
1503 "UpdateStatefulRw"
1504 } else {
1505 "UpdateStatefulRo"
1506 }
1507 } else {
1508 "UpdateStateless"
1509 }
1510 }
1511 MethodType::View => {
1512 if let Some(mutable) = &self.state {
1513 if mutable.is_some() {
1514 return Err(Error::new(
1515 fn_sig.span(),
1516 "Stateful view methods are not supported",
1517 ));
1518 }
1519
1520 "ViewStateful"
1521 } else {
1522 "ViewStateless"
1523 }
1524 }
1525 };
1526
1527 let method_type = format_ident!("{method_type}");
1528 let number_of_arguments = u8::try_from(method_metadata.len()).map_err(|_error| {
1529 Error::new(
1530 fn_sig.span(),
1531 format!("Number of arguments must not be more than {}", u8::MAX),
1532 )
1533 })?;
1534 let total_number_of_arguments =
1535 Literal::u8_unsuffixed(number_of_arguments.saturating_add(1));
1536 let number_of_arguments = Literal::u8_unsuffixed(number_of_arguments);
1537
1538 let original_method_name = &fn_sig.ident;
1539 let ffi_fn_name = derive_ffi_fn_name(self_type, trait_name, original_method_name)?;
1540 let method_name_metadata = derive_ident_metadata(&ffi_fn_name)?;
1541 Ok(quote_spanned! {fn_sig.span() =>
1542 #[expect(
1543 clippy::assertions_on_constants,
1544 reason = "Auto-generated compile-time check"
1545 )]
1546 const fn metadata()
1547 -> ([::core::primitive::u8; ::ab_contracts_macros::__private::MAX_METADATA_CAPACITY], usize)
1548 {
1549 assert!(
1550 #total_number_of_arguments <= ::ab_contracts_macros::__private::MAX_TOTAL_METHOD_ARGS,
1551 "Too many arguments"
1552 );
1553 ::ab_contracts_macros::__private::concat_metadata_sources(&[
1554 &[::ab_contracts_macros::__private::ContractMetadataKind::#method_type as ::core::primitive::u8],
1555 #method_name_metadata,
1556 &[#number_of_arguments],
1557 #( #method_metadata )*
1558 ])
1559 }
1560
1561 pub const METADATA: &[::core::primitive::u8] =
1566 metadata()
1567 .0
1568 .split_at(metadata().1)
1569 .0;
1570 })
1571 }
1572
1573 pub(super) fn generate_trait_ext_components(
1574 &self,
1575 fn_sig: &Signature,
1576 fn_attrs: &[Attribute],
1577 trait_name: Option<&Ident>,
1578 ) -> Result<ExtTraitComponents, Error> {
1579 let self_type = &self.self_type;
1580
1581 let mut preparation = Vec::new();
1582 let mut method_args = Vec::new();
1583 let mut external_args_args = Vec::new();
1584 let mut result_processing = Vec::new();
1585
1586 method_args.push(quote_spanned! {fn_sig.span() =>
1588 contract: ::ab_contracts_macros::__private::Address,
1589 });
1590
1591 for slot in &self.slots {
1593 let arg_name = &slot.arg_name;
1594
1595 method_args.push(quote_spanned! {fn_sig.span() =>
1596 #arg_name: &::ab_contracts_macros::__private::Address,
1597 });
1598 external_args_args.push(quote_spanned! {fn_sig.span() => #arg_name });
1599 }
1600
1601 for input in &self.inputs {
1603 let type_name = &input.type_name;
1604 let arg_name = &input.arg_name;
1605
1606 method_args.push(quote_spanned! {fn_sig.span() =>
1607 #arg_name: &#type_name,
1608 });
1609 external_args_args.push(quote_spanned! {fn_sig.span() => #arg_name });
1610 }
1611
1612 let mut outputs_iter = self.outputs.iter().peekable();
1614 while let Some(output) = outputs_iter.next() {
1615 let type_name = &output.type_name;
1616 let arg_name = &output.arg_name;
1617
1618 if outputs_iter.is_empty()
1621 && self.return_type.unit_return_type()
1622 && matches!(self.method_type, MethodType::Init)
1623 {
1624 continue;
1625 }
1626
1627 method_args.push(quote_spanned! {fn_sig.span() =>
1628 #arg_name: &mut #type_name,
1629 });
1630 external_args_args.push(quote_spanned! {fn_sig.span() => #arg_name });
1631 }
1632
1633 let original_method_name = &fn_sig.ident;
1634 let ext_method_name = derive_ffi_fn_name(self_type, trait_name, original_method_name)?;
1635 let env_self = if matches!(self.method_type, MethodType::View) {
1637 quote_spanned! {fn_sig.span() => &self }
1638 } else {
1639 quote_spanned! {fn_sig.span() => &mut self }
1640 };
1641 let method_context_arg = (!matches!(self.method_type, MethodType::View)).then(|| {
1643 quote_spanned! {fn_sig.span() =>
1644 method_context: ::ab_contracts_macros::__private::MethodContext,
1645 }
1646 });
1647 let method_signature = if matches!(self.method_type, MethodType::Init)
1651 || self.return_type.unit_return_type()
1652 {
1653 quote_spanned! {fn_sig.span() =>
1654 #[allow(dead_code, reason = "Macro-generated")]
1655 fn #ext_method_name(
1656 #env_self,
1657 #method_context_arg
1658 #( #method_args )*
1659 ) -> ::core::result::Result<(), ::ab_contracts_macros::__private::ContractError>
1660 }
1661 } else {
1662 let return_type = self.return_type.return_type();
1663
1664 preparation.push(quote_spanned! {fn_sig.span() =>
1665 let mut ok_result = ::core::mem::MaybeUninit::uninit();
1666 let mut ok_result_size =
1669 <#return_type as ::ab_contracts_macros::__private::TrivialType>::SIZE;
1670 });
1671 external_args_args.push(quote_spanned! {fn_sig.span() =>
1672 &mut ok_result,
1673 &mut ok_result_size
1674 });
1675 result_processing.push(quote_spanned! {fn_sig.span() =>
1676 ok_result.assume_init()
1678 });
1679
1680 quote_spanned! {fn_sig.span() =>
1681 #[allow(dead_code, reason = "Macro-generated")]
1682 fn #ext_method_name(
1683 #env_self,
1684 #method_context_arg
1685 #( #method_args )*
1686 ) -> ::core::result::Result<
1687 #return_type,
1688 ::ab_contracts_macros::__private::ContractError,
1689 >
1690 }
1691 };
1692
1693 let attrs = fn_attrs.iter().filter(|attr| {
1694 let path = match &attr.meta {
1695 Meta::Path(path) => path,
1696 Meta::List(list) => &list.path,
1697 Meta::NameValue(name_value) => &name_value.path,
1698 };
1699
1700 if let Some(ident) = path.get_ident() {
1701 ident == "doc" || ident == "allow" || ident == "expect"
1702 } else {
1703 false
1704 }
1705 });
1706 let definition = quote_spanned! {fn_sig.span() =>
1707 #[allow(
1708 clippy::too_many_arguments,
1709 reason = "Generated code may have more arguments that source code"
1710 )]
1711 #( #attrs )*
1712 #method_signature;
1713 };
1714
1715 let args_struct_name =
1716 derive_external_args_struct_name(self_type, trait_name, original_method_name)?;
1717 let method_context_value = if matches!(self.method_type, MethodType::View) {
1719 quote_spanned! {fn_sig.span() =>
1720 ::ab_contracts_macros::__private::MethodContext::Reset
1721 }
1722 } else {
1723 quote_spanned! {fn_sig.span() =>
1724 method_context
1725 }
1726 };
1727 let r#impl = quote_spanned! {fn_sig.span() =>
1728 #[inline]
1729 #method_signature {
1730 #( #preparation )*
1731
1732 let mut args = #original_method_name::#args_struct_name::new(
1733 #( #external_args_args, )*
1734 );
1735
1736 self.call(contract, &mut args, #method_context_value)?;
1737
1738 #[allow(
1740 unused_unsafe,
1741 reason = "Sometimes there is no result to process and block is empty"
1742 )]
1743 #[allow(
1744 clippy::let_unit_value,
1745 reason = "Sometimes there is no result to process and block is empty"
1746 )]
1747 let result = unsafe {
1748 #( #result_processing )*
1749 };
1750
1751 Ok(result)
1752 }
1753 };
1754
1755 Ok(ExtTraitComponents { definition, r#impl })
1756 }
1757}
1758
1759fn extract_arg_name(mut pat: &Pat) -> Option<Ident> {
1760 loop {
1761 match pat {
1762 Pat::Ident(pat_ident) => {
1763 return Some(pat_ident.ident.clone());
1764 }
1765 Pat::Reference(pat_reference) => {
1766 pat = &pat_reference.pat;
1767 }
1768 _ => {
1769 return None;
1770 }
1771 }
1772 }
1773}
1774
1775fn derive_ffi_fn_name(
1776 type_name: &Type,
1777 trait_name: Option<&Ident>,
1778 method_name: &Ident,
1779) -> Result<Ident, Error> {
1780 let type_name = extract_ident_from_type(type_name).ok_or_else(|| {
1781 Error::new(
1782 type_name.span(),
1783 "`#[contract]` must be applied to a simple struct without generics",
1784 )
1785 })?;
1786 let ffi_fn_prefix =
1787 RenameRule::SnakeCase.apply_to_variant(trait_name.unwrap_or(type_name).to_string());
1788
1789 Ok(format_ident!("{ffi_fn_prefix}_{method_name}"))
1790}
1791
1792fn derive_external_args_struct_name(
1793 type_name: &Type,
1794 trait_name: Option<&Ident>,
1795 method_name: &Ident,
1796) -> Result<Ident, Error> {
1797 let type_name = extract_ident_from_type(type_name).ok_or_else(|| {
1798 Error::new(
1799 type_name.span(),
1800 "`#[contract]` must be applied to a simple struct without generics",
1801 )
1802 })?;
1803 Ok(format_ident!(
1804 "{}{}Args",
1805 trait_name.unwrap_or(type_name),
1806 RenameRule::PascalCase.apply_to_field(method_name.to_string())
1807 ))
1808}