1#[macro_export]
14macro_rules! hash_fn {
15 (@raw $kind:expr, $name:expr, ( $( $ty:ty ),* $(,)? ) -> $reply:ty $(| $throws:ty )?) => {{
16 let mut fn_hash = $crate::keccak_const::Keccak256::new();
17 fn_hash = fn_hash.update($kind.as_bytes()).update($name.as_bytes());
18 $( fn_hash = fn_hash.update(&<$ty as $crate::sails_reflect_hash::ReflectHash>::HASH); )*
19 fn_hash = fn_hash.update(b"res").update(&<$reply as $crate::sails_reflect_hash::ReflectHash>::HASH);
20 $( fn_hash = fn_hash.update(b"throws").update(&<$throws as $crate::sails_reflect_hash::ReflectHash>::HASH); )?
21 fn_hash.finalize()
22 }};
23
24 (
25 $kind:literal $name:expr, ( $( $ty:ty ),* $(,)? ) -> $reply:ty $(| $throws:ty )?
26 ) => {
27 $crate::hash_fn!(@raw $kind, $name, ( $( $ty ),* ) -> $reply $(| $throws )?)
28 };
29
30 (
31 command $name:ident ( $( $ty:ty ),* $(,)? ) -> $reply:ty $(| $throws:ty )?
32 ) => {
33 $crate::hash_fn!(@raw "command", stringify!($name), ( $( $ty ),* ) -> $reply $(| $throws )?)
34 };
35
36 (
37 query $name:ident ( $( $ty:ty ),* $(,)? ) -> $reply:ty $(| $throws:ty )?
38 ) => {
39 $crate::hash_fn!(@raw "query", stringify!($name), ( $( $ty ),* ) -> $reply $(| $throws )?)
40 };
41}
42
43#[macro_export]
64macro_rules! program_ctor {
65 (
66 $prg:ident = $($call:ident)::+ ( $( $arg:ident ),* $(,)? ) .await .unwrap(),
67 params_struct = $params_ty:ty $(,)?
68 ) => {{
69 $crate::gstd::message_loop(async move {
70 $crate::program_ctor!(
71 @store $prg = $crate::ok_or_throws!($($call)::+ ( $( $arg, )* ).await, $params_ty, 0)
72 );
73 });
74 }};
75 (
76 $prg:ident = $($call:ident)::+ ( $( $arg:ident ),* $(,)? ) .await,
77 params_struct = $params_ty:ty $(,)?
78 ) => {{
79 $crate::gstd::message_loop(async move {
80 $crate::program_ctor!(@store $prg = $($call)::+ ( $( $arg, )* ).await);
81 });
82 }};
83 (
84 $prg:ident = $($call:ident)::+ ( $( $arg:ident ),* $(,)? ) .unwrap(),
85 params_struct = $params_ty:ty $(,)?
86 ) => {{
87 $crate::program_ctor!(
88 @store $prg = $crate::ok_or_throws!($($call)::+ ( $( $arg, )* ), $params_ty, 0)
89 );
90 }};
91 (
92 $prg:ident = $($call:ident)::+ ( $( $arg:ident ),* $(,)? ),
93 params_struct = $params_ty:ty $(,)?
94 ) => {{
95 $crate::program_ctor!(@store $prg = $($call)::+ ( $( $arg, )* ));
96 }};
97 (@store $prg:ident = $program:expr) => {{
98 unsafe {
99 $prg = Some($program);
100 }
101 }};
102}
103
104#[macro_export]
134macro_rules! invocation_io {
135 (
136 $struct_vis:vis struct $params_struct:ident {
137 $( $field_vis:vis $field:ident : $ty:ty ),* $(,)?
138 },
139 entry_id = $entry_id:expr $(,)?
140 ) => {
141 $crate::invocation_io!(
142 $struct_vis struct $params_struct {
143 $( $field_vis $field : $ty, )*
144 },
145 interface_id = $crate::meta::InterfaceId::zero(),
146 entry_id = $entry_id,
147 );
148 };
149
150 (
151 $struct_vis:vis struct $params_struct:ident {
152 $( $field_vis:vis $field:ident : $ty:ty ),* $(,)?
153 },
154 interface_id = $interface_id:expr,
155 entry_id = $entry_id:expr $(,)?
156 ) => {
157 $crate::invocation_io! {
158 @with_decode
159 $struct_vis struct $params_struct {
160 $( $field_vis $field : $ty, )*
161 },
162 interface_id = $interface_id,
163 entry_id = $entry_id,
164 }
165 };
166
167 (
168 $struct_vis:vis struct $params_struct:ident {
169 $( $field_vis:vis $field:ident : $ty:ty ),* $(,)?
170 },
171 entry_id = $entry_id:expr,
172 decode = false $(,)?
173 ) => {
174 $crate::invocation_io!(
175 $struct_vis struct $params_struct {
176 $( $field_vis $field : $ty, )*
177 },
178 interface_id = $crate::meta::InterfaceId::zero(),
179 entry_id = $entry_id,
180 decode = false,
181 );
182 };
183
184 (
185 $struct_vis:vis struct $params_struct:ident {
186 $( $field_vis:vis $field:ident : $ty:ty ),* $(,)?
187 },
188 interface_id = $interface_id:expr,
189 entry_id = $entry_id:expr,
190 decode = false $(,)?
191 ) => {
192 $crate::invocation_io! {
193 @without_decode
194 $struct_vis struct $params_struct {
195 $( $field_vis $field : $ty, )*
196 },
197 interface_id = $interface_id,
198 entry_id = $entry_id,
199 }
200 };
201
202 (@with_decode
203 $struct_vis:vis struct $params_struct:ident {
204 $( $field_vis:vis $field:ident : $ty:ty, )*
205 },
206 interface_id = $interface_id:expr,
207 entry_id = $entry_id:expr $(,)?
208 ) => {
209 #[derive($crate::Decode, $crate::TypeInfo)]
210 #[codec(crate = $crate::scale_codec)]
211 #[type_info(crate = $crate::type_info)]
212 $struct_vis struct $params_struct {
213 $( $field_vis $field: $ty, )*
214 }
215
216 $crate::invocation_io! {
217 @impl_common
218 $params_struct,
219 interface_id = $interface_id,
220 entry_id = $entry_id,
221 }
222 };
223
224 (@without_decode
225 $struct_vis:vis struct $params_struct:ident {
226 $( $field_vis:vis $field:ident : $ty:ty, )*
227 },
228 interface_id = $interface_id:expr,
229 entry_id = $entry_id:expr $(,)?
230 ) => {
231 #[derive($crate::TypeInfo)]
232 #[type_info(crate = $crate::type_info)]
233 $struct_vis struct $params_struct {
234 $( $field_vis $field: $ty, )*
235 }
236
237 $crate::invocation_io! {
238 @impl_common
239 $params_struct,
240 interface_id = $interface_id,
241 entry_id = $entry_id,
242 }
243 };
244
245 (@impl_common
246 $params_struct:ident,
247 interface_id = $interface_id:expr,
248 entry_id = $entry_id:expr $(,)?
249 ) => {
250 impl $crate::meta::Identifiable for $params_struct {
251 const INTERFACE_ID: $crate::meta::InterfaceId = $interface_id;
252 }
253
254 impl $crate::meta::MethodMeta for $params_struct {
255 const ENTRY_ID: u16 = $entry_id;
256 }
257
258 impl $crate::gstd::InvocationIo for $params_struct {
259 type Params = Self;
260 }
261 };
262}
263
264#[macro_export]
269macro_rules! service_route_dispatch {
270 (
271 $svc:ident : $service_ty:ty,
272 interface_id = $interface_id:expr,
273 entry_id = $entry_id:expr,
274 input = $input:expr $(,)?
275 ) => {{
276 let is_async = <$service_ty as $crate::gstd::services::Service>::Exposure::check_asyncness(
277 $interface_id,
278 $entry_id,
279 )
280 .unwrap_or_else(|| $crate::gstd::unknown_input_panic("Unknown call", &[]));
281
282 if is_async {
283 $crate::gstd::message_loop(async move {
284 $svc.try_handle_async($interface_id, $entry_id, $input, |encoded_result, value| {
285 $crate::gstd::msg::reply_bytes(encoded_result, value)
286 .expect("Failed to send output");
287 })
288 .await
289 .unwrap_or_else(|| $crate::gstd::unknown_input_panic("Unknown request", &[]));
290 });
291 } else {
292 $svc.try_handle($interface_id, $entry_id, $input, |encoded_result, value| {
293 $crate::gstd::msg::reply_bytes(encoded_result, value)
294 .expect("Failed to send output");
295 })
296 .unwrap_or_else(|| $crate::gstd::unknown_input_panic("Unknown request", &[]));
297 }
298 }};
299}
300
301#[macro_export]
307macro_rules! ok_or_throws {
308 ($res: expr, $param: ty, $route_idx: expr) => {
309 match $res {
310 Ok(r) => r,
311 Err(e) => {
312 let encoded = $crate::gstd::encode_invocation_payload::<$param, _, _>(
313 &e,
314 $route_idx,
315 |encoded| encoded.to_vec(),
316 );
317 if encoded.len() <= $crate::gstd::MAX_PANIC_PAYLOAD_SIZE {
318 $crate::gstd::Syscall::panic(&encoded)
319 } else {
320 ::core::panic!("Error payload is too large to panic")
321 }
322 }
323 }
324 };
325}
326
327#[cfg(test)]
328mod tests {
329 use crate::prelude::*;
330
331 #[allow(dead_code)]
332 struct MyProgram {
333 p1: u32,
334 p2: String,
335 }
336
337 static mut PROGRAM: Option<MyProgram> = None;
338
339 impl MyProgram {
340 pub async fn new(p1: u32, p2: String) -> Self {
341 Self { p1, p2 }
342 }
343
344 pub async fn new_result(p1: u32, p2: String) -> Result<Self, String> {
345 Ok(Self { p1, p2 })
346 }
347 }
348
349 mod meta_in_program {
350 invocation_io!(pub struct __NewParams {}, entry_id = 0,);
351 invocation_io!(pub struct __NewResultParams {}, entry_id = 0,);
352 }
353
354 #[test]
355 fn program_ctor_async() {
356 let p1 = 42_u32;
357 let p2 = String::from("payload");
358
359 let _compile = || {
360 program_ctor!(
361 PROGRAM = MyProgram::new(p1, p2).await,
362 params_struct = meta_in_program::__NewParams,
363 );
364 };
365 }
366
367 #[test]
368 fn program_ctor_async_unwrap_result() {
369 let p1 = 42_u32;
370 let p2 = String::from("payload");
371
372 let _compile = || {
373 program_ctor!(
374 PROGRAM = MyProgram::new_result(p1, p2).await.unwrap(),
375 params_struct = meta_in_program::__NewResultParams,
376 );
377 };
378 }
379
380 #[test]
381 fn invocation_io_macro_compiles() {
382 invocation_io!(
383 pub struct __FooParams {
384 pub(super) a: u32,
385 pub(super) b: String,
386 },
387 interface_id = crate::meta::InterfaceId::zero(),
388 entry_id = 7,
389 );
390
391 let _params: <__FooParams as crate::gstd::InvocationIo>::Params = __FooParams {
392 a: 1,
393 b: String::from("ok"),
394 };
395 let _ = (_params.a, &_params.b);
396
397 assert_eq!(<__FooParams as crate::meta::MethodMeta>::ENTRY_ID, 7);
398 }
399
400 #[test]
401 fn invocation_io_macro_defaults_interface_id_to_zero() {
402 invocation_io!(
403 pub struct __BarParams {
404 pub(super) a: u32,
405 },
406 entry_id = 3,
407 );
408
409 let params = __BarParams { a: 1 };
410 let _ = params.a;
411
412 assert_eq!(
413 <__BarParams as crate::meta::Identifiable>::INTERFACE_ID,
414 crate::meta::InterfaceId::zero()
415 );
416 assert_eq!(<__BarParams as crate::meta::MethodMeta>::ENTRY_ID, 3);
417 }
418
419 #[test]
420 fn service_route_dispatch_macro_compiles() {
421 struct DummyService;
422 struct DummyExposure;
423
424 impl crate::gstd::services::Service for DummyService {
425 type Exposure = DummyExposure;
426
427 fn expose(self, _route_idx: u8) -> Self::Exposure {
428 DummyExposure
429 }
430 }
431
432 impl crate::gstd::services::Exposure for DummyExposure {
433 fn interface_id() -> crate::meta::InterfaceId {
434 crate::meta::InterfaceId::zero()
435 }
436
437 fn route_idx(&self) -> u8 {
438 0
439 }
440
441 fn check_asyncness(
442 _interface_id: crate::meta::InterfaceId,
443 _entry_id: u16,
444 ) -> Option<bool> {
445 Some(false)
446 }
447 }
448
449 impl DummyExposure {
450 async fn try_handle_async(
451 self,
452 _interface_id: crate::meta::InterfaceId,
453 _entry_id: u16,
454 _input: &[u8],
455 _result_handler: impl FnOnce(&[u8], u128),
456 ) -> Option<()> {
457 Some(())
458 }
459
460 fn try_handle(
461 self,
462 _interface_id: crate::meta::InterfaceId,
463 _entry_id: u16,
464 _input: &[u8],
465 _result_handler: impl FnOnce(&[u8], u128),
466 ) -> Option<()> {
467 Some(())
468 }
469 }
470
471 let svc = DummyExposure;
472 let interface_id = crate::meta::InterfaceId::zero();
473 let entry_id = 0u16;
474 let input: &[u8] = &[];
475
476 let _compile = || {
477 service_route_dispatch!(
478 svc: DummyService,
479 interface_id = interface_id,
480 entry_id = entry_id,
481 input = input,
482 );
483 };
484 }
485}