hooks_core/
traits.rs

1use std::{
2    ops::{Deref, DerefMut},
3    pin::Pin,
4    task::Poll,
5};
6
7mod sealed {
8    pub trait Sealed<'hook, This: ?Sized> {}
9    impl<'hook, T: ?Sized> Sealed<'hook, T> for &'hook T {}
10}
11
12pub trait HookValueBounds<'hook, This: ?Sized>: sealed::Sealed<'hook, This> {}
13
14impl<'hook, T: ?Sized> HookValueBounds<'hook, T> for &'hook T {}
15
16/// [The Captures trick](https://rust-lang.github.io/rfcs/3498-lifetime-capture-rules-2024.html#the-captures-trick)
17pub trait Captures<U> {}
18impl<T: ?Sized, U> Captures<U> for T {}
19
20/// A helper trait to define
21/// *lifetime generic associated types (lifetime-GAT)*
22/// for [`Hook`].
23///
24/// This trait follows the [*better GAT*] pattern.
25/// *better GAT* is a pattern which implements *lifetime-GAT* without real GAT,
26/// and it also solves some problems relating to `for<'hook> HookValue<Value<'hook> = ...>`
27/// that real GAT currently doesn't solve.
28///
29/// Please don't impl this trait manually because in the future this will be changed to GAT.
30/// Instead, use [`impl_hook![...];`](crate::impl_hook).
31///
32/// [*better GAT*]: https://sabrinajewson.org/blog/the-better-alternative-to-lifetime-gats#the-better-gats
33pub trait HookValue<'hook, ImplicitBounds: HookValueBounds<'hook, Self> = &'hook Self> {
34    /// The output type of [`Hook::use_hook`].
35    ///
36    /// Please don't use this associated type directly.
37    /// Instead, use [`Value![]`](crate::Value) for future compatibility.
38    type Value;
39}
40
41/// Defines reactivity of a hook.
42///
43/// See [`poll_next_update()`](HookPollNextUpdate::poll_next_update) for details.
44pub trait HookPollNextUpdate {
45    /// The meaning of the return value is:
46    ///
47    /// - `Poll::Pending` means this hook's inner state is not updated
48    ///   after the last `use_hook`.
49    ///   The executor **DO NOT NEED** to call `use_hook` again
50    ///   because the returned value is expected to remain the same
51    ///   as the value from the last call.
52    ///   The executor **CAN** still call `use_hook`
53    ///   to re-get the returned value.
54    ///
55    /// - `Poll::Ready(true)` means this hook's inner state has been updated
56    ///   since the last `use_hook`.
57    ///   The executor **SHOULD** call `use_hook` again to get the new value.
58    ///   If the executor doesn't update or use this hook, instead, it polls the hook again,
59    ///   the hook may still return `Poll::Ready(true)`.
60    ///
61    /// - `Poll::Ready(false)` means this hook's inner state will never be updated.
62    ///   The executor **CAN** drop this hook.
63    ///   The executor **CAN** call `use_hook` to get the value or update it, and
64    ///   the hook **MIGHT** become dynamic again during `use_hook` or `update_hook`, or when
65    ///   some operations is done to the returned value.
66    fn poll_next_update(self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll<bool>;
67}
68
69/// Defines how to cleanup a hook.
70///
71/// Consider this as a re-entrant and pinned version of [`Drop`].
72/// Cleanups can be run in [`unmount`](HookUnmount::unmount).
73///
74/// After `unmount`, the hook might still be used or updated.
75pub trait HookUnmount {
76    #[inline(always)]
77    fn unmount(self: Pin<&mut Self>) {}
78}
79
80/// Defines how to use a hook (get value from the hook).
81///
82/// A hook is something that outputs values reactively.
83///
84/// ## How to impl `Hook`
85///
86/// Usually, you don't need to impl `Hook`.
87/// You can easily compose hooks with [`hook_fn!(...)`] or
88/// [`#[hook]`](https://docs.rs/hooks/latest/hooks/attr.hook.html).
89///
90/// The hook fn actually returns `impl UpdateHookUninitialized<Hook = impl Hook>`,
91/// so that this hook fn can also be composed in other `hook_fn`.
92/// For more information, see [`hook_fn!(...)`].
93///
94/// ### with `hook_fn!(...)` macro
95///
96/// ```
97/// # extern crate hooks_dev as hooks;
98/// # use hooks::{use_shared_set, hook_fn};
99/// hook_fn![
100///     pub fn use_my_hook() -> &'hook mut i32 {
101///         let (state, updater) = h![use_shared_set(1)];
102///         state
103///     }
104/// ];
105/// ```
106///
107/// ### with `#[hook]` attribute macro
108///
109/// ```
110/// # extern crate hooks_dev as hooks;
111/// # use hooks::{use_effect, hook};
112/// /// Print debug on `value` change.
113/// #[hook]
114/// fn use_debug<T: std::fmt::Debug + PartialEq + Copy>(value: T) {
115///     use_effect(|v: &_| {
116///         println!("{v:?}");
117///     }, value);
118/// }
119/// ```
120///
121/// ### implement `Hook` manually.
122///
123/// See [`impl_hook!`](crate::impl_hook).
124///
125/// ## Comparison with `LendingAsyncIterator`
126///
127/// A `Hook` is like a `LendingAsyncIterator`.
128/// They both produce items asynchronously,
129/// but they have different meanings on pending and terminating:
130///
131/// For pending:
132///
133/// - If a `LendingAsyncIterator` is pending
134///   (`poll_next` returns `Poll::Pending`),
135///   it is producing the next item.
136///
137/// - If a `Hook` is pending,
138///   (`poll_next_update` returns `Poll::Pending`),
139///   it is waiting for its inner state to update.
140///   When a `Hook` is pending, the executor can still *use* it
141///   by calling [`use_hook`](Hook::use_hook) and
142///   the returned value would remain the *same* as the last returned value.
143///   *Using* a hook is like *inspecting* it.
144///   Some hooks may do heavy work in `use_hook`.
145///   It is advised to call `use_hook` only after
146///   `poll_next_update` returns `Poll::Ready(true)`.
147///
148/// For terminating:
149///
150/// - If a `LendingAsyncIterator` is terminated
151///   (`poll_next` returns `Poll::Ready(None)`),
152///   the executor MUST NOT call `poll_next` again.
153///
154/// - There is no termination for a `Hook` until dropped.
155///   When `poll_next_update` returns `Poll::Ready(false)`,
156///   this means the hook is no longer dynamic
157///   (its inner state will no longer update).
158///   Thus, there is no need to call `use_hook` again because
159///   the returned value is expected to remain the *same*.
160///   But the executor can still call `use_hook` to re-get the value
161///   or update it with [`update_hook`](crate::UpdateHook::update_hook) or [`h`](crate::UpdateHookUninitialized::h),
162///   and this might make the hook dynamic again.
163///
164///   This behavior makes it possible to combine multiple hooks.
165///   When some hooks are no longer dynamic
166///   but other hooks depend on their returned values,
167///   the executor can still get the values
168///   from the no-longer-dynamic hooks,
169///   and pass the values to the dynamic hooks.
170///
171/// Also see [`NonLendingHook`] for a subset of hooks that doesn't lend lifetimes to values,
172/// which are like [`AsyncIterator`](std::async_iter::AsyncIterator) or [`Stream`](futures_core::Stream).
173///
174/// [`hook_fn!(...)`]: crate::hook_fn
175pub trait Hook: HookPollNextUpdate + HookUnmount + for<'hook> HookValue<'hook> {
176    fn use_hook(self: Pin<&mut Self>) -> <Self as HookValue<'_>>::Value;
177}
178
179/// `NonLendingHook` is a subset of [`Hook`].
180/// `Value` of `NonLendingHook` is not generic,
181/// thus not borrowing from the hook.
182/// In other words, `NonLendingHook` doesn't lend to its `Value`.
183///
184/// [`Hook`] is like a `LendingAsyncIterator`,
185/// `NonLendingHook` is like an [`AsyncIterator`](std::async_iter::AsyncIterator)
186/// (also known as [`Stream`](futures_core::Stream)).
187///
188/// You can run
189/// [`hook.into_values()`](crate::HookExt::into_values) and [`hook.values()`](crate::HookExt::values)
190/// to get a stream of values from a hook.
191pub trait NonLendingHook:
192    Hook + for<'hook> HookValue<'hook, Value = Self::NonGenericValue>
193{
194    type NonGenericValue;
195}
196
197impl<H, V> NonLendingHook for H
198where
199    H: Hook + for<'hook> HookValue<'hook, Value = V>,
200{
201    type NonGenericValue = V;
202}
203
204macro_rules! impl_for_deref_hook {
205    (
206        impl<$h:ident> (
207            $($ty:ty),*
208            $(,)?
209        ) {
210            $poll_next_update:item
211            $unmount:item
212            $use_hook:item
213        }
214    ) => {$(
215        impl<H: HookPollNextUpdate + Unpin + ?Sized> HookPollNextUpdate for $ty {
216            $poll_next_update
217        }
218
219        impl<H: HookUnmount + Unpin + ?Sized> HookUnmount for $ty {
220            $unmount
221        }
222
223        impl<'hook, H: Hook + Unpin + ?Sized> HookValue<'hook> for $ty {
224            type Value = <H as HookValue<'hook>>::Value;
225        }
226
227        impl<H: Hook + Unpin + ?Sized> Hook for $ty {
228            $use_hook
229        }
230    )*};
231}
232
233impl_for_deref_hook![
234    impl<H> (&mut H, Box<H>) {
235        #[inline]
236        fn poll_next_update(self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll<bool> {
237            H::poll_next_update(Pin::new(self.get_mut()), cx)
238        }
239
240        #[inline]
241        fn unmount(self: Pin<&mut Self>) {
242            H::unmount(Pin::new(self.get_mut()))
243        }
244
245        #[inline]
246        fn use_hook(self: Pin<&mut Self>) -> <Self as HookValue<'_>>::Value {
247            H::use_hook(Pin::new(self.get_mut()))
248        }
249    }
250];
251
252impl<P> HookPollNextUpdate for Pin<P>
253where
254    P: DerefMut,
255    <P as Deref>::Target: HookPollNextUpdate,
256{
257    #[inline]
258    fn poll_next_update(self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll<bool> {
259        <P::Target as HookPollNextUpdate>::poll_next_update(
260            crate::utils::pin_as_deref_mut(self),
261            cx,
262        )
263    }
264}
265
266impl<P> HookUnmount for Pin<P>
267where
268    P: DerefMut,
269    <P as Deref>::Target: HookUnmount,
270{
271    #[inline]
272    fn unmount(self: Pin<&mut Self>) {
273        <<P as Deref>::Target as HookUnmount>::unmount(crate::utils::pin_as_deref_mut(self))
274    }
275}
276
277impl<'hook, P> HookValue<'hook> for Pin<P>
278where
279    P: DerefMut,
280    <P as Deref>::Target: Hook,
281{
282    type Value = <<P as Deref>::Target as HookValue<'hook>>::Value;
283}
284
285impl<P> Hook for Pin<P>
286where
287    P: DerefMut,
288    <P as Deref>::Target: Hook,
289{
290    #[inline]
291    fn use_hook(self: Pin<&mut Self>) -> <Self as HookValue<'_>>::Value {
292        <P::Target as Hook>::use_hook(crate::utils::pin_as_deref_mut(self))
293    }
294}
295
296pub trait IntoHook {
297    type Hook: Hook;
298
299    fn into_hook(self) -> Self::Hook;
300
301    #[inline(always)]
302    fn into_hook_values(self) -> crate::Values<Self::Hook>
303    where
304        Self: Sized,
305    {
306        crate::Values::new(self.into_hook())
307    }
308}
309
310impl<H> IntoHook for H
311where
312    H: Hook,
313{
314    type Hook = H;
315
316    #[inline(always)]
317    fn into_hook(self) -> Self::Hook {
318        self
319    }
320}
321
322pub trait UpdateHook: IntoHook {
323    fn update_hook(self, hook: Pin<&mut Self::Hook>);
324}
325
326pub trait UpdateHookUninitialized: UpdateHook {
327    type Uninitialized: HookPollNextUpdate + HookUnmount + Default;
328
329    fn h(self, hook: Pin<&mut Self::Uninitialized>) -> <Self::Hook as HookValue<'_>>::Value;
330}
331
332/// Type alias for [`HookValue::Value`].
333///
334/// In the future, when [`HookValue`] changed to GAT,
335/// this type alias would still works.
336pub type Value<'hook, H> = <H as HookValue<'hook>>::Value;