race_proc_macro/
lib.rs

1extern crate proc_macro;
2extern crate syn;
3
4use proc_macro::TokenStream;
5use quote::quote;
6use syn::{parse_macro_input, ItemStruct};
7
8/// A macro to generate boilerplate code for using in wasm.
9///
10/// ```
11/// use race_api::prelude::*;
12/// use race_proc_macro::game_handler;
13///
14/// #[derive(BorshDeserialize, BorshSerialize)]
15/// #[game_handler]
16/// struct S {}
17///
18/// #[derive(BorshDeserialize, BorshSerialize)]
19/// struct Checkpoint {}
20///
21/// impl GameHandler for S {
22///
23///     type Checkpoint = Checkpoint;
24///
25///     fn init_state(context: &mut Effect, init_account: InitAccount) -> HandleResult<Self> {
26///         Ok(Self {})
27///     }
28
29///     fn handle_event(&mut self, context: &mut Effect, event: Event) -> HandleResult<()> {
30///         Ok(())
31///     }
32///
33///     fn into_checkpoint(self) -> HandleResult<Checkpoint> {
34///         Ok(Checkpoint {})
35///     }
36/// }
37/// ```
38#[proc_macro_attribute]
39pub fn game_handler(_metadata: TokenStream, input: TokenStream) -> TokenStream {
40    let s = parse_macro_input!(input as ItemStruct);
41    let s_idt = s.clone().ident;
42
43    TokenStream::from(quote! {
44
45        #s
46
47        pub fn read_ptr<T: BorshDeserialize>(ptr: &mut *mut u8, size: u32) -> Option<T> {
48            let slice = unsafe { core::slice::from_raw_parts_mut(*ptr, size as _) };
49            if let Ok(parsed) = T::try_from_slice(&slice) {
50                *ptr = unsafe { ptr.add(size as _) };
51                Some(parsed)
52            } else {
53                None
54            }
55        }
56
57        pub fn write_ptr<T: BorshSerialize>(ptr: &mut *mut u8, data: T) -> u32 {
58            if let Ok(vec) = data.try_to_vec() {
59                unsafe { std::ptr::copy(vec.as_ptr(), *ptr, vec.len()) }
60                vec.len() as _
61            } else {
62                0
63            }
64        }
65
66        #[no_mangle]
67        pub extern "C" fn handle_event(effect_size: u32, event_size: u32) -> u32 {
68            let mut ptr = 1 as *mut u8;
69            let mut effect: race_api::effect::Effect = if let Some(effect) =  read_ptr(&mut ptr, effect_size) {
70                effect
71            } else {
72                return 1
73            };
74            let event: race_api::event::Event = if let Some(event) = read_ptr(&mut ptr, event_size) {
75                event
76            } else {
77                return 2
78            };
79
80            let mut handler: #s_idt = effect.__handler_state();
81            match handler.handle_event(&mut effect, event) {
82                Ok(_) => effect.__set_handler_state(&handler),
83                Err(e) => effect.__set_error(e),
84            }
85
86            if effect.is_checkpoint {
87                match handler.into_checkpoint() {
88                    Ok(checkpoint_state) => effect.__set_checkpoint(checkpoint_state),
89                    Err(e) => effect.__set_error(e),
90                }
91            }
92
93            let mut ptr = 1 as *mut u8;
94            write_ptr(&mut ptr, effect)
95        }
96
97        #[no_mangle]
98        pub extern "C" fn init_state(effect_size: u32, init_account_size: u32) -> u32 {
99            let mut ptr = 1 as *mut u8;
100            let mut effect: race_api::effect::Effect = if let Some(effect) =  read_ptr(&mut ptr, effect_size) {
101                effect
102            } else {
103                return 1
104            };
105            let init_account: race_api::engine::InitAccount = if let Some(init_account) = read_ptr(&mut ptr, init_account_size) {
106                init_account
107            } else {
108                return 2
109            };
110            match #s_idt::init_state(&mut effect, init_account) {
111                Ok(handler) => effect.__set_handler_state(&handler),
112                Err(e) => effect.__set_error(e),
113            }
114            let mut ptr = 1 as *mut u8;
115            write_ptr(&mut ptr, effect)
116        }
117    })
118}