Skip to main content

proc_singleton/
lib.rs

1use proc_macro::TokenStream;
2use quote::quote;
3use syn::{
4    Attribute, DeriveInput, ItemStatic, Result, TypePath,
5    parse::{Parse, ParseStream},
6    parse_macro_input,
7};
8
9struct SingletonArgs {
10    type_name: TypePath,
11}
12
13impl Parse for SingletonArgs {
14    fn parse(input: ParseStream) -> Result<Self> {
15        let type_name = input.parse()?;
16        Ok(SingletonArgs { type_name })
17    }
18}
19
20/// Create a singleton from a static variable.
21///
22/// # Examples
23/// ```
24/// use std::sync::LazyLock;
25/// use uuid::Uuid;
26/// use proc_singleton::singleton_from_static;
27///
28/// #[singleton_from_static(Identifier)]
29/// static IDENT: LazyLock<Identifier> = LazyLock::new(|| {
30///     Identifier {
31///         id: Uuid::new_v4(),
32///     }
33/// });
34///
35/// struct Identifier {
36///     id: Uuid,
37/// }
38///
39/// fn main() {
40///     let instance = Identifier::get_instance();
41///     let ptr = instance as *const Identifier;
42///     let same_ptr = Identifier::get_instance() as *const Identifier;
43///
44///     assert_eq!(ptr, same_ptr);
45/// }
46/// ```
47#[proc_macro_attribute]
48pub fn singleton_from_static(attr: TokenStream, item: TokenStream) -> TokenStream {
49    let args = parse_macro_input!(attr as SingletonArgs);
50    let type_name = &args.type_name;
51    let input = parse_macro_input!(item as ItemStatic);
52    let static_name = &input.ident;
53
54    let expanded = quote! {
55        #input
56
57        impl #type_name {
58            pub fn get_instance() -> &'static #type_name {
59                &#static_name
60            }
61        }
62    };
63
64    expanded.into()
65}
66
67/// Derives an Arc-based singleton implementation for a struct based on a static variable.
68///
69/// # Examples
70/// ```
71/// use std::sync::{LazyLock, Arc};
72/// use uuid::Uuid;
73/// use proc_singleton::singleton_from_static_arc;
74///
75/// #[singleton_from_static_arc(Identifier)]
76/// static IDENT: LazyLock<Arc<Identifier>> = LazyLock::new(|| {
77///     Arc::new(Identifier {
78///         id: Uuid::new_v4(),
79///     })
80/// });
81///
82/// struct Identifier {
83///     id: Uuid,
84/// }
85///
86/// fn main() {
87///     let instance = Identifier::get_instance();
88///     let same_instance = Identifier::get_instance();
89///
90///     assert!(Arc::ptr_eq(&instance, &same_instance));
91/// }
92/// ```
93#[cfg(feature = "arc")]
94#[proc_macro_attribute]
95pub fn singleton_from_static_arc(attr: TokenStream, item: TokenStream) -> TokenStream {
96    let args = parse_macro_input!(attr as SingletonArgs);
97    let type_name = &args.type_name;
98    let input = parse_macro_input!(item as ItemStatic);
99    let static_name = &input.ident;
100
101    let expanded = quote! {
102        #input
103
104        impl #type_name {
105            pub fn get_instance() -> Arc<#type_name> {
106                Arc::clone(&#static_name)
107            }
108        }
109    };
110
111    expanded.into()
112}
113
114/// Derives a singleton implementation for a struct based on a static variable.
115///
116/// # Examples
117/// ```
118/// use std::sync::LazyLock;
119/// use uuid::Uuid;
120/// use proc_singleton::Singleton;
121///
122/// static IDENT: LazyLock<Identifier> = LazyLock::new(|| {
123///     Identifier {
124///         id: Uuid::new_v4(),
125///     }
126/// });
127/// #[derive(Singleton)]
128/// #[singleton(IDENT)]
129/// struct Identifier {
130///     id: Uuid,
131/// }
132///
133/// fn main() {
134///     let instance = Identifier::get_instance();
135///     let ptr = instance as *const Identifier;
136///     let same_ptr = Identifier::get_instance() as *const Identifier;
137///
138///     assert_eq!(ptr, same_ptr);
139/// }
140/// ```
141#[proc_macro_derive(Singleton, attributes(singleton))]
142pub fn derive_singleton(input: TokenStream) -> TokenStream {
143    let input = parse_macro_input!(input as DeriveInput);
144    let struct_name = &input.ident;
145    let static_name =
146        find_singleton_static_name(&input.attrs).expect("#[singleton(STATIC_NAME)] is required");
147
148    let expanded = quote! {
149        impl #struct_name {
150            pub fn get_instance() -> &'static #struct_name {
151                &#static_name
152            }
153        }
154    };
155
156    expanded.into()
157}
158
159/// Derives a singleton implementation for a struct based on a static Arc variable.
160///
161/// # Examples
162/// ```
163/// use std::sync::{LazyLock, Arc};
164/// use uuid::Uuid;
165/// use proc_singleton::ArcSingleton;
166///
167/// static IDENT: LazyLock<Arc<Identifier>> = LazyLock::new(|| {
168///     Arc::new(Identifier {
169///         id: Uuid::new_v4(),
170///     })
171/// });
172/// #[derive(ArcSingleton)]
173/// #[singleton(IDENT)]
174/// struct Identifier {
175///     id: Uuid,
176/// }
177///
178/// fn main() {
179///     let instance = Identifier::get_instance();
180///     let same_instance = Identifier::get_instance();
181///
182///     assert!(Arc::ptr_eq(&instance, &same_instance));
183/// }
184/// ```
185#[cfg(feature = "arc")]
186#[proc_macro_derive(ArcSingleton, attributes(singleton))]
187pub fn arc_derive_singleton(input: TokenStream) -> TokenStream {
188    let input = parse_macro_input!(input as DeriveInput);
189    let struct_name = &input.ident;
190    let static_name =
191        find_singleton_static_name(&input.attrs).expect("#[singleton(STATIC_NAME)] is required");
192
193    let expanded = quote! {
194        impl #struct_name {
195            pub fn get_instance() -> Arc<#struct_name> {
196                Arc::clone(&#static_name)
197            }
198        }
199    };
200
201    expanded.into()
202}
203
204fn find_singleton_static_name(attrs: &[Attribute]) -> Option<syn::Ident> {
205    for attr in attrs {
206        if attr.path().is_ident("singleton") {
207            if let Ok(syn::Expr::Path(expr_path)) = attr.parse_args::<syn::Expr>() {
208                if let Some(ident) = expr_path.path.get_ident() {
209                    return Some(ident.clone());
210                }
211            }
212        }
213    }
214    None
215}