maycoon_core/signal/
eval.rs

1use crate::reference::Ref;
2use crate::signal::Signal;
3use std::rc::Rc;
4
5/// A signal that evaluates a function to get the value.
6///
7/// The evaluation function will be called every time the value is requested via [Signal::get],
8/// so it's recommended to avoid expensive operations.
9pub struct EvalSignal<T: 'static> {
10    eval: Rc<dyn Fn() -> T>,
11}
12
13impl<T: 'static> EvalSignal<T> {
14    /// Create a new eval signal using the given evaluation function.
15    #[inline(always)]
16    pub fn new(eval: impl Fn() -> T + 'static) -> Self {
17        Self {
18            eval: Rc::new(eval),
19        }
20    }
21}
22
23impl<T: 'static> Signal<T> for EvalSignal<T> {
24    #[inline(always)]
25    fn get(&self) -> Ref<'_, T> {
26        Ref::Owned((self.eval)())
27    }
28
29    #[inline(always)]
30    fn set_value(&self, _: T) {}
31
32    #[inline(always)]
33    fn listen(self, _: Box<dyn Fn(Ref<'_, T>)>) -> Self
34    where
35        Self: Sized,
36    {
37        self
38    }
39
40    #[inline(always)]
41    fn notify(&self) {}
42
43    #[inline(always)]
44    fn dyn_clone(&self) -> Box<dyn Signal<T>> {
45        Box::new(self.clone())
46    }
47}
48
49impl<T: 'static> Clone for EvalSignal<T> {
50    #[inline(always)]
51    fn clone(&self) -> Self {
52        Self {
53            eval: self.eval.clone(),
54        }
55    }
56}
57
58#[cfg(all(test, feature = "test"))]
59mod tests {
60    use crate::signal::Signal;
61    use crate::signal::eval::EvalSignal;
62    use std::cell::RefCell;
63    use std::rc::Rc;
64
65    /// Tests the [EvalSignal] implementation of [Signal].
66    #[test]
67    fn test_eval_signal() {
68        let value = Rc::new(RefCell::new(0));
69
70        let value_clone = value.clone();
71        let signal = EvalSignal::new(move || {
72            *value_clone.borrow_mut() += 1;
73            *value_clone.borrow()
74        });
75
76        // signal = value = 1
77        assert_eq!(*signal.get(), 1);
78        assert_eq!(*value.borrow(), 1);
79
80        // signal = value = 2
81        assert_eq!(*signal.get(), 2);
82        assert_eq!(*value.borrow(), 2);
83
84        // signal = value = 3
85        assert_eq!(*signal.get(), 3);
86        assert_eq!(*value.borrow(), 3);
87    }
88}