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}