Skip to main content
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        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            /// This method calls the callback's function.
45            pub fn emit(&self, value: $in_ty) -> OUT {
46                (*self.cb)(value)
47            }
48        }
49
50        impl<IN> $callback<IN> {
51            /// Creates a "no-op" callback which can be used when it is not suitable to use an
52            /// `Option<$callback>`.
53            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            /// Creates a new [`Callback`] from another callback and a function.
66            ///
67            /// That when emitted will call that function and will emit the original callback
68            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            /// Creates a new [`CallbackRef`] from another callback and a function.
82            ///
83            /// That when emitted will call that function and will emit the original callback
84            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            /// Creates a new [`CallbackRefMut`] from another callback and a function.
98            ///
99            /// That when emitted will call that function and will emit the original callback
100            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            /// Creates a new [`Callback`] from another callback and a function.
114            ///
115            /// When emitted will call the function and, only if it returns `Some(value)`, will emit
116            /// `value` to the original callback.
117            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            /// Creates a new [`CallbackRef`] from another callback and a function.
132            ///
133            /// When emitted will call the function and, only if it returns `Some(value)`, will emit
134            /// `value` to the original callback.
135            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            /// Creates a new [`CallbackRefMut`] from another callback and a function.
150            ///
151            /// When emitted will call the function and, only if it returns `Some(value)`, will emit
152            /// `value` to the original callback.
153            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
172/// Universal callback wrapper.
173///
174/// An `Rc` wrapper is used to make it cloneable.
175pub struct Callback<IN, OUT = ()> {
176    /// A callback which can be called multiple times
177    pub(crate) cb: Rc<dyn Fn(IN) -> OUT>,
178}
179
180generate_callback_impls!(Callback, IN, output => output);
181
182/// Universal callback wrapper with reference in argument.
183///
184/// An `Rc` wrapper is used to make it cloneable.
185pub struct CallbackRef<IN, OUT = ()> {
186    /// A callback which can be called multiple times
187    pub(crate) cb: Rc<dyn Fn(&IN) -> OUT>,
188}
189
190generate_callback_impls!(CallbackRef, &IN, output => #[allow(clippy::needless_borrow)] &output);
191
192/// Universal callback wrapper with mutable reference in argument.
193///
194/// An `Rc` wrapper is used to make it cloneable.
195pub struct CallbackRefMut<IN, OUT = ()> {
196    /// A callback which can be called multiple times
197    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    /// emit the callback with the provided value
209    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}