freya_core/lifecycle/
memo.rs1use std::{
2 mem::MaybeUninit,
3 ops::Deref,
4};
5
6use crate::{
7 lifecycle::writable_utils::WritableUtils,
8 prelude::{
9 ReadRef,
10 State,
11 spawn,
12 use_hook,
13 },
14 reactive_context::ReactiveContext,
15};
16
17pub fn use_memo<T: 'static + PartialEq>(callback: impl FnMut() -> T + 'static) -> Memo<T> {
30 use_hook(|| Memo::create(callback))
31}
32
33pub struct Memo<T> {
34 pub(crate) state: State<T>,
35}
36
37impl<T: 'static> PartialEq for Memo<T> {
38 fn eq(&self, other: &Self) -> bool {
39 self.state == other.state
40 }
41}
42
43impl<T: 'static> Eq for Memo<T> {}
44
45impl<T> Clone for Memo<T> {
46 fn clone(&self) -> Self {
47 *self
48 }
49}
50impl<T> Copy for Memo<T> {}
51
52impl<T: Copy + PartialEq + 'static> Deref for Memo<T> {
55 type Target = dyn Fn() -> T;
56
57 fn deref(&self) -> &Self::Target {
58 unsafe { Memo::deref_impl(self) }
59 }
60}
61
62impl<T: PartialEq> Memo<T> {
63 #[doc(hidden)]
68 unsafe fn deref_impl<'a>(memo: &Memo<T>) -> &'a dyn Fn() -> T
69 where
70 Self: Sized + 'a,
71 T: Clone + 'static,
72 {
73 let uninit_callable = MaybeUninit::<Self>::uninit();
77 let uninit_closure = move || Memo::read(unsafe { &*uninit_callable.as_ptr() }).clone();
79
80 let size_of_closure = std::mem::size_of_val(&uninit_closure);
82 assert_eq!(size_of_closure, std::mem::size_of::<Self>());
83
84 fn cast_lifetime<'a, T>(_a: &T, b: &'a T) -> &'a T {
86 b
87 }
88 let reference_to_closure = cast_lifetime(
89 {
90 &uninit_closure
92 },
93 #[allow(clippy::missing_transmute_annotations)]
94 unsafe {
96 std::mem::transmute(memo)
97 },
98 );
99
100 reference_to_closure as &_
102 }
103}
104
105impl<T: 'static + PartialEq> Memo<T> {
106 pub fn create(mut callback: impl FnMut() -> T + 'static) -> Memo<T> {
107 let (rx, rc) = ReactiveContext::new_for_task();
108 let mut state = State::create(ReactiveContext::run(rc.clone(), &mut callback));
109 spawn(async move {
110 loop {
111 rx.notified().await;
112 state.set_if_modified(ReactiveContext::run(rc.clone(), &mut callback));
113 }
114 });
115 Memo { state }
116 }
117
118 #[track_caller]
119 pub fn read(&self) -> ReadRef<'static, T> {
120 self.state.read()
121 }
122
123 #[track_caller]
124 pub fn peek(&self) -> ReadRef<'static, T> {
125 self.state.peek()
126 }
127}