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}