maycoon_core/signal/
memoized.rs1use crate::reference::Ref;
2use crate::signal::Signal;
3use crate::signal::listener::{Listener, ListenerRegister};
4use std::cell::OnceCell;
5use std::rc::Rc;
6
7pub 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 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 #[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 #[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 assert_eq!(*sum.borrow(), 0);
110 assert_eq!(*diff.borrow(), 0);
112
113 assert_eq!(*signal.get(), 1);
115
116 assert_eq!(*sum.borrow(), 1);
118 assert_eq!(*diff.borrow(), -1);
120 }
121}