1use std::fmt;
8use std::rc::Rc;
9
10use crate::html::ImplicitClone;
11
12macro_rules! generate_callback_impls {
13 ($callback:ident, $in_ty:ty, $out_var:ident => $out_val:expr) => {
14 impl<IN, OUT, F: Fn($in_ty) -> OUT + 'static> From<F> for $callback<IN, OUT> {
15 fn from(func: F) -> Self {
16 $callback { cb: Rc::new(func) }
17 }
18 }
19
20 impl<IN, OUT> Clone for $callback<IN, OUT> {
21 fn clone(&self) -> Self {
22 Self {
23 cb: self.cb.clone(),
24 }
25 }
26 }
27
28 #[allow(ambiguous_wide_pointer_comparisons)]
31 impl<IN, OUT> PartialEq for $callback<IN, OUT> {
32 fn eq(&self, other: &$callback<IN, OUT>) -> bool {
33 let ($callback { cb }, $callback { cb: rhs_cb }) = (self, other);
34 Rc::ptr_eq(cb, rhs_cb)
35 }
36 }
37
38 impl<IN, OUT> fmt::Debug for $callback<IN, OUT> {
39 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
40 write!(f, "$callback<_>")
41 }
42 }
43
44 impl<IN, OUT> $callback<IN, OUT> {
45 pub fn emit(&self, value: $in_ty) -> OUT {
47 (*self.cb)(value)
48 }
49 }
50
51 impl<IN> $callback<IN> {
52 pub fn noop() -> Self {
55 Self::from(|_: $in_ty| ())
56 }
57 }
58
59 impl<IN> Default for $callback<IN> {
60 fn default() -> Self {
61 Self::noop()
62 }
63 }
64
65 impl<IN: 'static, OUT: 'static> $callback<IN, OUT> {
66 pub fn reform<F, T>(&self, func: F) -> Callback<T, OUT>
70 where
71 F: Fn(T) -> IN + 'static,
72 {
73 let this = self.clone();
74 let func = move |input: T| {
75 #[allow(unused_mut)]
76 let mut $out_var = func(input);
77 this.emit($out_val)
78 };
79 func.into()
80 }
81
82 pub fn reform_ref<F, T>(&self, func: F) -> CallbackRef<T, OUT>
86 where
87 F: Fn(&T) -> $in_ty + 'static,
88 {
89 let this = self.clone();
90 let func = move |input: &T| {
91 #[allow(unused_mut)]
92 let mut $out_var = func(input);
93 this.emit($out_val)
94 };
95 func.into()
96 }
97
98 pub fn reform_ref_mut<F, T>(&self, func: F) -> CallbackRefMut<T, OUT>
102 where
103 F: Fn(&mut T) -> $in_ty + 'static,
104 {
105 let this = self.clone();
106 let func = move |input: &mut T| {
107 #[allow(unused_mut)]
108 let mut $out_var = func(input);
109 this.emit($out_val)
110 };
111 func.into()
112 }
113
114 pub fn filter_reform<F, T>(&self, func: F) -> Callback<T, Option<OUT>>
119 where
120 F: Fn(T) -> Option<IN> + 'static,
121 {
122 let this = self.clone();
123 let func = move |input: T| {
124 func(input).map(
125 #[allow(unused_mut)]
126 |mut $out_var| this.emit($out_val),
127 )
128 };
129 func.into()
130 }
131
132 pub fn filter_reform_ref<F, T>(&self, func: F) -> CallbackRef<T, Option<OUT>>
137 where
138 F: Fn(&T) -> Option<$in_ty> + 'static,
139 {
140 let this = self.clone();
141 let func = move |input: &T| {
142 func(input).map(
143 #[allow(unused_mut)]
144 |mut $out_var| this.emit($out_val),
145 )
146 };
147 func.into()
148 }
149
150 pub fn filter_reform_ref_mut<F, T>(&self, func: F) -> CallbackRefMut<T, Option<OUT>>
155 where
156 F: Fn(&mut T) -> Option<$in_ty> + 'static,
157 {
158 let this = self.clone();
159 let func = move |input: &mut T| {
160 func(input).map(
161 #[allow(unused_mut)]
162 |mut $out_var| this.emit($out_val),
163 )
164 };
165 func.into()
166 }
167 }
168
169 impl<IN, OUT> ImplicitClone for $callback<IN, OUT> {}
170 };
171}
172
173pub struct Callback<IN, OUT = ()> {
177 pub(crate) cb: Rc<dyn Fn(IN) -> OUT>,
179}
180
181generate_callback_impls!(Callback, IN, output => output);
182
183pub struct CallbackRef<IN, OUT = ()> {
187 pub(crate) cb: Rc<dyn Fn(&IN) -> OUT>,
189}
190
191generate_callback_impls!(CallbackRef, &IN, output => #[allow(clippy::needless_borrow)] &output);
192
193pub struct CallbackRefMut<IN, OUT = ()> {
197 pub(crate) cb: Rc<dyn Fn(&mut IN) -> OUT>,
199}
200
201generate_callback_impls!(CallbackRefMut, &mut IN, output => &mut output);
202
203#[cfg(test)]
204mod test {
205 use std::sync::Mutex;
206
207 use super::*;
208
209 fn emit<T, I, R: 'static + Clone, F, OUT>(values: I, f: F) -> Vec<R>
211 where
212 I: IntoIterator<Item = T>,
213 F: FnOnce(Callback<R, ()>) -> Callback<T, OUT>,
214 {
215 let result = Rc::new(Mutex::new(Vec::new()));
216 let cb_result = result.clone();
217 let cb = f(Callback::<R, ()>::from(move |v| {
218 cb_result.lock().unwrap().push(v);
219 }));
220 for value in values {
221 cb.emit(value);
222 }
223 let x = result.lock().unwrap().clone();
224 x
225 }
226
227 #[test]
228 fn test_callback() {
229 assert_eq!(*emit([true, false], |cb| cb), vec![true, false]);
230 }
231
232 #[test]
233 fn test_reform() {
234 assert_eq!(
235 *emit([true, false], |cb| cb.reform(|v: bool| !v)),
236 vec![false, true]
237 );
238 }
239
240 #[test]
241 fn test_filter_reform() {
242 assert_eq!(
243 *emit([1, 2, 3], |cb| cb.filter_reform(|v| match v {
244 1 => Some(true),
245 2 => Some(false),
246 _ => None,
247 })),
248 vec![true, false]
249 );
250 }
251
252 #[test]
253 fn test_ref() {
254 let callback: CallbackRef<usize, usize> = CallbackRef::from(|x: &usize| *x);
255 assert_eq!(callback.emit(&42), 42);
256 }
257
258 #[test]
259 fn test_ref_mut() {
260 let callback: CallbackRefMut<usize, ()> = CallbackRefMut::from(|x: &mut usize| *x = 42);
261 let mut value: usize = 0;
262 callback.emit(&mut value);
263 assert_eq!(value, 42);
264 }
265
266 #[test]
267 fn test_reform_ref() {
268 let callback: Callback<usize, usize> = Callback::from(|x: usize| x + 1);
269 let reformed: CallbackRef<usize, usize> = callback.reform_ref(|x: &usize| *x + 2);
270 assert_eq!(reformed.emit(&42), 45);
271 }
272
273 #[test]
274 fn test_reform_ref_mut() {
275 let callback: CallbackRefMut<usize, ()> = CallbackRefMut::from(|x: &mut usize| *x += 1);
276 let reformed: CallbackRefMut<usize, ()> = callback.reform_ref_mut(|x: &mut usize| {
277 *x += 2;
278 x
279 });
280 let mut value: usize = 42;
281 reformed.emit(&mut value);
282 assert_eq!(value, 45);
283 }
284
285 #[test]
286 fn test_filter_reform_ref() {
287 let callback: Callback<usize, usize> = Callback::from(|x: usize| x + 1);
288 let reformed: CallbackRef<usize, Option<usize>> =
289 callback.filter_reform_ref(|x: &usize| Some(*x + 2));
290 assert_eq!(reformed.emit(&42), Some(45));
291 }
292
293 #[test]
294 fn test_filter_reform_ref_mut() {
295 let callback: CallbackRefMut<usize, ()> = CallbackRefMut::from(|x: &mut usize| *x += 1);
296 let reformed: CallbackRefMut<usize, Option<()>> =
297 callback.filter_reform_ref_mut(|x: &mut usize| {
298 *x += 2;
299 Some(x)
300 });
301 let mut value: usize = 42;
302 reformed.emit(&mut value).expect("is some");
303 assert_eq!(value, 45);
304 }
305}