blob: 49b1753102b0059883c47d232f50af5a6c5a4566 [file] [log] [blame]
#![recursion_limit = "128"]
use proc_macro::TokenStream;
use quote::quote;
use syn::Error;
use syn::ItemFn;
#[allow(clippy::needless_doctest_main)]
/// Procedural macro. This generates a function to read and parse
/// an environment variable to a usize at compile time. We have to use a
/// function since procedural macros can't generate expressions at the moment.
/// Example:
///
///
/// ```ignore
/// make_read_env_var!("MAX_COUNT")
///
/// fn do_it() {
/// let max = read_MAX_COUNT();
/// }
/// ```
#[proc_macro]
pub fn make_read_env_var(input: TokenStream) -> TokenStream {
let env_arg = syn::parse_macro_input!(input as syn::LitStr);
let env_var_name = env_arg.value();
let app_size = match std::env::var(&env_var_name) {
Ok(r) => r,
Err(_r) => {
return syn::Error::new(
env_arg.span(),
format!("Failed to find {} in environment, is it set?", env_var_name),
)
.to_compile_error()
.into()
}
};
let size = match app_size.parse::<usize>() {
Ok(r) => r,
Err(_r) => {
return syn::Error::new(
env_arg.span(),
format!("Environment var {} can't be parsed to usize", env_var_name),
)
.to_compile_error()
.into()
}
};
let concat = format!("read_{}", env_var_name);
let i = syn::Ident::new(&concat, proc_macro2::Span::call_site());
let result = quote! {
fn #i() -> usize { #size }
};
result.into()
}
#[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 { ::libtock::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 { ::libtock::executor::block_on(_block) }
}
))
.unwrap();
assert_eq!(actual, expected);
}
}