1use crate::{
15 owner::{LocalStorage, StoredValue},
16 traits::{Dispose, WithValue},
17 IntoReactiveValue,
18};
19use std::{fmt, rc::Rc, sync::Arc};
20
21pub trait Callable<In: 'static, Out: 'static = ()> {
23 fn try_run(&self, input: In) -> Option<Out>;
27 fn run(&self, input: In) -> Out;
32}
33
34pub struct UnsyncCallback<In: 'static, Out: 'static = ()>(
45 StoredValue<Rc<dyn Fn(In) -> Out>, LocalStorage>,
46);
47
48impl<In> fmt::Debug for UnsyncCallback<In> {
49 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
50 fmt.write_str("Callback")
51 }
52}
53
54impl<In, Out> Copy for UnsyncCallback<In, Out> {}
55
56impl<In, Out> Clone for UnsyncCallback<In, Out> {
57 fn clone(&self) -> Self {
58 *self
59 }
60}
61
62impl<In, Out> Dispose for UnsyncCallback<In, Out> {
63 fn dispose(self) {
64 self.0.dispose();
65 }
66}
67
68impl<In, Out> UnsyncCallback<In, Out> {
69 pub fn new<F>(f: F) -> UnsyncCallback<In, Out>
71 where
72 F: Fn(In) -> Out + 'static,
73 {
74 Self(StoredValue::new_local(Rc::new(f)))
75 }
76
77 #[inline]
79 pub fn matches(&self, other: &Self) -> bool {
80 self.0.with_value(|self_value| {
81 other
82 .0
83 .with_value(|other_value| Rc::ptr_eq(self_value, other_value))
84 })
85 }
86}
87
88impl<In: 'static, Out: 'static> Callable<In, Out> for UnsyncCallback<In, Out> {
89 fn try_run(&self, input: In) -> Option<Out> {
90 self.0.try_with_value(|fun| fun(input))
91 }
92
93 fn run(&self, input: In) -> Out {
94 self.0.with_value(|fun| fun(input))
95 }
96}
97
98macro_rules! impl_unsync_callable_from_fn {
99 ($($arg:ident),*) => {
100 impl<F, $($arg,)* T, Out> From<F> for UnsyncCallback<($($arg,)*), Out>
101 where
102 F: Fn($($arg),*) -> T + 'static,
103 T: Into<Out> + 'static,
104 $($arg: 'static,)*
105 {
106 fn from(f: F) -> Self {
107 paste::paste!(
108 Self::new(move |($([<$arg:lower>],)*)| f($([<$arg:lower>]),*).into())
109 )
110 }
111 }
112 };
113}
114
115impl_unsync_callable_from_fn!();
116impl_unsync_callable_from_fn!(P1);
117impl_unsync_callable_from_fn!(P1, P2);
118impl_unsync_callable_from_fn!(P1, P2, P3);
119impl_unsync_callable_from_fn!(P1, P2, P3, P4);
120impl_unsync_callable_from_fn!(P1, P2, P3, P4, P5);
121impl_unsync_callable_from_fn!(P1, P2, P3, P4, P5, P6);
122impl_unsync_callable_from_fn!(P1, P2, P3, P4, P5, P6, P7);
123impl_unsync_callable_from_fn!(P1, P2, P3, P4, P5, P6, P7, P8);
124impl_unsync_callable_from_fn!(P1, P2, P3, P4, P5, P6, P7, P8, P9);
125impl_unsync_callable_from_fn!(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10);
126impl_unsync_callable_from_fn!(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11);
127impl_unsync_callable_from_fn!(
128 P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12
129);
130
131pub struct Callback<In, Out = ()>(
142 StoredValue<Arc<dyn Fn(In) -> Out + Send + Sync>>,
143)
144where
145 In: 'static,
146 Out: 'static;
147
148impl<In, Out> fmt::Debug for Callback<In, Out> {
149 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
150 fmt.write_str("SyncCallback")
151 }
152}
153
154impl<In, Out> Callable<In, Out> for Callback<In, Out> {
155 fn try_run(&self, input: In) -> Option<Out> {
156 self.0.try_with_value(|fun| fun(input))
157 }
158
159 fn run(&self, input: In) -> Out {
160 self.0.with_value(|f| f(input))
161 }
162}
163
164impl<In, Out> Clone for Callback<In, Out> {
165 fn clone(&self) -> Self {
166 *self
167 }
168}
169
170impl<In, Out> Dispose for Callback<In, Out> {
171 fn dispose(self) {
172 self.0.dispose();
173 }
174}
175
176impl<In, Out> Copy for Callback<In, Out> {}
177
178macro_rules! impl_callable_from_fn {
179 ($($arg:ident),*) => {
180 impl<F, $($arg,)* T, Out> From<F> for Callback<($($arg,)*), Out>
181 where
182 F: Fn($($arg),*) -> T + Send + Sync + 'static,
183 T: Into<Out> + 'static,
184 $($arg: Send + Sync + 'static,)*
185 {
186 fn from(f: F) -> Self {
187 paste::paste!(
188 Self::new(move |($([<$arg:lower>],)*)| f($([<$arg:lower>]),*).into())
189 )
190 }
191 }
192 };
193}
194
195impl_callable_from_fn!();
196impl_callable_from_fn!(P1);
197impl_callable_from_fn!(P1, P2);
198impl_callable_from_fn!(P1, P2, P3);
199impl_callable_from_fn!(P1, P2, P3, P4);
200impl_callable_from_fn!(P1, P2, P3, P4, P5);
201impl_callable_from_fn!(P1, P2, P3, P4, P5, P6);
202impl_callable_from_fn!(P1, P2, P3, P4, P5, P6, P7);
203impl_callable_from_fn!(P1, P2, P3, P4, P5, P6, P7, P8);
204impl_callable_from_fn!(P1, P2, P3, P4, P5, P6, P7, P8, P9);
205impl_callable_from_fn!(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10);
206impl_callable_from_fn!(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11);
207impl_callable_from_fn!(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12);
208
209impl<In: 'static, Out: 'static> Callback<In, Out> {
210 #[track_caller]
212 pub fn new<F>(fun: F) -> Self
213 where
214 F: Fn(In) -> Out + Send + Sync + 'static,
215 {
216 Self(StoredValue::new(Arc::new(fun)))
217 }
218
219 #[inline]
221 pub fn matches(&self, other: &Self) -> bool {
222 self.0
223 .try_with_value(|self_value| {
224 other.0.try_with_value(|other_value| {
225 Arc::ptr_eq(self_value, other_value)
226 })
227 })
228 .flatten()
229 .unwrap_or(false)
230 }
231}
232
233#[doc(hidden)]
234pub struct __IntoReactiveValueMarkerCallbackSingleParam;
235
236#[doc(hidden)]
237pub struct __IntoReactiveValueMarkerCallbackStrOutputToString;
238
239impl<I, O, F>
240 IntoReactiveValue<
241 Callback<I, O>,
242 __IntoReactiveValueMarkerCallbackSingleParam,
243 > for F
244where
245 F: Fn(I) -> O + Send + Sync + 'static,
246{
247 #[track_caller]
248 fn into_reactive_value(self) -> Callback<I, O> {
249 Callback::new(self)
250 }
251}
252
253impl<I, O, F>
254 IntoReactiveValue<
255 UnsyncCallback<I, O>,
256 __IntoReactiveValueMarkerCallbackSingleParam,
257 > for F
258where
259 F: Fn(I) -> O + 'static,
260{
261 #[track_caller]
262 fn into_reactive_value(self) -> UnsyncCallback<I, O> {
263 UnsyncCallback::new(self)
264 }
265}
266
267impl<I, F>
268 IntoReactiveValue<
269 Callback<I, String>,
270 __IntoReactiveValueMarkerCallbackStrOutputToString,
271 > for F
272where
273 F: Fn(I) -> &'static str + Send + Sync + 'static,
274{
275 #[track_caller]
276 fn into_reactive_value(self) -> Callback<I, String> {
277 Callback::new(move |i| self(i).to_string())
278 }
279}
280
281impl<I, F>
282 IntoReactiveValue<
283 UnsyncCallback<I, String>,
284 __IntoReactiveValueMarkerCallbackStrOutputToString,
285 > for F
286where
287 F: Fn(I) -> &'static str + 'static,
288{
289 #[track_caller]
290 fn into_reactive_value(self) -> UnsyncCallback<I, String> {
291 UnsyncCallback::new(move |i| self(i).to_string())
292 }
293}
294
295#[cfg(test)]
296mod tests {
297 use super::Callable;
298 use crate::{
299 callback::{Callback, UnsyncCallback},
300 owner::Owner,
301 traits::Dispose,
302 IntoReactiveValue,
303 };
304
305 struct NoClone {}
306
307 #[test]
308 fn clone_callback() {
309 let owner = Owner::new();
310 owner.set();
311
312 let callback = Callback::new(move |_no_clone: NoClone| NoClone {});
313 let _cloned = callback;
314 }
315
316 #[test]
317 fn clone_unsync_callback() {
318 let owner = Owner::new();
319 owner.set();
320
321 let callback =
322 UnsyncCallback::new(move |_no_clone: NoClone| NoClone {});
323 let _cloned = callback;
324 }
325
326 #[test]
327 fn runback_from() {
328 let owner = Owner::new();
329 owner.set();
330
331 let _callback: Callback<(), String> = (|| "test").into();
332 let _callback: Callback<(i32, String), String> =
333 (|num, s| format!("{num} {s}")).into();
334 let _callback: Callback<usize, &'static str> =
336 (|_usize| "test").into_reactive_value();
337 let _callback: Callback<usize, String> =
338 (|_usize| "test").into_reactive_value();
339 }
340
341 #[test]
342 fn sync_callback_from() {
343 let owner = Owner::new();
344 owner.set();
345
346 let _callback: UnsyncCallback<(), String> = (|| "test").into();
347 let _callback: UnsyncCallback<(i32, String), String> =
348 (|num, s| format!("{num} {s}")).into();
349 let _callback: UnsyncCallback<usize, &'static str> =
351 (|_usize| "test").into_reactive_value();
352 let _callback: UnsyncCallback<usize, String> =
353 (|_usize| "test").into_reactive_value();
354 }
355
356 #[test]
357 fn sync_callback_try_run() {
358 let owner = Owner::new();
359 owner.set();
360
361 let callback = Callback::new(move |arg| arg);
362 assert_eq!(callback.try_run((0,)), Some((0,)));
363 callback.dispose();
364 assert_eq!(callback.try_run((0,)), None);
365 }
366
367 #[test]
368 fn unsync_callback_try_run() {
369 let owner = Owner::new();
370 owner.set();
371
372 let callback = UnsyncCallback::new(move |arg| arg);
373 assert_eq!(callback.try_run((0,)), Some((0,)));
374 callback.dispose();
375 assert_eq!(callback.try_run((0,)), None);
376 }
377
378 #[test]
379 fn callback_matches_same() {
380 let owner = Owner::new();
381 owner.set();
382
383 let callback1 = Callback::new(|x: i32| x * 2);
384 let callback2 = callback1;
385 assert!(callback1.matches(&callback2));
386 }
387
388 #[test]
389 fn callback_matches_different() {
390 let owner = Owner::new();
391 owner.set();
392
393 let callback1 = Callback::new(|x: i32| x * 2);
394 let callback2 = Callback::new(|x: i32| x + 1);
395 assert!(!callback1.matches(&callback2));
396 }
397
398 #[test]
399 fn unsync_callback_matches_same() {
400 let owner = Owner::new();
401 owner.set();
402
403 let callback1 = UnsyncCallback::new(|x: i32| x * 2);
404 let callback2 = callback1;
405 assert!(callback1.matches(&callback2));
406 }
407
408 #[test]
409 fn unsync_callback_matches_different() {
410 let owner = Owner::new();
411 owner.set();
412
413 let callback1 = UnsyncCallback::new(|x: i32| x * 2);
414 let callback2 = UnsyncCallback::new(|x: i32| x + 1);
415 assert!(!callback1.matches(&callback2));
416 }
417}