maycoon_core/signal/
memoized.rs

1use crate::reference::Ref;
2use crate::signal::Signal;
3use crate::signal::listener::{Listener, ListenerRegister};
4use std::cell::OnceCell;
5use std::rc::Rc;
6
7/// A signal for creating a value once, when requested.
8/// The value is immutable after creation.
9/// Calling [Signal::set], [Signal::set_value], [Signal::listen] or [Signal::notify] has no effect.
10///
11/// **NOTE:** The inner factory function will only be called **once**, when the value is requested via [Signal::get].
12pub struct MemoizedSignal<T: 'static> {
13    inner: Rc<OnceCell<T>>,
14    factory: Rc<dyn Fn() -> T>,
15    listeners: ListenerRegister<T>,
16}
17
18impl<T: 'static> MemoizedSignal<T> {
19    #[inline(always)]
20    /// Create a new memoized signal using the given factory function.
21    pub fn new(factory: impl Fn() -> T + 'static) -> Self {
22        Self {
23            inner: Rc::new(OnceCell::new()),
24            factory: Rc::new(factory),
25            listeners: ListenerRegister::new(),
26        }
27    }
28
29    /// Returns if the value has been initialized or not.
30    #[inline(always)]
31    pub fn is_init(&self) -> bool {
32        self.inner.get().is_some()
33    }
34}
35
36impl<T: 'static> Signal<T> for MemoizedSignal<T> {
37    #[inline(always)]
38    fn get(&self) -> Ref<'_, T> {
39        if !self.is_init() {
40            self.inner.set((self.factory)()).ok().unwrap();
41
42            self.notify();
43        }
44
45        Ref::Borrow(self.inner.get().unwrap())
46    }
47
48    #[inline(always)]
49    fn set_value(&self, _: T) {}
50
51    #[inline(always)]
52    fn listen(self, listener: Box<dyn Fn(Ref<'_, T>)>) -> Self
53    where
54        Self: Sized,
55    {
56        Self {
57            inner: self.inner,
58            factory: self.factory,
59            listeners: self.listeners.register(Listener::new(listener)),
60        }
61    }
62
63    #[inline(always)]
64    fn notify(&self) {
65        self.listeners.notify(|| self.get());
66    }
67
68    #[inline(always)]
69    fn dyn_clone(&self) -> Box<dyn Signal<T>> {
70        Box::new(self.clone())
71    }
72}
73
74impl<T: 'static> Clone for MemoizedSignal<T> {
75    #[inline(always)]
76    fn clone(&self) -> Self {
77        Self {
78            inner: self.inner.clone(),
79            factory: self.factory.clone(),
80            listeners: self.listeners.clone(),
81        }
82    }
83}
84
85#[cfg(all(test, feature = "test"))]
86mod tests {
87    use crate::signal::Signal;
88    use crate::signal::memoized::MemoizedSignal;
89    use std::cell::RefCell;
90    use std::rc::Rc;
91
92    /// Tests the [MemoizedSignal] implementation of [Signal].
93    #[test]
94    fn test_memoized_signal() {
95        let sum = Rc::new(RefCell::new(0));
96        let diff = Rc::new(RefCell::new(0));
97
98        let sum_clone = sum.clone();
99        let diff_clone = diff.clone();
100        let signal = MemoizedSignal::new(|| 1)
101            .listen(Box::new(move |val| {
102                *sum_clone.borrow_mut() += *val;
103            }))
104            .listen(Box::new(move |val| {
105                *diff_clone.borrow_mut() -= *val;
106            }));
107
108        // sum = 0
109        assert_eq!(*sum.borrow(), 0);
110        // diff = 0
111        assert_eq!(*diff.borrow(), 0);
112
113        // signal = 1
114        assert_eq!(*signal.get(), 1);
115
116        // sum = sum + signal = 0 + 1 = 1
117        assert_eq!(*sum.borrow(), 1);
118        // diff = diff - signal = 0 - 1 = -1
119        assert_eq!(*diff.borrow(), -1);
120    }
121}