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}