@@ -2,33 +2,34 @@ use proc_macro::TokenStream;
22use quote:: { format_ident, quote} ;
33use syn:: { Ident , ItemFn , ItemStatic , ReturnType , parse_macro_input} ;
44
5- /// Wraps a `static mut` variable as a reactive signal (similar to a property)
6- /// with getter and setter functions.
5+ /// Wraps a `static mut` variable as a reactive global signal.
76///
8- /// The `ref_signal!` macro transforms a `static mut` variable into a `reactive_cache::Signal`,
9- /// and automatically generates:
10- /// 1. A `_get()` function that returns a reference to the value, allowing read access.
11- /// - This reference behaves like a normal immutable reference for most purposes.
12- /// 2. A `_set(value)` function to write the value (returns `true` if changed).
13- ///
14- /// Unlike `signal!`, `ref_signal!` does **not** generate a same-named function that directly returns the value.
7+ /// The `signal!` macro transforms a `static mut` variable into a `reactive_cache::Signal`,
8+ /// and generates a **function with the same name as the variable** that returns a
9+ /// `&'static Rc<Signal<T>>`. You can then call `.get()` to read the value or `.set(value)` to update it.
1510///
1611/// # Requirements
1712///
18- /// - The macro currently supports only `static mut` variables.
19- /// - The variable type must implement `Eq`.
13+ /// - Supports only `static mut` variables.
14+ /// - Type `T` must implement `Eq`.
2015///
2116/// # Examples
2217///
2318/// ```rust
24- /// use reactive_macros::ref_signal;
19+ /// use reactive_macros::signal;
20+ ///
21+ /// signal!(static mut A: i32 = 10;);
22+ ///
23+ /// assert_eq!(*A().get(), 10);
24+ /// assert!(A().set(20));
25+ /// assert_eq!(*A().get(), 20);
26+ /// assert!(!A().set(20)); // No change
2527///
26- /// ref_signal !(static mut A : String = "hello".to_string(););
28+ /// signal !(static mut B : String = "hello".to_string(););
2729///
28- /// assert_eq!(&*A_get(), "hello");
29- /// assert!(A_set("signal".to_string()));
30- /// assert_eq!(&*A_get(), "signal");
31- /// assert!(!A_set("signal".to_string())); // No change
30+ /// assert_eq!(*B().get(), "hello");
31+ /// assert!(B().set("world".to_string()));
32+ /// assert_eq!(*B().get(), "world");
3233/// ```
3334///
3435/// # SAFETY
@@ -51,121 +52,22 @@ use syn::{Ident, ItemFn, ItemStatic, ReturnType, parse_macro_input};
5152/// halting problem. Therefore, you must ensure manually that effects do not
5253/// update signals within their own dependency chain.
5354#[ proc_macro]
54- pub fn ref_signal ( input : TokenStream ) -> TokenStream {
55+ pub fn signal ( input : TokenStream ) -> TokenStream {
5556 let item = parse_macro_input ! ( input as ItemStatic ) ;
5657
57- let attrs = & item. attrs ;
5858 let vis = & item. vis ;
59- let static_token = & item. static_token ;
60- let _mutability = & item. mutability ;
6159 let ident = & item. ident ;
62- let colon_token = & item. colon_token ;
6360 let ty = & item. ty ;
64- let eq_token = & item. eq_token ;
6561 let expr = & item. expr ;
66- let semi_token = & item. semi_token ;
67-
68- let mutability = match & item. mutability {
69- syn:: StaticMutability :: Mut ( _) => quote ! { mut } ,
70- syn:: StaticMutability :: None => quote ! { } ,
71- _ => {
72- return syn:: Error :: new_spanned ( & item. mutability , "Mutability not supported" )
73- . to_compile_error ( )
74- . into ( ) ;
75- }
76- } ;
77-
78- let ident_p = format_ident ! ( "_{}" , ident. to_string( ) . to_uppercase( ) ) ;
79- let ident_get = format_ident ! ( "{}_get" , ident) ;
80- let ident_set = format_ident ! ( "{}_set" , ident) ;
8162
8263 let lazy_ty = quote ! { reactive_cache:: Lazy <std:: rc:: Rc <reactive_cache:: Signal <#ty>>> } ;
8364 let expr = quote ! { reactive_cache:: Lazy :: new( || reactive_cache:: Signal :: new( #expr) ) } ;
8465
8566 let expanded = quote ! {
86- #( #attrs) *
87- #vis #static_token #mutability #ident_p #colon_token #lazy_ty #eq_token #expr #semi_token
88-
89- #[ allow( non_snake_case) ]
90- pub fn #ident_get( ) -> std:: cell:: Ref <' static , #ty> {
91- unsafe { #ident_p. get( ) }
92- }
93-
94- #[ allow( non_snake_case) ]
95- pub fn #ident_set( value: #ty) -> bool {
96- unsafe { #ident_p. set( value) }
97- }
98- } ;
99-
100- expanded. into ( )
101- }
102-
103- /// Wraps a `static mut` variable as a reactive signal (similar to a property)
104- /// with getter and setter functions.
105- ///
106- /// The `signal!` macro transforms a `static mut` variable into a `reactive_cache::Signal`,
107- /// and automatically generates:
108- /// 1. A `_get()` function that returns a reference to the value, allowing read access.
109- /// - This reference behaves like a normal immutable reference for most purposes.
110- /// 2. A `_set(value)` function to write the value (returns `true` if changed).
111- /// 3. A function with the same name as the variable that directly returns the value
112- /// by dereferencing the underlying variable. This requires the type to implement `Copy`.
113- ///
114- /// # Requirements
115- ///
116- /// - The macro currently supports only `static mut` variables.
117- /// - The variable type must implement `Eq`.
118- ///
119- /// # Examples
120- ///
121- /// ```rust
122- /// use reactive_macros::{ref_signal, signal};
123- ///
124- /// signal!(static mut A: i32 = 10;);
125- ///
126- /// assert_eq!(A(), 10); // returns value directly (requires Copy)
127- /// assert_eq!(*A_get(), 10); // returns a reference to the value
128- /// assert!(A_set(20));
129- /// assert_eq!(A(), 20);
130- /// assert!(!A_set(20)); // No change
131- /// ```
132- ///
133- /// # SAFETY
134- ///
135- /// This macro wraps `static mut` variables internally, so it **is not thread-safe**.
136- /// It should be used only in single-threaded contexts.
137- ///
138- /// # Warning
139- ///
140- /// **Do not set any signal that is part of the same effect chain.**
141- ///
142- /// Effects automatically run whenever one of their dependent signals changes.
143- /// If an effect modifies a signal that it (directly or indirectly) observes,
144- /// it creates a circular dependency. This can lead to:
145- /// - an infinite loop of updates, or
146- /// - conflicting updates that the system cannot resolve.
147- ///
148- /// In the general case, it is impossible to automatically determine whether
149- /// such an effect will ever terminate—this is essentially a version of the
150- /// halting problem. Therefore, you must ensure manually that effects do not
151- /// update signals within their own dependency chain.
152- #[ proc_macro]
153- pub fn signal ( input : TokenStream ) -> TokenStream {
154- let input_clone: proc_macro2:: TokenStream = input. clone ( ) . into ( ) ;
155-
156- let item = parse_macro_input ! ( input as ItemStatic ) ;
157- let ident = & item. ident ;
158- let ty = & item. ty ;
159-
160- let ident_p = format_ident ! ( "_{}" , ident. to_string( ) . to_uppercase( ) ) ;
161- let ident_fn = format_ident ! ( "{}" , ident) ;
162-
163- let expanded = quote ! {
164- ref_signal!( #input_clone) ;
165-
16667 #[ allow( non_snake_case) ]
167- pub fn #ident_fn( ) -> #ty {
168- * unsafe { #ident_p. get( ) }
68+ #vis fn #ident( ) -> & ' static std:: rc:: Rc <reactive_cache:: Signal <#ty>> {
69+ static mut #ident: #lazy_ty = #expr;
70+ unsafe { & * #ident }
16971 }
17072 } ;
17173
@@ -252,12 +154,9 @@ pub fn memo(_attr: TokenStream, item: TokenStream) -> TokenStream {
252154 let expr = quote ! { reactive_cache:: Lazy :: new( || reactive_cache:: Memo :: new( || #block) ) } ;
253155
254156 let expanded = quote ! {
255- static mut #ident: #ty = #expr;
256-
257- #vis #sig
258- where #output_ty: Clone
259- {
260- unsafe { ( * #ident) . get( ) }
157+ #vis #sig {
158+ static mut #ident: #ty = #expr;
159+ unsafe { #ident. get( ) }
261160 }
262161 } ;
263162
@@ -281,7 +180,7 @@ pub fn memo(_attr: TokenStream, item: TokenStream) -> TokenStream {
281180/// # Examples
282181///
283182/// ```rust
284- /// use reactive_macros::{ evaluate, ref_signal} ;
183+ /// use reactive_macros::evaluate;
285184///
286185/// fn print(msg: String) {
287186/// println!("{}", msg);
0 commit comments