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