use std::{marker::PhantomData, pin::Pin};
use hooks_core::{Hook, HookPollNextUpdate, HookUnmount, UpdateHook};
pin_project_lite::pin_project![
#[derive(Debug)]
pub struct LazyPinnedHook<H>
where
H: HookPollNextUpdate,
H: HookUnmount,
{
#[pin]
hook: Option<H>,
}
];
impl<H: HookPollNextUpdate + HookUnmount> Default for LazyPinnedHook<H> {
fn default() -> Self {
Self { hook: None }
}
}
impl<H: HookPollNextUpdate + HookUnmount> LazyPinnedHook<H> {
#[inline]
pub fn pin_project(self: Pin<&mut Self>) -> Pin<&mut Option<H>> {
self.project().hook
}
#[inline]
pub fn pin_project_hook(self: Pin<&mut Self>) -> Option<Pin<&mut H>> {
self.pin_project().as_pin_mut()
}
}
impl<H: Hook> LazyPinnedHook<H> {
pub fn h(mut self: Pin<&mut Self>, into_hook: impl UpdateHook<Hook = H>) -> crate::Value<H> {
if let Some(hook) = self.as_mut().pin_project_hook() {
into_hook.update_hook(hook)
} else {
self.as_mut().pin_project().set(Some(into_hook.into_hook()))
}
H::use_hook(self.pin_project_hook().unwrap())
}
}
hooks_core::impl_hook![
impl<H: HookPollNextUpdate + HookUnmount> LazyPinnedHook<H> {
fn unmount(self) {
if let Some(hook) = self.pin_project_hook() {
H::unmount(hook)
}
}
fn poll_next_update(self, cx: _) {
let hook = self.pin_project_hook();
if let Some(hook) = hook {
<H as HookPollNextUpdate>::poll_next_update(hook, cx)
} else {
std::task::Poll::Ready(false)
}
}
#[inline(always)]
fn use_hook(self) -> Pin<&'hook mut Self> {
self
}
}
];
pub struct UseLazyPinnedHook<H: Hook>(PhantomData<H>);
#[cfg_attr(
all(
feature = "futures-core",
feature = "proc-macro",
feature = "use_shared_set",
feature = "use_effect",
),
doc = r###"
```
# use hooks::prelude::*;
#[hook]
fn use_demo() -> i32 {
let (state, updater) = use_shared_set(0);
if *state < 2 {
use_effect(|v: &i32| updater.set(*v + 1), *state);
}
*state
}
# use futures_lite::StreamExt;
# futures_lite::future::block_on(async {
let values: Vec<_> = use_demo().into_hook_values().collect().await;
assert_eq!(values, [0])
# });
```
"###
)]
#[cfg_attr(
all(
feature = "futures-core",
feature = "proc-macro",
feature = "use_shared_set",
feature = "use_effect",
),
doc = r###"
```
# use hooks::prelude::*;
#[hook]
fn use_demo() -> i32 {
let (state, updater) = use_shared_set(0);
let hook_effect = use_lazy_pinned_hook();
if *state < 2 {
let updater = updater.clone();
hook_effect.h(use_effect(move |v: &i32| updater.set(*v + 1), *state));
}
*state
}
# use futures_lite::StreamExt;
# futures_lite::future::block_on(async {
let values: Vec<_> = use_demo().into_hook_values().collect().await;
assert_eq!(values, [0, 1, 2])
# });
```
"###
)]
#[inline(always)]
pub fn use_lazy_pinned_hook<H: Hook>() -> UseLazyPinnedHook<H> {
UseLazyPinnedHook(PhantomData)
}
hooks_core::impl_hook![
impl<H: Hook> UseLazyPinnedHook<H> {
#[inline]
fn into_hook(self) -> LazyPinnedHook<H> {
LazyPinnedHook::default()
}
#[inline]
fn update_hook(self, _hook: _) {}
#[inline]
fn h(self, hook: LazyPinnedHook<H>) {
hook
}
}
];