global_static_singleton/
lib.rs1use pm::Span;
2use proc_macro as pm;
3
4use quote::{quote, ToTokens};
5use syn::{parse_macro_input, ItemStruct, Expr, Ident, ItemFn, spanned::Spanned};
6
7#[proc_macro_attribute]
8pub fn singleton(attr: pm::TokenStream, item: pm::TokenStream) -> pm::TokenStream {
16 let data = parse_macro_input!(item as ItemStruct);
17 let attr_expr = syn::parse::<Expr>(attr.clone());
18
19 let default = syn::parse::<Expr>(quote! { Default::default }.into()).unwrap();
20 let expr = match attr_expr {
21 Ok(tree) => tree,
22 Err(_) if attr.is_empty() => default,
23 Err(e) => return e.to_compile_error().into(),
24 };
25
26 let struct_name = &data.ident;
27 let static_name = syn::Ident::new(&struct_name.to_string().to_uppercase(), struct_name.span());
28 let fn_name = syn::Ident::new(
29 &format!("_{}_global_init", struct_name.to_string().to_lowercase()),
30 Span::call_site().into());
31
32
33 let out = quote! {
34 pub static #static_name: global_static::Global<#struct_name> = global_static::Global::new(#expr);
35 #[global_static::ctor::ctor]
36 fn #fn_name() {
37 #static_name.init()
38 }
39 #data
40 };
41
42
43
44 out.into()
45}
46
47
48#[proc_macro_attribute]
49pub fn singleton_fn(attr: pm::TokenStream, item: pm::TokenStream) -> pm::TokenStream {
56 let data = parse_macro_input!(item as ItemFn);
57 let attr_ident = syn::parse::<Ident>(attr).ok();
58
59 let item_name = &data.sig.ident;
60 let struct_name = match &data.sig.output {
61 syn::ReturnType::Default => quote! { () },
62 syn::ReturnType::Type(_, ty) => quote! { #ty },
63 };
64
65 let static_name = match attr_ident {
66 Some(ident) => ident,
67 None => syn::Ident::new(&item_name.to_string().to_uppercase(), item_name.span()),
68 };
69 let fn_name = syn::Ident::new(
70 &format!("_{}_global_init", static_name.to_string().to_lowercase()),
71 Span::call_site().into());
72
73 quote!{
74 pub static #static_name: global_static::Global<#struct_name> = global_static::Global::new(#item_name);
75 #[global_static::ctor::ctor]
76 fn #fn_name() {
77 #static_name.init()
78 }
79 #data
80 }.into()
81}