1#![doc = include_str!("../README.md")]
2
3use proc_macro::TokenStream;
4use proc_macro2::Span;
5use quote::quote;
6use syn::{Error, FnArg, ItemFn, ReturnType};
7
8fn compiler_error(err: Error) -> TokenStream {
9 err.to_compile_error().into()
10}
11
12fn common_main(item: TokenStream, arg_num: usize, export_name: &str, err_msg: &str) -> TokenStream {
13 let main = syn::parse_macro_input!(item as ItemFn);
14 let mut err = if let ReturnType::Type(_, ty) = &main.sig.output {
15 quote! { #ty }.to_string() != "!"
16 } else {
17 true
18 };
19
20 let args = &main.sig.inputs;
21 for arg in args.iter() {
22 if let FnArg::Typed(pat) = arg {
23 let ty = &pat.ty;
24 if quote! { #ty }.to_string() != "usize" {
25 err = true;
26 break;
27 }
28 }
29 }
30 if args.len() != arg_num {
31 err = true;
32 }
33
34 if err {
35 compiler_error(Error::new(Span::call_site(), err_msg))
36 } else {
37 quote! {
38 #[unsafe(export_name = #export_name)]
39 #main
40 }
41 .into()
42 }
43}
44
45#[proc_macro_attribute]
62pub fn main(attr: TokenStream, item: TokenStream) -> TokenStream {
63 if !attr.is_empty() {
64 return compiler_error(Error::new(
65 Span::call_site(),
66 "expect an empty attribute or `#[axplat::main]`",
67 ));
68 };
69 common_main(
70 item,
71 2,
72 "__axplat_main",
73 "expect a function with type `fn(cpu_id: usize, arg: usize) -> !`",
74 )
75}
76
77#[proc_macro_attribute]
93pub fn secondary_main(attr: TokenStream, item: TokenStream) -> TokenStream {
94 if !attr.is_empty() {
95 return compiler_error(Error::new(
96 Span::call_site(),
97 "expect an empty attribute or `#[axplat::secondary_main]`",
98 ));
99 };
100 common_main(
101 item,
102 1,
103 "__axplat_secondary_main",
104 "expect a function with type `fn(cpu_id: usize) -> !`",
105 )
106}