use std::{
mem::MaybeUninit,
ops::Deref,
};
use crate::{
prelude::{
ReadRef,
State,
spawn,
use_hook,
},
reactive_context::ReactiveContext,
};
pub fn use_memo<T: 'static + PartialEq>(callback: impl FnMut() -> T + 'static) -> Memo<T> {
use_hook(|| Memo::create(callback))
}
pub struct Memo<T> {
state: State<T>,
}
impl<T> Clone for Memo<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T> Copy for Memo<T> {}
impl<T: Copy + PartialEq + 'static> Deref for Memo<T> {
type Target = dyn Fn() -> T;
fn deref(&self) -> &Self::Target {
unsafe { Memo::deref_impl(self) }
}
}
impl<T: PartialEq> Memo<T> {
#[doc(hidden)]
unsafe fn deref_impl<'a>(memo: &Memo<T>) -> &'a dyn Fn() -> T
where
Self: Sized + 'a,
T: Clone + 'static,
{
let uninit_callable = MaybeUninit::<Self>::uninit();
let uninit_closure = move || Memo::read(unsafe { &*uninit_callable.as_ptr() }).clone();
let size_of_closure = std::mem::size_of_val(&uninit_closure);
assert_eq!(size_of_closure, std::mem::size_of::<Self>());
fn cast_lifetime<'a, T>(_a: &T, b: &'a T) -> &'a T {
b
}
let reference_to_closure = cast_lifetime(
{
&uninit_closure
},
#[allow(clippy::missing_transmute_annotations)]
unsafe {
std::mem::transmute(memo)
},
);
reference_to_closure as &_
}
}
impl<T: 'static + PartialEq> Memo<T> {
pub fn create(mut callback: impl FnMut() -> T + 'static) -> Memo<T> {
let (rx, rc) = ReactiveContext::new_for_task();
let mut state = State::create(ReactiveContext::run(rc.clone(), &mut callback));
spawn(async move {
loop {
rx.notified().await;
state.set_if_modified(ReactiveContext::run(rc.clone(), &mut callback));
}
});
Memo { state }
}
pub fn read(&self) -> ReadRef<'static, T> {
self.state.read()
}
pub fn peek(&self) -> ReadRef<'static, T> {
self.state.peek()
}
}