yew_stdweb/
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 crate::html::ImplicitClone;
8use std::cell::RefCell;
9use std::fmt;
10use std::rc::Rc;
11
12/// Universal callback wrapper.
13/// <aside class="warning">
14/// Use callbacks carefully, because if you call one from the `update` loop
15/// of a `Component` (even from JS) it will delay a message until next.
16/// Callbacks should be used from JS callbacks or `setTimeout` calls.
17/// </aside>
18/// An `Rc` wrapper is used to make it cloneable.
19pub enum Callback<IN> {
20    /// A callback which can be called multiple times
21    Callback(Rc<dyn Fn(IN)>),
22    /// A callback which can only be called once. The callback will panic if it is
23    /// called more than once.
24    CallbackOnce(Rc<CallbackOnce<IN>>),
25}
26
27type CallbackOnce<IN> = RefCell<Option<Box<dyn FnOnce(IN)>>>;
28
29impl<IN, F: Fn(IN) + 'static> From<F> for Callback<IN> {
30    fn from(func: F) -> Self {
31        Callback::Callback(Rc::new(func))
32    }
33}
34
35impl<IN> Clone for Callback<IN> {
36    fn clone(&self) -> Self {
37        match self {
38            Callback::Callback(cb) => Callback::Callback(cb.clone()),
39            Callback::CallbackOnce(cb) => Callback::CallbackOnce(cb.clone()),
40        }
41    }
42}
43
44#[allow(clippy::vtable_address_comparisons)]
45impl<IN> PartialEq for Callback<IN> {
46    fn eq(&self, other: &Callback<IN>) -> bool {
47        match (&self, &other) {
48            (Callback::Callback(cb), Callback::Callback(other_cb)) => Rc::ptr_eq(cb, other_cb),
49            (Callback::CallbackOnce(cb), Callback::CallbackOnce(other_cb)) => {
50                Rc::ptr_eq(cb, other_cb)
51            }
52            _ => false,
53        }
54    }
55}
56
57impl<IN> fmt::Debug for Callback<IN> {
58    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
59        let data = match self {
60            Callback::Callback(_) => "Callback<_>",
61            Callback::CallbackOnce(_) => "CallbackOnce<_>",
62        };
63
64        f.write_str(data)
65    }
66}
67
68impl<IN> Callback<IN> {
69    /// This method calls the callback's function.
70    pub fn emit(&self, value: IN) {
71        match self {
72            Callback::Callback(cb) => cb(value),
73            Callback::CallbackOnce(rc) => {
74                let cb = rc.replace(None);
75                let f = cb.expect("callback in CallbackOnce has already been used");
76                f(value)
77            }
78        };
79    }
80
81    /// Creates a callback from an `FnOnce`. The programmer is responsible for ensuring
82    /// that the callback is only called once. If it is called more than once, the callback
83    /// will panic.
84    pub fn once<F>(func: F) -> Self
85    where
86        F: FnOnce(IN) + 'static,
87    {
88        Callback::CallbackOnce(Rc::new(RefCell::new(Some(Box::new(func)))))
89    }
90
91    /// Creates a "no-op" callback which can be used when it is not suitable to use an
92    /// `Option<Callback>`.
93    pub fn noop() -> Self {
94        Self::from(|_| {})
95    }
96}
97
98impl<IN> Default for Callback<IN> {
99    fn default() -> Self {
100        Self::noop()
101    }
102}
103
104impl<IN: 'static> Callback<IN> {
105    /// Changes the input type of the callback to another.
106    /// Works like the `map` method but in the opposite direction.
107    pub fn reform<F, T>(&self, func: F) -> Callback<T>
108    where
109        F: Fn(T) -> IN + 'static,
110    {
111        let this = self.clone();
112        let func = move |input| {
113            let output = func(input);
114            this.emit(output);
115        };
116        Callback::from(func)
117    }
118}
119
120impl<T> ImplicitClone for Callback<T> {}
121
122#[cfg(test)]
123pub(crate) mod test_util {
124    use super::*;
125    use std::cell::RefCell;
126    use std::future::Future;
127    use std::pin::Pin;
128    use std::task::{Context, Poll, Waker};
129
130    struct CallbackHandle<T> {
131        waker: Option<Waker>,
132        output: Option<T>,
133    }
134
135    impl<T> Default for CallbackHandle<T> {
136        fn default() -> Self {
137            CallbackHandle {
138                waker: None,
139                output: None,
140            }
141        }
142    }
143
144    pub(crate) struct CallbackFuture<T>(Rc<RefCell<CallbackHandle<T>>>);
145
146    impl<T> Clone for CallbackFuture<T> {
147        fn clone(&self) -> Self {
148            Self(self.0.clone())
149        }
150    }
151
152    impl<T> Default for CallbackFuture<T> {
153        fn default() -> Self {
154            Self(Rc::default())
155        }
156    }
157
158    impl<T: 'static> From<CallbackFuture<T>> for Callback<T> {
159        fn from(callback: CallbackFuture<T>) -> Callback<T> {
160            Callback::from(move |r| callback.finish(r))
161        }
162    }
163
164    impl<T> Future for CallbackFuture<T> {
165        type Output = T;
166        fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
167            if let Some(output) = self.ready() {
168                Poll::Ready(output)
169            } else {
170                let handle = &self.0;
171                handle.borrow_mut().waker = Some(cx.waker().clone());
172                Poll::Pending
173            }
174        }
175    }
176
177    impl<T> CallbackFuture<T> {
178        pub fn ready(&self) -> Option<T> {
179            self.0.borrow_mut().output.take()
180        }
181
182        fn finish(&self, output: T) {
183            self.0.borrow_mut().output = Some(output);
184            if let Some(waker) = self.0.borrow_mut().waker.take() {
185                waker.wake();
186            }
187        }
188    }
189}