solo5_rs_macros/
lib.rs

1#![feature(box_patterns)]
2use proc_macro::TokenStream;
3use proc_macro2::Span;
4use quote::quote;
5use syn::{parse_macro_input, FnArg, Ident, ItemFn, PatType, ReturnType, Type, TypePath};
6
7/* NOTE:
8 * This is one of the cheap wrapper macro and is not considered to be a good interface,
9 * but is adopted because I cannot come up with a better way to do it at the moment.
10*/
11
12#[proc_macro_attribute]
13pub fn main(_attr: TokenStream, item: TokenStream) -> TokenStream {
14    #[cfg(feature = "alloc")]
15    let tlsf_init = quote! {
16     // solo5_rs::tlsf::GLOBAL.init(start.heap_start, start.heap_size as usize);
17      unsafe {
18        solo5_rs::ALLOCATOR.lock().init(start.heap_start as *mut u8, start.heap_size as usize);
19    }
20    };
21
22    #[cfg(not(feature = "alloc"))]
23    let tlsf_init = quote! {};
24
25    let body = item.clone();
26    let body = parse_macro_input!(body as ItemFn);
27    let ident = body.sig.ident.clone();
28    let inputs = body.sig.inputs.clone();
29    let solo5_main_arg = match inputs.len() {
30        0 => {
31            quote! {}
32        }
33        1 => {
34            let arg = inputs.first().unwrap();
35            match arg {
36                FnArg::Typed(PatType {
37                    attrs: _,
38                    pat: _,
39                    colon_token: _,
40                    ty: box Type::Path(TypePath { qself: _, path }),
41                }) => {
42                    if *path.get_ident().unwrap() == Ident::new("Solo5StartInfo", Span::call_site())
43                    {
44                        quote! {
45                            solo5_rs::Solo5StartInfo::from(start)
46                        }
47                    } else {
48                        let entry = quote! {
49                            compile_error!("The only argument of solo5_rs::main function must be of type Solo5StartInfo.");
50                            #body
51                        };
52
53                        return TokenStream::from(entry);
54                    }
55                }
56                _ => {
57                    let entry = quote! {
58                        compile_error!("solo5_rs::main function does not take `self`.");
59                        #body
60                    };
61
62                    return TokenStream::from(entry);
63                }
64            }
65        }
66        _ => {
67            let entry = quote! {
68                compile_error!("solo5_rs::main function has too many arguments.");
69                #body
70            };
71
72            return TokenStream::from(entry);
73        }
74    };
75
76    let call_main = match body.sig.output.clone() {
77        ReturnType::Default => quote! {
78        #ident(#solo5_main_arg);
79                return 0;
80            },
81        ReturnType::Type(_, _) => quote! {
82        u64::from(#ident(#solo5_main_arg))
83            },
84    };
85
86    let entry = quote! {
87    #[no_mangle]
88    pub extern "C" fn solo5_app_main(start: &solo5_sys::solo5_start_info) -> u64 {
89        #tlsf_init
90        #call_main
91    }
92        };
93
94    let mut entry = TokenStream::from(entry);
95    entry.extend(item);
96
97    entry
98}