blob: 9804e29b3e58b412e3bd42174c006b3b7e021500 [file] [log] [blame]
#![recursion_limit = "128"]
extern crate proc_macro;
extern crate proc_macro2;
use proc_macro::TokenStream;
use quote::quote;
use syn::Error;
use syn::ItemFn;
#[allow(clippy::needless_doctest_main)]
/// Procedural attribute macro. This is meant to be applied to a binary's async
/// `main()`, transforming into a function that returns a type acceptable for
/// `main()`. In other words, this will not compile with libtock-rs:
/// ```ignore
/// async fn main() {
/// // async code
/// }
/// ```
/// and this will:
/// ```ignore
/// #[libtock::main]
/// async fn main() {
/// // async code
/// }
/// ```
#[proc_macro_attribute]
pub fn main(_: TokenStream, input: TokenStream) -> TokenStream {
generate_main_wrapped(input.into()).into()
}
fn generate_main_wrapped(input: proc_macro2::TokenStream) -> proc_macro2::TokenStream {
try_generate_main_wrapped(input).unwrap_or_else(|err| err.to_compile_error())
}
fn try_generate_main_wrapped(
input: proc_macro2::TokenStream,
) -> Result<proc_macro2::TokenStream, Error> {
let ast = syn::parse2::<ItemFn>(input)?;
let block = ast.block;
let output = &ast.sig.output;
Ok(quote!(
fn main() #output {
static mut MAIN_INVOKED: bool = false;
unsafe {
if MAIN_INVOKED {
panic!("Main called recursively; this is unsafe with #[libtock::main]");
}
MAIN_INVOKED = true;
}
let _block = async #block;
unsafe {::core::executor::block_on(_block) }
}
))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn wraps_main_into_blocking_executor() {
let method_def: proc_macro2::TokenStream = quote! {
async fn main() -> ::libtock::result::TockResult<()>{
method_call().await;
}
};
let actual: ItemFn = syn::parse2::<ItemFn>(generate_main_wrapped(method_def)).unwrap();
let expected: ItemFn = syn::parse2::<ItemFn>(quote!(
fn main() -> ::libtock::result::TockResult<()> {
static mut MAIN_INVOKED: bool = false;
unsafe {
if MAIN_INVOKED {
panic!("Main called recursively; this is unsafe with #[libtock::main]");
}
MAIN_INVOKED = true;
}
let _block = async {
method_call().await;
};
unsafe { ::core::executor::block_on(_block) }
}
))
.unwrap();
assert_eq!(actual, expected);
}
}