#![doc = include_str!("../README.md")]
use proc_macro::TokenStream;
use proc_macro2::Span;
use quote::quote;
use syn::{Error, FnArg, ItemFn, ReturnType};
fn compiler_error(err: Error) -> TokenStream {
err.to_compile_error().into()
}
fn common_main(item: TokenStream, arg_num: usize, export_name: &str, err_msg: &str) -> TokenStream {
let main = syn::parse_macro_input!(item as ItemFn);
let mut err = if let ReturnType::Type(_, ty) = &main.sig.output {
quote! { #ty }.to_string() != "!"
} else {
true
};
let args = &main.sig.inputs;
for arg in args.iter() {
if let FnArg::Typed(pat) = arg {
let ty = &pat.ty;
if quote! { #ty }.to_string() != "usize" {
err = true;
break;
}
}
}
if args.len() != arg_num {
err = true;
}
if err {
compiler_error(Error::new(Span::call_site(), err_msg))
} else {
quote! {
#[unsafe(export_name = #export_name)]
#main
}
.into()
}
}
#[proc_macro_attribute]
pub fn main(attr: TokenStream, item: TokenStream) -> TokenStream {
if !attr.is_empty() {
return compiler_error(Error::new(
Span::call_site(),
"expect an empty attribute or `#[axplat::main]`",
));
};
common_main(
item,
2,
"__axplat_main",
"expect a function with type `fn(cpu_id: usize, arg: usize) -> !`",
)
}
#[proc_macro_attribute]
pub fn secondary_main(attr: TokenStream, item: TokenStream) -> TokenStream {
if !attr.is_empty() {
return compiler_error(Error::new(
Span::call_site(),
"expect an empty attribute or `#[axplat::secondary_main]`",
));
};
common_main(
item,
1,
"__axplat_secondary_main",
"expect a function with type `fn(cpu_id: usize) -> !`",
)
}