#![allow(clippy::needless_doctest_main)]
use proc_macro::TokenStream;
use quote::ToTokens;
#[proc_macro_attribute]
pub fn main(attr: TokenStream, item: TokenStream) -> TokenStream {
let mut func = syn::parse_macro_input!(item as syn::ItemFn);
let attr = attr.to_string();
enum MagicStatic {
Module(syn::Path),
Item(syn::Path),
}
impl quote::ToTokens for MagicStatic {
fn to_tokens(&self, tokens: &mut quote::__private::TokenStream) {
match self {
MagicStatic::Module(path) => tokens.extend(quote::quote! { #path::magic_static() }),
MagicStatic::Item(path) => tokens.extend(quote::quote! { #path.__init() }),
}
}
}
let mut magic_statics = vec![];
for item in attr.split(',').map(|path| path.trim()) {
if let Some(item) = item.strip_prefix("mod ").map(str::trim) {
if item.contains("::") {
magic_statics.push(MagicStatic::Module(syn::parse_str(item).expect("Expected path to a module containing an accessible `magic_static` function")));
} else {
magic_statics.push(MagicStatic::Module(syn::parse_str(&format!("self::{}", item)).expect("Expected path to a module containing an accessible `magic_static` function")));
}
} else {
magic_statics.push(MagicStatic::Item(syn::parse_str(item).expect("Expected path to magic static")));
}
}
func.block.stmts.insert(
0,
syn::parse(quote::quote! {
{
#(#magic_statics;)*
}
}.into()).expect("Internal error"),
);
func.into_token_stream().into()
}
#[proc_macro_attribute]
pub fn magic_static(_attr: TokenStream, item: TokenStream) -> TokenStream {
let mut func = syn::parse_macro_input!(item as syn::ItemStatic);
let ty = func.ty;
let expr = func.expr;
func.ty = Box::new(syn::parse_quote! { ::magic_static::MagicStatic<#ty> });
func.expr = Box::new(syn::parse_quote! {
::magic_static::MagicStatic {
initialized: ::magic_static::__magic_static_initialized!(),
value: ::core::cell::UnsafeCell::new(::core::mem::MaybeUninit::uninit()),
init: || #expr
}
});
func.into_token_stream().into()
}