// This macro is based very heavily on the entry one in rust-embedded use proc_macro::TokenStream; use proc_macro2::Span; use quote::quote; use syn::{ItemFn, ReturnType, Type, Visibility, Ident}; use rand; use rand::Rng; #[proc_macro_attribute] pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream { let f: ItemFn = syn::parse(input).expect("#[agb::entry] must be applied to a function"); // Check that the function signature is correct assert!( f.sig.constness.is_none() && f.vis == Visibility::Inherited && f.sig.abi.is_none() && f.sig.inputs.is_empty() && f.sig.generics.params.is_empty() && f.sig.generics.where_clause.is_none() && match f.sig.output { ReturnType::Type(_, ref ty) => match **ty { Type::Never(_) => true, _ => false, }, _ => false, }, "#[agb::entry] must have signature [unsafe] fn () -> !" ); assert!(args.to_string() == "", "Must pass no args to #[agb::entry] macro"); let fn_name = random_ident(); let attrs = f.attrs; let stmts = f.block.stmts; quote!( #[export_name = "main"] #(#attrs)* pub fn #fn_name() -> ! { #(#stmts)* } ).into() } fn random_ident() -> Ident { let mut rng = rand::thread_rng(); Ident::new( &(0..16).map(|i| { if i == 0 || rng.gen() { ('a' as u8 + rng.gen::() % 25) as char } else { ('0' as u8 + rng.gen::() % 10) as char } }).collect::(), Span::call_site() ) }