altv_internal_resource_main_macro/
lib.rs1use proc_macro::TokenStream;
2use quote::quote;
3use syn::{parse::Parser, spanned::Spanned, ItemFn};
4
5#[proc_macro_attribute]
32pub fn resource_main_func(params: TokenStream, input: TokenStream) -> TokenStream {
33 let fn_item = {
34 let input = input.clone();
35 syn::parse_macro_input!(input as ItemFn)
36 };
37 let syn::ItemFn {
38 sig: fn_sig,
39 block: fn_block,
40 ..
41 } = fn_item;
42 let fn_ident = fn_sig.ident;
43
44 if fn_ident != "main" {
45 return compile_error(fn_ident, "main function must be named \"main\"");
46 }
47 if !fn_sig.inputs.is_empty() {
48 return compile_error(fn_sig.inputs, "main function can't have any arguments");
49 }
50
51 let crate_name = parse_crate_name_from_params(params);
52
53 let wrapped_fn = quote! {
54 fn #fn_ident() -> bool {
55 use #crate_name::IntoVoidResult;
56 fn user_code() -> impl IntoVoidResult #fn_block
57
58 match user_code().into_void_result() {
59 Ok(()) => {
60 true
61 }
62 Err(err) => {
63 #crate_name::__internal::on_main_error(err);
64 false
65 }
66 }
67 }
68 };
69
70 let exportified_fn = relib_exportify::exportify(wrapped_fn);
71
72 quote! {
73 use #crate_name::__internal::relib_module as _;
75
76 #exportified_fn
77 }
78 .into()
79}
80
81fn compile_error(spanned: impl Spanned, message: &str) -> TokenStream {
82 syn::Error::new(spanned.span(), message)
83 .to_compile_error()
84 .into()
85}
86
87fn parse_crate_name_from_params(params: TokenStream) -> syn::Ident {
88 let mut crate_name = "altv".to_owned();
89
90 let parser = syn::meta::parser(|meta| {
91 assert!(
92 meta.path.is_ident("crate_name"),
93 "expected crate_name parameter"
94 );
95 let literal: syn::LitStr = meta.value()?.parse()?;
96 crate_name = literal.value();
97 Ok(())
98 });
99
100 parser
101 .parse(params)
102 .expect("Failed to parse altv::main parameters");
103
104 quote::format_ident!("{crate_name}")
105}