sauron_core/vdom/attribute/
callback.rs

1//! Callbacks contains function that can be called at a later time.
2//! This is used in containing an event listener attached to an DOM element.
3use std::{any::TypeId, cell::RefCell, fmt, rc::Rc};
4
5/// A generic sized representation of a function that can be
6/// attached to a Node. The callback will essentially be owned by the element
7///
8/// Limitations:
9/// The callback takes an Fn instead of FnMut,
10/// therefore it can not mutate the environment variables
11///
12/// In effect callbacks attached to DOM events are limited
13/// to only passing an OUT to the program and not complex statements.
14///
15/// Note: It would have been nice to have the inner value be
16/// `Rc<FnMut(IN) -> OUT> +'a`
17/// but there are a lot of issues that this becomes infeasible:
18///  1 - wasm_bindgen::Closure requires that 'static references to the closure
19///  2 - Accessing `Rc::get_mut` requires that there is no other `Rc` or `Weak` references
20///     else where.
21///         - We could be iterating on the elements for recursively setting the attributes
22///             which is not allowed to have recursive mutable iteration
23///         - Attributes of the same name are merged therefore cloning the attributes, hence the
24///         callback is necessary.
25///
26pub struct Callback<IN, OUT> {
27    /// the function to be executed
28    func: Rc<RefCell<dyn FnMut(IN) -> OUT>>,
29    /// the type_id of the function
30    func_type_id: TypeId,
31    /// the type type_id of the event this callback will be attached to
32    event_type_id: TypeId,
33    /// the type_id of the return type of this callback when executed.
34    msg_type_id: TypeId,
35}
36
37impl<IN, F, OUT> From<F> for Callback<IN, OUT>
38where
39    F: FnMut(IN) -> OUT + 'static,
40    OUT: 'static,
41    IN: 'static,
42{
43    fn from(func: F) -> Self {
44        Self {
45            func: Rc::new(RefCell::new(func)),
46            func_type_id: TypeId::of::<F>(),
47            event_type_id: TypeId::of::<IN>(),
48            msg_type_id: TypeId::of::<OUT>(),
49        }
50    }
51}
52
53/// Note:
54/// using the #[derive(Debug)] needs IN and OUT to also be Debug
55///
56/// The reason this is manually implemented is, so that IN and OUT
57/// doesn't need to be Debug as it is part of the Callback objects and are not shown.
58impl<IN, OUT> fmt::Debug for Callback<IN, OUT> {
59    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
60        write!(
61            f,
62            "in: {:?}, out: {:?}, func: {:?}",
63            self.event_type_id, self.msg_type_id, self.func_type_id
64        )
65    }
66}
67
68impl<IN, OUT> Callback<IN, OUT>
69where
70    IN: 'static,
71    OUT: 'static,
72{
73    /// This method calls the actual callback.
74    pub fn emit(&self, input: IN) -> OUT {
75        (self.func.borrow_mut())(input)
76    }
77
78    /// map this Callback msg such that `Callback<IN, OUT>` becomes `Callback<IN, MSG2>`
79    /// Note: the original func_type_id is preserved here
80    pub fn map_msg<F, MSG2>(self, cb2: F) -> Callback<IN, MSG2>
81    where
82        F: Fn(OUT) -> MSG2 + Clone + 'static,
83        MSG2: 'static,
84    {
85        let source_func_type_id = self.func_type_id;
86        let cb = move |input| {
87            let out = self.emit(input);
88            cb2(out)
89        };
90        Callback {
91            func: Rc::new(RefCell::new(cb)),
92            func_type_id: source_func_type_id,
93            event_type_id: TypeId::of::<IN>(),
94            msg_type_id: TypeId::of::<OUT>(),
95        }
96    }
97}
98
99/// Note:
100/// using the #[derive(Clone)] needs IN and OUT to also be Clone
101///
102/// The reason this is manually implemented is, so that IN and OUT
103/// doesn't need to be Clone as it is part of the Callback objects and cloning here
104/// is just cloning the pointer of the actual callback function
105impl<IN, OUT> Clone for Callback<IN, OUT> {
106    fn clone(&self) -> Self {
107        Self {
108            func: Rc::clone(&self.func),
109            func_type_id: self.func_type_id,
110            event_type_id: self.event_type_id,
111            msg_type_id: self.msg_type_id,
112        }
113    }
114}
115
116/// Compare if the callbacks are equal
117/// Note, we are only comparing the type_id of the function, the input and the output
118impl<IN, OUT> PartialEq for Callback<IN, OUT> {
119    fn eq(&self, other: &Self) -> bool {
120        self.event_type_id == other.event_type_id
121            && self.msg_type_id == other.msg_type_id
122            && self.func_type_id == other.func_type_id
123    }
124}