freya_core/lifecycle/
memo.rs1use std::{
2 mem::MaybeUninit,
3 ops::Deref,
4};
5
6use crate::{
7 prelude::{
8 ReadRef,
9 State,
10 spawn,
11 use_hook,
12 },
13 reactive_context::ReactiveContext,
14};
15
16pub fn use_memo<T: 'static + PartialEq>(callback: impl FnMut() -> T + 'static) -> Memo<T> {
17 use_hook(|| Memo::create(callback))
18}
19
20pub struct Memo<T> {
21 state: State<T>,
22}
23
24impl<T> Clone for Memo<T> {
25 fn clone(&self) -> Self {
26 *self
27 }
28}
29impl<T> Copy for Memo<T> {}
30
31impl<T: Copy + PartialEq + 'static> Deref for Memo<T> {
34 type Target = dyn Fn() -> T;
35
36 fn deref(&self) -> &Self::Target {
37 unsafe { Memo::deref_impl(self) }
38 }
39}
40
41impl<T: PartialEq> Memo<T> {
42 #[doc(hidden)]
47 unsafe fn deref_impl<'a>(memo: &Memo<T>) -> &'a dyn Fn() -> T
48 where
49 Self: Sized + 'a,
50 T: Clone + 'static,
51 {
52 let uninit_callable = MaybeUninit::<Self>::uninit();
56 let uninit_closure = move || Memo::read(unsafe { &*uninit_callable.as_ptr() }).clone();
58
59 let size_of_closure = std::mem::size_of_val(&uninit_closure);
61 assert_eq!(size_of_closure, std::mem::size_of::<Self>());
62
63 fn cast_lifetime<'a, T>(_a: &T, b: &'a T) -> &'a T {
65 b
66 }
67 let reference_to_closure = cast_lifetime(
68 {
69 &uninit_closure
71 },
72 #[allow(clippy::missing_transmute_annotations)]
73 unsafe {
75 std::mem::transmute(memo)
76 },
77 );
78
79 reference_to_closure as &_
81 }
82}
83
84impl<T: 'static + PartialEq> Memo<T> {
85 pub fn create(mut callback: impl FnMut() -> T + 'static) -> Memo<T> {
86 let (rx, rc) = ReactiveContext::new_for_task();
87 let mut state = State::create(ReactiveContext::run(rc.clone(), &mut callback));
88 spawn(async move {
89 loop {
90 rx.notified().await;
91 state.set_if_modified(ReactiveContext::run(rc.clone(), &mut callback));
92 }
93 });
94 Memo { state }
95 }
96
97 pub fn read(&self) -> ReadRef<'static, T> {
98 self.state.read()
99 }
100
101 pub fn peek(&self) -> ReadRef<'static, T> {
102 self.state.peek()
103 }
104}