magic_static_macro/
lib.rs1#![allow(clippy::needless_doctest_main)]
2
3use proc_macro::TokenStream;
4use quote::ToTokens;
5
6#[proc_macro_attribute]
7pub fn main(attr: TokenStream, item: TokenStream) -> TokenStream {
62 let mut func = syn::parse_macro_input!(item as syn::ItemFn);
63 let attr = attr.to_string();
64
65 enum MagicStatic {
66 Module(syn::Path),
67 Item(syn::Path),
68 }
69 impl quote::ToTokens for MagicStatic {
70 fn to_tokens(&self, tokens: &mut quote::__private::TokenStream) {
71 match self {
72 MagicStatic::Module(path) => tokens.extend(quote::quote! { #path::magic_static() }),
73 MagicStatic::Item(path) => tokens.extend(quote::quote! { #path.__init() }),
74 }
75 }
76 }
77
78 let mut magic_statics = vec![];
79 for item in attr.split(',').map(|path| path.trim()) {
80 if let Some(item) = item.strip_prefix("mod ").map(str::trim) {
81 if item.contains("::") {
82 magic_statics.push(MagicStatic::Module(syn::parse_str(item).expect("Expected path to a module containing an accessible `magic_static` function")));
83 } else {
84 magic_statics.push(MagicStatic::Module(syn::parse_str(&format!("self::{}", item)).expect("Expected path to a module containing an accessible `magic_static` function")));
85 }
86 } else {
87 magic_statics.push(MagicStatic::Item(syn::parse_str(item).expect("Expected path to magic static")));
88 }
89 }
90
91 func.block.stmts.insert(
92 0,
93 syn::parse(quote::quote! {
94 {
95 #(#magic_statics;)*
96 }
97 }.into()).expect("Internal error"),
98 );
99
100 func.into_token_stream().into()
101}
102
103#[proc_macro_attribute]
104pub fn magic_static(_attr: TokenStream, item: TokenStream) -> TokenStream {
105 let mut func = syn::parse_macro_input!(item as syn::ItemStatic);
106
107 let ty = func.ty;
108 let expr = func.expr;
109
110 func.ty = Box::new(syn::parse_quote! { ::magic_static::MagicStatic<#ty> });
111 func.expr = Box::new(syn::parse_quote! {
112 ::magic_static::MagicStatic {
113 initialized: ::magic_static::__magic_static_initialized!(),
114 value: ::core::cell::UnsafeCell::new(::core::mem::MaybeUninit::uninit()),
115 init: || #expr
116 }
117 });
118
119 func.into_token_stream().into()
120}