use proc_macro::TokenStream;
use quote::quote;
use syn::{
Attribute, DeriveInput, ItemStatic, Result, TypePath,
parse::{Parse, ParseStream},
parse_macro_input,
};
struct SingletonArgs {
type_name: TypePath,
}
impl Parse for SingletonArgs {
fn parse(input: ParseStream) -> Result<Self> {
let type_name = input.parse()?;
Ok(SingletonArgs { type_name })
}
}
#[proc_macro_attribute]
pub fn singleton_from_static(attr: TokenStream, item: TokenStream) -> TokenStream {
let args = parse_macro_input!(attr as SingletonArgs);
let type_name = &args.type_name;
let input = parse_macro_input!(item as ItemStatic);
let static_name = &input.ident;
let expanded = quote! {
#input
impl #type_name {
pub fn get_instance() -> &'static #type_name {
&#static_name
}
}
};
expanded.into()
}
#[cfg(feature = "arc")]
#[proc_macro_attribute]
pub fn singleton_from_static_arc(attr: TokenStream, item: TokenStream) -> TokenStream {
let args = parse_macro_input!(attr as SingletonArgs);
let type_name = &args.type_name;
let input = parse_macro_input!(item as ItemStatic);
let static_name = &input.ident;
let expanded = quote! {
#input
impl #type_name {
pub fn get_instance() -> Arc<#type_name> {
Arc::clone(&#static_name)
}
}
};
expanded.into()
}
#[proc_macro_derive(Singleton, attributes(singleton))]
pub fn derive_singleton(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let struct_name = &input.ident;
let static_name =
find_singleton_static_name(&input.attrs).expect("#[singleton(STATIC_NAME)] is required");
let expanded = quote! {
impl #struct_name {
pub fn get_instance() -> &'static #struct_name {
&#static_name
}
}
};
expanded.into()
}
#[cfg(feature = "arc")]
#[proc_macro_derive(ArcSingleton, attributes(singleton))]
pub fn arc_derive_singleton(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let struct_name = &input.ident;
let static_name =
find_singleton_static_name(&input.attrs).expect("#[singleton(STATIC_NAME)] is required");
let expanded = quote! {
impl #struct_name {
pub fn get_instance() -> Arc<#struct_name> {
Arc::clone(&#static_name)
}
}
};
expanded.into()
}
fn find_singleton_static_name(attrs: &[Attribute]) -> Option<syn::Ident> {
for attr in attrs {
if attr.path().is_ident("singleton") {
if let Ok(syn::Expr::Path(expr_path)) = attr.parse_args::<syn::Expr>() {
if let Some(ident) = expr_path.path.get_ident() {
return Some(ident.clone());
}
}
}
}
None
}