global_static_singleton/
lib.rs

1use 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]
8///Generate a ctor static of this struct.
9///By defeault, uses `Default` if the type implements it. You can pass an expression to the
10///attribute to use it instead.
11///```rust,ignore
12///#[singleton] //using Default::default
13///#[singleton(MyType::parse)] //using MyType::parse
14///#[singleton(|| MyType::new())] //closures work too
15pub 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]
49///Generate a ctor static with this function.
50///```rust,ignore
51///#[singleton_fn] //using MAKE_THING as name
52///#[singleton_fn(MY_STATIC)] //using MY_STATIC as name 
53///fn make_thing() -> Thing;
54///```
55pub 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}