multiversx_sc/io/
arg_nested_tuple.rs

1use unwrap_infallible::UnwrapInfallible;
2
3use super::{EndpointDynArgLoader, EndpointSingleArgLoader};
4use crate::{
5    api::{
6        const_handles, EndpointArgumentApi, EndpointArgumentApiImpl, ErrorApi, ErrorApiImpl,
7        ManagedTypeApi, StaticVarApiImpl, VMApi,
8    },
9    codec::{DecodeError, TopDecodeMulti, TopDecodeMultiInput},
10    err_msg,
11    io::{ArgErrorHandler, ArgId},
12    types::{ManagedArgBuffer, ManagedBuffer, ManagedType, ManagedVecRefIterator},
13};
14
15/// Argument count cannot change during execution, and it can get queried multiple times,
16/// so it makes sense to save it statically.
17///
18/// Especially the `EndpointDynArgLoader` repeatedly needs this value, keeping statically means it is no longer carried around.
19fn init_arguments_static_data<AA>()
20where
21    AA: EndpointArgumentApi + ManagedTypeApi + ErrorApi,
22{
23    AA::static_var_api_impl().set_num_arguments(AA::argument_api_impl().get_num_arguments());
24}
25
26/// Check that number of arguments is equal to value.
27///
28/// Since in this scenario this will be the only check, there is no need to load the argument count to static.
29///
30/// Inline prevented following an investigation.
31#[inline(never)]
32fn check_num_arguments_eq<AA>(expected: i32)
33where
34    AA: EndpointArgumentApi + ManagedTypeApi + ErrorApi,
35{
36    if AA::argument_api_impl().get_num_arguments() != expected {
37        AA::error_api_impl().signal_error(err_msg::ARG_WRONG_NUMBER.as_bytes());
38    }
39}
40
41/// Check that number of arguments is greater or equal than value.
42///
43/// Condition occurs when single args are followed by var-args.
44///
45/// Inline prevented following an investigation.
46#[inline(never)]
47fn check_num_arguments_ge<AA>(expected: i32)
48where
49    AA: EndpointArgumentApi + ManagedTypeApi + ErrorApi,
50{
51    if AA::static_var_api_impl().get_num_arguments() < expected {
52        AA::error_api_impl().signal_error(DecodeError::MULTI_TOO_FEW_ARGS.message_bytes());
53    }
54}
55
56/// Check that loader went through all existing arguments.
57#[inline(never)]
58fn check_no_more_args<AA, L>(loader: L)
59where
60    AA: EndpointArgumentApi + ManagedTypeApi + ErrorApi,
61    L: TopDecodeMultiInput,
62{
63    if loader.has_next() {
64        AA::error_api_impl().signal_error(DecodeError::MULTI_TOO_MANY_ARGS.message_bytes());
65    }
66}
67
68#[inline(never)]
69fn load_single_arg<AA, T>(index: i32, arg_id: ArgId) -> T
70where
71    AA: EndpointArgumentApi + ManagedTypeApi + ErrorApi,
72    T: TopDecodeMulti,
73{
74    let mut arg_loader = EndpointSingleArgLoader::<AA>::new(index);
75    let h = ArgErrorHandler::<AA>::from(arg_id);
76    T::multi_decode_or_handle_err(&mut arg_loader, h).unwrap_infallible()
77}
78
79#[inline(never)]
80fn load_multi_arg<AA, L, T>(loader: &mut L, arg_id: ArgId) -> T
81where
82    AA: EndpointArgumentApi + ManagedTypeApi + ErrorApi,
83    L: TopDecodeMultiInput,
84    T: TopDecodeMulti,
85{
86    let h = ArgErrorHandler::<AA>::from(arg_id);
87    T::multi_decode_or_handle_err(loader, h).unwrap_infallible()
88}
89
90/// Models an argument tree of the form `(arg1, (arg2, ... (argn, ())))`, used for retrieving endpoint arguments.
91///
92/// It translates to a small algorithm determined at compile-time. That is why all methods are inlined.
93pub trait ArgNestedTuple<AA>
94where
95    AA: EndpointArgumentApi + ManagedTypeApi + ErrorApi,
96{
97    type ArgNames;
98
99    fn check_num_single_args(index: i32);
100    fn next_single_arg(index: i32, arg_names: Self::ArgNames) -> Self;
101    fn next_multi_arg<L: TopDecodeMultiInput>(loader: L, arg_names: Self::ArgNames) -> Self;
102}
103
104impl<AA, Head, Tail> ArgNestedTuple<AA> for (Head, Tail)
105where
106    AA: EndpointArgumentApi + ManagedTypeApi + ErrorApi,
107    Head: TopDecodeMulti,
108    Tail: ArgNestedTuple<AA>,
109{
110    type ArgNames = (&'static str, Tail::ArgNames);
111
112    #[inline(always)]
113    fn check_num_single_args(index: i32) {
114        if Head::IS_SINGLE_VALUE {
115            Tail::check_num_single_args(index + 1);
116        } else {
117            // both check_num_arguments_ge and EndpointDynArgLoader need it in the future
118            init_arguments_static_data::<AA>();
119
120            check_num_arguments_ge::<AA>(index);
121        }
122    }
123
124    #[inline(always)]
125    fn next_single_arg(index: i32, arg_names: Self::ArgNames) -> Self {
126        if Head::IS_SINGLE_VALUE {
127            let (arg_name, tail_names) = arg_names;
128            let value = load_single_arg::<AA, Head>(index, ArgId::from(arg_name));
129            (value, Tail::next_single_arg(index + 1, tail_names))
130        } else {
131            let loader = EndpointDynArgLoader::<AA>::new_at_index(index);
132            Self::next_multi_arg(loader, arg_names)
133        }
134    }
135
136    #[inline(always)]
137    fn next_multi_arg<L: TopDecodeMultiInput>(mut loader: L, arg_names: Self::ArgNames) -> Self {
138        let (arg_name, tail_names) = arg_names;
139        let value = load_multi_arg::<AA, L, Head>(&mut loader, ArgId::from(arg_name));
140        (value, Tail::next_multi_arg(loader, tail_names))
141    }
142}
143
144impl<AA> ArgNestedTuple<AA> for ()
145where
146    AA: EndpointArgumentApi + ManagedTypeApi + ErrorApi,
147{
148    type ArgNames = ();
149
150    #[inline(always)]
151    fn check_num_single_args(index: i32) {
152        check_num_arguments_eq::<AA>(index);
153    }
154
155    #[inline(always)]
156    fn next_single_arg(_index: i32, _arg_names: Self::ArgNames) -> Self {}
157
158    #[inline(always)]
159    fn next_multi_arg<L: TopDecodeMultiInput>(loader: L, _arg_names: Self::ArgNames) -> Self {
160        check_no_more_args::<AA, L>(loader);
161    }
162}
163
164/// Used for loading all regular endpoint arguments. A call to this gets generated for all endpoints and callbacks.
165#[inline(always)]
166pub fn load_endpoint_args<AA, N>(arg_names: N::ArgNames) -> N
167where
168    AA: EndpointArgumentApi + ManagedTypeApi + ErrorApi,
169    N: ArgNestedTuple<AA>,
170{
171    N::check_num_single_args(0);
172    N::next_single_arg(0, arg_names)
173}
174
175#[inline(always)]
176pub fn load_callback_closure_args<AA, N>(arg_names: N::ArgNames) -> N
177where
178    AA: VMApi,
179    N: ArgNestedTuple<AA>,
180{
181    let loader = callback_closure_args_loader::<AA>();
182    N::next_multi_arg(loader, arg_names)
183}
184
185/// Currently used for the callback closure. No distinction there for single values.
186#[inline(always)]
187pub fn load_multi_args_custom_loader<AA, L, N>(loader: L, arg_names: N::ArgNames) -> N
188where
189    AA: EndpointArgumentApi + ManagedTypeApi + ErrorApi,
190    L: TopDecodeMultiInput,
191    N: ArgNestedTuple<AA>,
192{
193    init_arguments_static_data::<AA>();
194    N::next_multi_arg(loader, arg_names)
195}
196
197fn callback_closure_args_loader<AA>() -> ManagedVecRefIterator<'static, AA, ManagedBuffer<AA>>
198where
199    AA: VMApi,
200{
201    let cb_closure_args_serialized = ManagedBuffer::<AA>::new();
202    AA::argument_api_impl().load_callback_closure_buffer(cb_closure_args_serialized.get_handle());
203    unsafe {
204        let mut cb_closure_args_buffer =
205            ManagedArgBuffer::<AA>::from_raw_handle(const_handles::CALLBACK_CLOSURE_ARGS_BUFFER);
206        cb_closure_args_buffer.deserialize_overwrite(cb_closure_args_serialized);
207        ManagedVecRefIterator::new_from_handle(cb_closure_args_buffer.forget_into_handle())
208    }
209}