1use reactive_graph::{
45 owner::{LocalStorage, StoredValue},
46 traits::{Dispose, WithValue},
47};
48use std::{fmt, rc::Rc, sync::Arc};
49
50pub trait Callable<In: 'static, Out: 'static = ()> {
52 fn try_run(&self, input: In) -> Option<Out>;
56 fn run(&self, input: In) -> Out;
61}
62
63pub struct UnsyncCallback<In: 'static, Out: 'static = ()>(
65 StoredValue<Rc<dyn Fn(In) -> Out>, LocalStorage>,
66);
67
68impl<In> fmt::Debug for UnsyncCallback<In> {
69 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
70 fmt.write_str("Callback")
71 }
72}
73
74impl<In, Out> Copy for UnsyncCallback<In, Out> {}
75
76impl<In, Out> Clone for UnsyncCallback<In, Out> {
77 fn clone(&self) -> Self {
78 *self
79 }
80}
81
82impl<In, Out> Dispose for UnsyncCallback<In, Out> {
83 fn dispose(self) {
84 self.0.dispose();
85 }
86}
87
88impl<In, Out> UnsyncCallback<In, Out> {
89 pub fn new<F>(f: F) -> UnsyncCallback<In, Out>
91 where
92 F: Fn(In) -> Out + 'static,
93 {
94 Self(StoredValue::new_local(Rc::new(f)))
95 }
96
97 #[inline]
99 pub fn matches(&self, other: &Self) -> bool {
100 self.0.with_value(|self_value| {
101 other
102 .0
103 .with_value(|other_value| Rc::ptr_eq(self_value, other_value))
104 })
105 }
106}
107
108impl<In: 'static, Out: 'static> Callable<In, Out> for UnsyncCallback<In, Out> {
109 fn try_run(&self, input: In) -> Option<Out> {
110 self.0.try_with_value(|fun| fun(input))
111 }
112
113 fn run(&self, input: In) -> Out {
114 self.0.with_value(|fun| fun(input))
115 }
116}
117
118macro_rules! impl_unsync_callable_from_fn {
119 ($($arg:ident),*) => {
120 impl<F, $($arg,)* T, Out> From<F> for UnsyncCallback<($($arg,)*), Out>
121 where
122 F: Fn($($arg),*) -> T + 'static,
123 T: Into<Out> + 'static,
124 $($arg: 'static,)*
125 {
126 fn from(f: F) -> Self {
127 paste::paste!(
128 Self::new(move |($([<$arg:lower>],)*)| f($([<$arg:lower>]),*).into())
129 )
130 }
131 }
132 };
133}
134
135impl_unsync_callable_from_fn!();
136impl_unsync_callable_from_fn!(P1);
137impl_unsync_callable_from_fn!(P1, P2);
138impl_unsync_callable_from_fn!(P1, P2, P3);
139impl_unsync_callable_from_fn!(P1, P2, P3, P4);
140impl_unsync_callable_from_fn!(P1, P2, P3, P4, P5);
141impl_unsync_callable_from_fn!(P1, P2, P3, P4, P5, P6);
142impl_unsync_callable_from_fn!(P1, P2, P3, P4, P5, P6, P7);
143impl_unsync_callable_from_fn!(P1, P2, P3, P4, P5, P6, P7, P8);
144impl_unsync_callable_from_fn!(P1, P2, P3, P4, P5, P6, P7, P8, P9);
145impl_unsync_callable_from_fn!(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10);
146impl_unsync_callable_from_fn!(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11);
147impl_unsync_callable_from_fn!(
148 P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12
149);
150
151pub struct Callback<In, Out = ()>(
175 StoredValue<Arc<dyn Fn(In) -> Out + Send + Sync>>,
176)
177where
178 In: 'static,
179 Out: 'static;
180
181impl<In, Out> fmt::Debug for Callback<In, Out> {
182 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
183 fmt.write_str("SyncCallback")
184 }
185}
186
187impl<In, Out> Callable<In, Out> for Callback<In, Out> {
188 fn try_run(&self, input: In) -> Option<Out> {
189 self.0.try_with_value(|fun| fun(input))
190 }
191
192 fn run(&self, input: In) -> Out {
193 self.0.with_value(|f| f(input))
194 }
195}
196
197impl<In, Out> Clone for Callback<In, Out> {
198 fn clone(&self) -> Self {
199 *self
200 }
201}
202
203impl<In, Out> Dispose for Callback<In, Out> {
204 fn dispose(self) {
205 self.0.dispose();
206 }
207}
208
209impl<In, Out> Copy for Callback<In, Out> {}
210
211macro_rules! impl_callable_from_fn {
212 ($($arg:ident),*) => {
213 impl<F, $($arg,)* T, Out> From<F> for Callback<($($arg,)*), Out>
214 where
215 F: Fn($($arg),*) -> T + Send + Sync + 'static,
216 T: Into<Out> + 'static,
217 $($arg: Send + Sync + 'static,)*
218 {
219 fn from(f: F) -> Self {
220 paste::paste!(
221 Self::new(move |($([<$arg:lower>],)*)| f($([<$arg:lower>]),*).into())
222 )
223 }
224 }
225 };
226}
227
228impl_callable_from_fn!();
229impl_callable_from_fn!(P1);
230impl_callable_from_fn!(P1, P2);
231impl_callable_from_fn!(P1, P2, P3);
232impl_callable_from_fn!(P1, P2, P3, P4);
233impl_callable_from_fn!(P1, P2, P3, P4, P5);
234impl_callable_from_fn!(P1, P2, P3, P4, P5, P6);
235impl_callable_from_fn!(P1, P2, P3, P4, P5, P6, P7);
236impl_callable_from_fn!(P1, P2, P3, P4, P5, P6, P7, P8);
237impl_callable_from_fn!(P1, P2, P3, P4, P5, P6, P7, P8, P9);
238impl_callable_from_fn!(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10);
239impl_callable_from_fn!(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11);
240impl_callable_from_fn!(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12);
241
242impl<In: 'static, Out: 'static> Callback<In, Out> {
243 pub fn new<F>(fun: F) -> Self
245 where
246 F: Fn(In) -> Out + Send + Sync + 'static,
247 {
248 Self(StoredValue::new(Arc::new(fun)))
249 }
250
251 #[inline]
253 pub fn matches(&self, other: &Self) -> bool {
254 self.0
255 .try_with_value(|self_value| {
256 other.0.try_with_value(|other_value| {
257 Arc::ptr_eq(self_value, other_value)
258 })
259 })
260 .flatten()
261 .unwrap_or(false)
262 }
263}
264
265#[cfg(test)]
266mod tests {
267 use super::Callable;
268 use crate::callback::{Callback, UnsyncCallback};
269 use reactive_graph::traits::Dispose;
270
271 struct NoClone {}
272
273 #[test]
274 fn clone_callback() {
275 let callback = Callback::new(move |_no_clone: NoClone| NoClone {});
276 let _cloned = callback;
277 }
278
279 #[test]
280 fn clone_unsync_callback() {
281 let callback =
282 UnsyncCallback::new(move |_no_clone: NoClone| NoClone {});
283 let _cloned = callback;
284 }
285
286 #[test]
287 fn runback_from() {
288 let _callback: Callback<(), String> = (|| "test").into();
289 let _callback: Callback<(i32, String), String> =
290 (|num, s| format!("{num} {s}")).into();
291 }
292
293 #[test]
294 fn sync_callback_from() {
295 let _callback: UnsyncCallback<(), String> = (|| "test").into();
296 let _callback: UnsyncCallback<(i32, String), String> =
297 (|num, s| format!("{num} {s}")).into();
298 }
299
300 #[test]
301 fn sync_callback_try_run() {
302 let callback = Callback::new(move |arg| arg);
303 assert_eq!(callback.try_run((0,)), Some((0,)));
304 callback.dispose();
305 assert_eq!(callback.try_run((0,)), None);
306 }
307
308 #[test]
309 fn unsync_callback_try_run() {
310 let callback = UnsyncCallback::new(move |arg| arg);
311 assert_eq!(callback.try_run((0,)), Some((0,)));
312 callback.dispose();
313 assert_eq!(callback.try_run((0,)), None);
314 }
315
316 #[test]
317 fn callback_matches_same() {
318 let callback1 = Callback::new(|x: i32| x * 2);
319 let callback2 = callback1;
320 assert!(callback1.matches(&callback2));
321 }
322
323 #[test]
324 fn callback_matches_different() {
325 let callback1 = Callback::new(|x: i32| x * 2);
326 let callback2 = Callback::new(|x: i32| x + 1);
327 assert!(!callback1.matches(&callback2));
328 }
329
330 #[test]
331 fn unsync_callback_matches_same() {
332 let callback1 = UnsyncCallback::new(|x: i32| x * 2);
333 let callback2 = callback1;
334 assert!(callback1.matches(&callback2));
335 }
336
337 #[test]
338 fn unsync_callback_matches_different() {
339 let callback1 = UnsyncCallback::new(|x: i32| x * 2);
340 let callback2 = UnsyncCallback::new(|x: i32| x + 1);
341 assert!(!callback1.matches(&callback2));
342 }
343}