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