This is unreleased documentation for Yew Next version.
For up-to-date documentation, see the latest version on docs.rs.

yew/
callback.rs

1//! This module contains data types for interacting with `Scope`s.
2//!
3//! ## Relevant examples
4//! - [Counter](https://github.com/yewstack/yew/tree/master/examples/counter)
5//! - [Timer](https://github.com/yewstack/yew/tree/master/examples/timer)
6
7use 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        // We are okay with comparisons from different compilation units to result in false
29        // not-equal results. This should only lead in the worst-case to some unneeded re-renders.
30        #[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            /// This method calls the callback's function.
46            pub fn emit(&self, value: $in_ty) -> OUT {
47                (*self.cb)(value)
48            }
49        }
50
51        impl<IN> $callback<IN> {
52            /// Creates a "no-op" callback which can be used when it is not suitable to use an
53            /// `Option<$callback>`.
54            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            /// Creates a new [`Callback`] from another callback and a function.
67            ///
68            /// That when emitted will call that function and will emit the original callback
69            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            /// Creates a new [`CallbackRef`] from another callback and a function.
83            ///
84            /// That when emitted will call that function and will emit the original callback
85            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            /// Creates a new [`CallbackRefMut`] from another callback and a function.
99            ///
100            /// That when emitted will call that function and will emit the original callback
101            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            /// Creates a new [`Callback`] from another callback and a function.
115            ///
116            /// When emitted will call the function and, only if it returns `Some(value)`, will emit
117            /// `value` to the original callback.
118            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            /// Creates a new [`CallbackRef`] from another callback and a function.
133            ///
134            /// When emitted will call the function and, only if it returns `Some(value)`, will emit
135            /// `value` to the original callback.
136            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            /// Creates a new [`CallbackRefMut`] from another callback and a function.
151            ///
152            /// When emitted will call the function and, only if it returns `Some(value)`, will emit
153            /// `value` to the original callback.
154            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
173/// Universal callback wrapper.
174///
175/// An `Rc` wrapper is used to make it cloneable.
176pub struct Callback<IN, OUT = ()> {
177    /// A callback which can be called multiple times
178    pub(crate) cb: Rc<dyn Fn(IN) -> OUT>,
179}
180
181generate_callback_impls!(Callback, IN, output => output);
182
183/// Universal callback wrapper with reference in argument.
184///
185/// An `Rc` wrapper is used to make it cloneable.
186pub struct CallbackRef<IN, OUT = ()> {
187    /// A callback which can be called multiple times
188    pub(crate) cb: Rc<dyn Fn(&IN) -> OUT>,
189}
190
191generate_callback_impls!(CallbackRef, &IN, output => #[allow(clippy::needless_borrow)] &output);
192
193/// Universal callback wrapper with mutable reference in argument.
194///
195/// An `Rc` wrapper is used to make it cloneable.
196pub struct CallbackRefMut<IN, OUT = ()> {
197    /// A callback which can be called multiple times
198    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    /// emit the callback with the provided value
210    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}