reactive_signals/macros.rs
1/// The `signal!` macro is used to create signals of all types. It automatically detects
2/// the type of the provided data or function and if it implements [PartialEq] or [Hash](std::hash::Hash)
3///
4/// Arguments:
5/// - `scope`: mandatory. The [Scope](crate::Scope) to use when creating the [Signal](crate::Signal)
6/// - `clone:`: optional. A space-separated list of data to clone and provide to the function.
7/// - `server` | `client`: optional. Whether the signal should run only on the server or the client.
8/// - `inner`: the data or function the signal handles.
9///
10/// Examples:
11///
12/// - [reactive data signals](Self#Example_of_reactive_data_signals)
13/// - [functional reactive signals](Self#Example_of_functional_reactive_signals)
14/// - [async functional reactive signals](Self#Example_of_async_functional_reactive_signals)
15///
16/// # Example of reactive data signals
17///
18/// ```rust
19/// # use reactive_signals::types::*;
20/// use reactive_signals::{Scope, Signal, signal, runtimes::ClientRuntime};
21///
22/// let sc = ClientRuntime::new_root_scope();
23///
24/// // Create a string signal. Since string implements PartialEq the
25/// // signal will only notify it's subscribers if it's value change.
26/// let string_sig = signal!(sc, "hi".to_string());
27///
28/// struct MyNoEqData;
29/// // Create a signal from data that doesn't implement equality.
30/// // it will always notify the subscribers when it changes.
31/// let no_eq_sig = signal!(sc, MyNoEqData);
32/// ```
33///
34/// # Example of functional reactive signals
35///
36/// ```rust
37/// # use reactive_signals::types::*;
38/// # use std::cell::RefCell;
39/// # use std::rc::Rc;
40/// #
41/// use reactive_signals::{Scope, Signal, signal, runtimes::ClientRuntime};
42///
43/// let sc = ClientRuntime::new_root_scope();
44///
45/// struct MyNoEqData;
46///
47/// let count_sig = signal!(sc, 4);
48///
49/// // create a simple functional signal
50/// let func_sig = signal!(sc, move || count_sig.get() + 1);
51///
52/// ///////////// the clone argument /////////////
53///
54/// let counter = Rc::new(RefCell::new(0));
55///
56/// // using the clone argument you can provide a space-separated
57/// // list of data to clone and provide to the function.
58/// let counter_upd = signal!(sc, clone: counter, move || *counter.borrow_mut() += 1);
59///
60/// // the above is equivalent to:
61/// let counter_upd = {
62/// let counter = counter.clone();
63/// signal!(sc, move || *counter.borrow_mut() += 1)
64/// };
65///
66/// ///////////// client and server only signals /////////////
67///
68/// // create a signal that only runs on the server
69/// let server_func = signal!(sc, server, move || count_sig.get() + 1);
70///
71/// // create a signal that only runs on the client
72/// let client_func = signal!(sc, client, move || count_sig.get() + 1);
73/// ```
74///
75/// # Example of async functional reactive signals
76///
77/// Note that this has not yet been implemented and the exact details of the API
78/// has not been ironed out.
79///
80/// ```rust ignore
81/// // creating an async closure/function is basically the same as normal one
82/// let remote_count = signal!(sc, move async || {
83/// // some async stuff
84/// fetch_usize_count().await
85/// });
86///
87/// // an async signal works just like any other signal, it just waits until
88/// // the async closure finishes before notifiying subscribers.
89/// signal!(sc, move || println!("Remote count is now: {}", remote_count.get()));
90///
91/// let async_timer = signal!(sc, 500 ms, move async |&mut interval| {
92/// // runs after 500 ms
93/// // you can change the interval or stop it by:
94/// *interval = None;
95/// });
96/// ```
97#[macro_export]
98macro_rules! signal {
99 ($scope:ident, $inner:expr) => {{
100 #[allow(unused_imports)]
101 use $crate::{EqFuncKind, TrueFuncKind, EqDataKind, TrueDataKind, HashEqDataKind};
102 match ($scope, $inner) {
103 tuple => (&&tuple).signal_kind().new(tuple),
104 }
105 }};
106 ($scope:ident, server, $inner:expr) => {{
107 #[allow(unused_imports)]
108 use $crate::{ServerEqFuncKind, ServerTrueFuncKind};
109 match ($scope, $inner) {
110 tuple => (&&tuple).server_kind().new(tuple),
111 }
112 }};
113 ($scope:ident, server, clone: $($clone:ident) +, $inner:expr) => {{
114 $(let $clone = $clone.clone();)*
115 #[allow(unused_imports)]
116 use $crate::{ServerEqFuncKind, ServerTrueFuncKind};
117 match ($scope, $inner) {
118 tuple => (&&tuple).server_kind().new(tuple),
119 }
120 }};
121
122 ($scope:ident, client, $inner:expr) => {{
123 #[allow(unused_imports)]
124 use $crate::{ClientEqFuncKind, ClientTrueFuncKind};
125 match ($scope, $inner) {
126 tuple => (&&tuple).client_kind().new(tuple),
127 }
128 }};
129 ($scope:ident, client, clone: $($clone:ident) +, $inner:expr) => {{
130 $(let $clone = $clone.clone();)*
131 #[allow(unused_imports)]
132 use $crate::{ClientEqFuncKind, ClientTrueFuncKind};
133 match ($scope, $inner) {
134 tuple => (&&tuple).client_kind().new(tuple),
135 }
136 }};
137 ($scope:ident, clone: $($clone:ident) +, $data:expr) => {{
138 $(let $clone = $clone.clone();)*
139 #[allow(unused_imports)]
140 use $crate::{EqFuncKind, TrueFuncKind};
141 match ($scope, $data) {
142 tuple => (&&tuple).signal_kind().new(tuple),
143 }
144 }};
145}
146
147#[test]
148fn test() {
149 use crate::runtimes::ServerRuntime;
150 let sx = ServerRuntime::new_root_scope();
151 let _sig = signal!(sx, 32);
152 // assert!(!sig.eq);
153
154 let x = 5;
155 let _sig = signal!(sx, x);
156 // assert!(!sig.eq);
157
158 let _sig = signal!(sx, || 32);
159 // assert!(sig.eq);
160
161 #[derive(Clone)]
162 struct NonEq;
163 let _sig = signal!(sx, || NonEq);
164 // assert!(!sig.eq);
165
166 let ne = NonEq;
167 let _sig = signal!(sx, move || ne.clone());
168 // assert!(!sig.eq);
169
170 let ne = NonEq;
171 let _sig = signal!(sx, clone: ne, move || ne.clone());
172
173 // assert!(!sig.eq);
174
175 let _sit = signal!(sx, server, move || ne.clone());
176
177 let srv = signal!(sx, server, move || 1);
178 assert_eq!(srv.opt_get(), Some(1));
179
180 let clnt = signal!(sx, client, move || 1);
181
182 assert_eq!(clnt.opt_get(), None);
183}