hooks_core/
macros.rs

1/// This macro should only be used in [`hook_fn`](crate::hook_fn) or `#[hook]`.
2#[macro_export]
3macro_rules! h {
4    [] => {
5        ::core::compile_error! {"h! must be used in hook_fn!"}
6    };
7}
8
9/// Write hook fn without any proc-macro.
10///
11/// A *hook fn* is a `fn` which returns [`impl UpdateHookUninitialized`](trait@crate::UpdateHookUninitialized).
12/// (Thus the output type is also [`UpdateHook`](crate::UpdateHook) + [`IntoHook`](crate::IntoHook)).
13///
14/// Tips: `cargo fmt` supports formatting code inside `hook_fn!(...);` and `hook_fn![...];`, but not `hook_fn!{...}`.
15///
16/// ## Usage
17///
18/// ```
19/// # use hooks_core::prelude::*;
20/// hook_fn!(
21///     fn use_constant() -> i32 {
22///         1
23///     }
24/// );
25///
26/// # use hooks_core::{UpdateHookUninitialized, Hook, HookValue, HookPollNextUpdate, HookUnmount};
27/// /// `use_constant()` actually returns:
28/// # fn assert_0() ->
29/// UpdateHookUninitialized![i32]
30/// # { use_constant() }
31/// /// The above macro expands to
32/// # fn assert_1() ->
33/// impl UpdateHookUninitialized<
34///     Uninitialized = impl HookPollNextUpdate + HookUnmount + Default,
35///     Hook = impl Hook + for<'hook> HookValue<'hook, Value = i32>
36/// >
37/// # { use_constant() }
38/// ```
39///
40/// ## Use other hooks with [`h!(...)`](h)
41///
42/// Usually, you would want to use other hooks in hook_fn.
43///
44/// ```
45/// # extern crate hooks_dev as hooks;
46/// use hooks::prelude::*;
47///
48/// hook_fn!(
49///     fn use_auto_increment(max: i32) -> i32 {
50///         let state = h!(use_shared_signal(0));
51///         let v = state.get();
52///
53///         h!(use_effect_with::<i32, _>(|old_dependency| {
54///             if *old_dependency != Some(v) {
55///                 *old_dependency = Some(v);
56///                 let state = state.clone();
57///                 Some(move |v: &_| state.set(*v + 1))
58///             } else {
59///                 None
60///             }
61///         }));
62///
63///         v
64///     }
65/// );
66/// ```
67///
68/// ## Capture lifetimes
69///
70/// Lifetimes in `fn` generics and `type Bounds = impl 'a` are captured.
71///
72/// For example, in
73///
74/// ```
75/// # use hooks_core::hook_fn;
76/// hook_fn!(
77///     fn use_hook<'a>(_: &'a str) {}
78/// );
79/// ```
80///
81/// the return type of `use_hook` auto captures
82/// lifetime `'a` with trait bound [`Captures<&'a ()>`](crate::Captures)
83/// (even the argument `&'a str` is not used in the fn body).
84///
85/// <details>
86/// <summary>
87///
88/// If the lifetime is elided and used,
89/// it must be specified with `type Bounds = impl '_;`.
90///
91/// </summary>
92///
93/// ```compile_fail
94/// # use hooks_core::hook_fn;
95/// hook_fn!(
96///     fn use_hook(v: &str) {
97///         println!("{}", v);
98///     }
99/// );
100/// ```
101///
102/// ```
103/// # use hooks_core::hook_fn;
104/// hook_fn!(
105///     type Bounds = impl '_;
106///     fn use_hook(v: &str) {
107///         println!("{}", v);
108///     }
109/// );
110/// ```
111///
112/// </details>
113///
114/// <details><summary>
115///
116/// If the lifetime comes from outer `Self` and is used,
117/// it must be specified with `type Bounds = impl 'a;`.
118///
119/// </summary>
120///
121/// ```compile_fail
122/// # use hooks_core::hook_fn;
123/// struct Data<'a>(&'a str);
124///
125/// impl<'a> Data<'a> {
126///     hook_fn!(
127///         fn use_data(self) {
128///             println!("{}", self.0)
129///         }
130///    );
131/// }
132/// ```
133///
134/// ```
135/// # use hooks_core::hook_fn;
136/// struct Data<'a>(&'a str);
137///
138/// impl<'a> Data<'a> {
139///     hook_fn!(
140///         type Bounds = impl 'a;
141///         fn use_data(self) {
142///             println!("{}", self.0)
143///         }
144///    );
145/// }
146/// ```
147///
148/// </details>
149///
150/// <details>
151/// <summary>
152///
153/// In Rust 2024 and later editions, all lifetimes should be auto captured
154/// so you don't need to specify them explicitly.
155///
156/// </summary>
157///
158/// See [lifetime_capture_rules_2024](https://rust-lang.github.io/rfcs/3498-lifetime-capture-rules-2024.html)
159///
160/// </details>
161///
162/// ## Use an explicit identifier for hook
163///
164/// `h!(use_hook())` actually expands to [`use_hook().h(hook_id)`](crate::UpdateHookUninitialized::h),
165/// where `hook_id` is of type [`Pin<&mut UpdateHookUninitialized::Uninitialized>`](crate::UpdateHookUninitialized::Uninitialized).
166///
167/// You can use an explicit `hook_id` for debugging.
168///
169/// ```
170/// # extern crate hooks_dev as hooks;
171/// # use hooks::prelude::*;
172/// hook_fn!(
173///     fn use_my_state() -> i32 {
174///         println!("{state_hook:?}"); // inspect this hook before it is used
175///
176///         let mut the_state_hook = state_hook;
177///         let state_hook = std::pin::Pin::as_mut(&mut the_state_hook);
178///
179///         let (state, updater) = h![state_hook = use_shared_set(1)];
180///
181///         let state = *state;
182///
183///         println!("{the_state_hook:?}"); // inspect this hook after it is used
184///
185///         state
186///     }
187/// );
188/// ```
189///
190/// ## Limitations
191///
192/// <details><summary>
193///
194/// Only *top level token trees* are parsed.
195///
196/// </summary>
197///
198/// The following example doesn't work because
199/// two macros are in a token tree `()`, which stops them to be parsed.
200///
201/// ```compile_fail
202/// # extern crate hooks_dev as hooks;
203/// # use hooks::prelude::*;
204/// hook_fn!(
205///     fn use_shared_signal_2() -> (i32, i32) {
206///         (
207///             h![use_shared_signal(0)].get(),
208///             h![use_shared_signal(1)].get(),
209///         )
210///     }
211/// );
212/// ```
213///
214/// You have to make sure `h!` to be *top level token trees* when using `hook_fn!`.
215///
216/// ```
217/// # extern crate hooks_dev as hooks;
218/// # use hooks::prelude::*;
219/// hook_fn!(
220///     fn use_shared_signal_2() -> (i32, i32) {
221///         let a = h![use_shared_signal(0)].get();
222///         let b = h![use_shared_signal(1)].get();
223///         (a, b)
224///     }
225/// );
226/// ```
227///
228/// `#[hook]` parses any *top level expressions* (expressions that are not wrapped in `{}`).
229/// Thus, the following example works.
230///
231/// ```
232/// # extern crate hooks_dev as hooks;
233/// # use hooks::prelude::*;
234/// #[hook]
235/// fn use_shared_signal_2() -> (i32, i32) {
236///     (
237///         use_shared_signal(0).get(),
238///         use_shared_signal(1).get(),
239///     )
240/// }
241/// ```
242///
243/// </details>
244///
245/// <details><summary>
246///
247/// Supports at most 10 hooks.
248///
249/// </summary>
250///
251/// ```
252/// # use hooks_core::hook_fn;
253/// # hook_fn!( fn use_hook(_: usize) {} );
254/// hook_fn!(
255///     fn use_10_hooks() {
256///         h!(use_hook(0)); h!(use_hook(1)); h!(use_hook(2)); h!(use_hook(3)); h!(use_hook(4));
257///         h!(use_hook(5)); h!(use_hook(6)); h!(use_hook(7)); h!(use_hook(8)); h!(use_hook(9));
258///     }
259/// );
260/// ```
261///
262/// ```compile_fail
263/// # use hooks_core::hook_fn;
264/// # hook_fn!( fn use_hook(_: usize) {} );
265/// hook_fn!(
266///     fn use_11_hooks() {
267///         h!(use_hook(0)); h!(use_hook(1)); h!(use_hook(2)); h!(use_hook(3)); h!(use_hook(4));
268///         h!(use_hook(5)); h!(use_hook(6)); h!(use_hook(7)); h!(use_hook(8)); h!(use_hook(9));
269///         h!(use_hook(10));
270///     }
271/// );
272/// ```
273///
274/// This limitation also applies to `#[hook]` because
275/// traits are only implemented for [`HookTuple<(0, 1, ...)>`](crate::HookTuple)
276/// with at most 10 elements.
277///
278/// ```compile_fail
279/// # extern crate hooks_dev as hooks;
280/// # use hooks::hook;
281/// # hooks_core::hook_fn!( fn use_hook(_: usize) {} );
282/// #[hook]
283/// fn use_11_hooks() {
284///     use_hook(0); use_hook(1); use_hook(2); use_hook(3); use_hook(4);
285///     use_hook(5); use_hook(6); use_hook(7); use_hook(8); use_hook(9);
286///     use_hook(10);
287/// }
288/// ```
289///
290/// </details>
291///
292/// <details><summary>
293///
294/// Output type must not be opaque type (impl Trait)
295///
296/// </summary>
297///
298/// ```compile_fail
299/// # use hooks_core::hook_fn;
300/// # use std::future::Future;
301/// hook_fn!(
302///     fn use_future() -> impl Future<Output = ()> {
303///         async {}
304///     }
305/// );
306/// ```
307///
308/// `#[hook]` allows to do so.
309///
310/// ```
311/// # extern crate hooks_dev as hooks;
312/// # use hooks::hook;
313/// # use std::future::Future;
314/// #[hook]
315/// fn use_future() -> impl Future<Output = ()> {
316///     async {}
317/// }
318/// ```
319///
320/// </details>
321#[macro_export]
322macro_rules! hook_fn {
323    (
324        type Bounds = impl $hook_bound:lifetime $(+ $hook_bounds:lifetime)* ;
325        $($rest:tt)*
326    ) => {
327        $crate::__impl_hook_fn_bounds_resolved! {
328            { $hook_bound $(+ $hook_bounds)* }
329            $($rest)*
330        }
331    };
332    ($($rest:tt)*) => {
333        $crate::__impl_hook_fn_bounds_resolved! {
334            {}
335            $($rest)*
336        }
337    };
338}
339
340/// ```
341/// # extern crate hooks_dev as hooks;
342/// # use hooks::prelude::*;
343/// let closure = hooks_core::transform_hook_fn_body_as_closure! {
344///     // options
345///     []
346///     // code
347///     {
348///         let (state, _updater) = h![use_shared_set(3)];
349///         *state
350///     }
351/// };
352///
353/// let mut inner_hook = Default::default();
354/// let value = closure(std::pin::Pin::new(&mut inner_hook));
355/// assert_eq!(value, 3);
356/// ```
357#[macro_export]
358macro_rules! transform_hook_fn_body_as_closure {
359    ( $options:tt $code:tt ) => {
360        $crate::__impl_fn_hook_body! {
361            // state
362            [
363                $options
364                [
365                    __hooks_hook_0
366                    __hooks_hook_1
367                    __hooks_hook_2
368                    __hooks_hook_3
369                    __hooks_hook_4
370                    __hooks_hook_5
371                    __hooks_hook_6
372                    __hooks_hook_7
373                    __hooks_hook_8
374                    __hooks_hook_9
375                ]
376            ]
377            [] // used_ids
378            [] // transformed_code
379            $code
380            $code
381        }
382    };
383}
384
385/// Easily impl traits.
386///
387/// For example:
388///
389/// ```
390/// # use hooks_core::impl_hook;
391/// struct OutputOnce<T>(pub Option<T>);
392///
393/// impl_hook!(
394///     impl<T: Unpin> OutputOnce<T> {
395///         /// HookUnmount is implemented with default `fn unmount()`
396///         fn unmount() {}
397///
398///         /// HookPollNextUpdate is implemented
399///         fn poll_next_update(self, _cx: _) {
400///             std::task::Poll::Ready(self.get_mut().0.is_some())
401///         }
402///
403///         /// HookValue and Hook is implemented
404///         /// HookValue::Value is `&'hook mut Option<T>`
405///         #[inline]
406///         fn use_hook(self) -> &'hook mut Option<T> {
407///             &mut self.get_mut().0
408///         }
409///     }
410/// );
411/// ```
412///
413/// ## Just write the methods of the traits you want to impl in one impl block
414///
415/// ```
416/// # use hooks_core::impl_hook;
417/// # struct MyType;
418/// impl_hook!(
419///     impl<__> MyType { // <__> helps rustfmt to format this macro.
420///         // write methods here
421/// #       fn poll_next_update(self) { false.into() }
422///     }
423/// );
424/// # fn asserts() -> impl hooks_core::HookPollNextUpdate { MyType }
425/// ```
426///
427/// Generics and where clause are supported.
428///
429/// ```
430/// # use hooks_core::impl_hook;
431/// # struct MyType<'a, T, F>(&'a T, F);
432/// impl_hook!(
433///     impl<'a, T: Clone + Default, F: FnMut(&T) -> T> MyType<'a, T, F>
434///     where
435///         T: Sized,
436///     {
437///         // write methods here
438/// #       fn poll_next_update(self) { false.into() }
439///     }
440/// );
441/// # fn asserts() -> impl hooks_core::HookPollNextUpdate { MyType(&1, Clone::clone) }
442/// ```
443///
444/// ## Supported traits
445///
446/// <details>
447/// <summary>
448///
449/// impl [`HookUnmount`] with [`unmount`](crate::HookUnmount::unmount)
450///
451/// </summary>
452///
453/// With default implementation:
454///
455/// ```
456/// # use hooks_core::impl_hook; struct MyType;
457/// impl_hook!(
458///     impl<__> MyType {
459///         fn unmount() {}
460///     }
461/// );
462/// ```
463///
464/// With custom implementation:
465///
466/// ```
467/// # use hooks_core::impl_hook; struct MyType;
468/// # impl MyType { fn do_something(&self) {} }
469/// impl_hook!(
470///     impl<__> MyType {
471///         fn unmount(self) {
472///             self.do_something();
473///         }
474///     }
475/// );
476/// ```
477///
478/// </details>
479///
480/// <details><summary>
481///
482/// impl [`HookPollNextUpdate`] with [`poll_next_update`](crate::HookPollNextUpdate::poll_next_update)
483///
484/// </summary>
485///
486/// Argument `cx` is of type `&mut std::task::Context<'_>`.
487/// The return type is `std::task::Poll<bool>`
488///
489/// ```
490/// # use std::future::Future;
491/// # use hooks_core::impl_hook;
492/// # pin_project_lite::pin_project!( struct MyType {
493/// #     #[pin]
494/// #     inner: std::future::Ready<bool>,
495/// # });
496/// impl_hook!(
497///     impl<__> MyType {
498///         fn poll_next_update(self, cx: _) {
499/// #           let cx: &mut std::task::Context<'_> = cx;
500///             self.project().inner.poll(cx)
501///         }
502///     }
503/// );
504/// ```
505///
506/// </details>
507///
508/// <details><summary>
509///
510/// impl [`HookValue`] and [`Hook`] with [`use_hook`](crate::Hook::use_hook)
511///
512/// </summary>
513///
514/// Note that [`Hook`] requires [`HookUnmount`] + [`HookPollNextUpdate`]
515///
516/// ```
517/// # use hooks_core::impl_hook;
518/// # #[derive(Clone, Copy)] struct MyValueType; struct MyType(MyValueType);
519/// impl_hook!(
520///     impl<__> MyType {
521///         fn unmount() {}
522///         fn poll_next_update(self, cx: _) { todo!() }
523///         fn use_hook(self) -> MyValueType {
524///             self.0
525///         }
526///     }
527/// );
528/// ```
529///
530/// </details>
531///
532/// <details><summary>
533///
534/// impl [`IntoHook`] with [`into_hook`](crate::IntoHook::into_hook)
535///
536/// </summary>
537///
538/// ```
539/// # use hooks_core::impl_hook; struct UseMyHook(i32); struct MyHook(i32);
540/// # impl_hook!( impl MyHook { fn unmount() {} fn poll_next_update(self, cx: _) { todo!() } fn use_hook(self) {} });
541/// impl_hook!(
542///     impl<__> UseMyHook {
543///         fn into_hook(self) -> MyHook {
544///             MyHook(self.0)
545///         }
546///     }
547/// );
548/// ```
549///
550/// </details>
551///
552/// <details><summary>
553///
554/// impl [`UpdateHook`] with [`update_hook`](crate::UpdateHook::update_hook)
555///
556/// </summary>
557///
558/// Note that [`UpdateHook`] requires [`IntoHook`].
559///
560/// ```
561/// # use hooks_core::impl_hook; struct UseMyHook(i32); struct MyHook(i32);
562/// # impl_hook!( impl MyHook { fn unmount() {} fn poll_next_update(self, cx: _) { todo!() } fn use_hook(self) {} });
563/// impl_hook!(
564///     impl<__> UseMyHook {
565///         fn into_hook(self) -> MyHook {
566///             MyHook(self.0)
567///         }
568///         fn update_hook(self, mut hook: _) {
569///             hook.0 = self.0
570///         }
571///     }
572/// );
573/// ```
574///
575/// </details>
576///
577/// <details><summary>
578///
579/// impl [`UpdateHookUninitialized`] with [`h`](crate::UpdateHookUninitialized::h)
580///
581/// </summary>
582///
583/// Note that [`UpdateHookUninitialized`] requires [`UpdateHook`] + [`IntoHook`].
584///
585/// ```
586/// # use hooks_core::impl_hook; struct UseMyHook(i32); struct MyHook(i32);
587/// # #[derive(Default)] struct MyHookUninitialized(Option<i32>);
588/// # impl_hook!( impl MyHook { fn unmount() {} fn poll_next_update(self, cx: _) { todo!() } fn use_hook(self) {} });
589/// # impl_hook!( impl MyHookUninitialized { fn unmount() {} fn poll_next_update(self, cx: _) { todo!() } });
590/// impl_hook!(
591///     impl<__> UseMyHook {
592///         fn into_hook(self) -> MyHook {
593///             MyHook(self.0)
594///         }
595///         fn update_hook(self, mut hook: _) {
596///             hook.0 = self.0
597///         }
598///         fn h(self, mut hook: MyHookUninitialized) {
599///             hook.0.get_or_insert(self.0);
600///         }
601///     }
602/// );
603/// ```
604///
605/// </details>
606///
607/// [`HookUnmount`]: crate::HookUnmount
608/// [`HookPollNextUpdate`]: crate::HookPollNextUpdate
609/// [`HookValue`]: crate::HookValue
610/// [`Hook`]: trait@crate::Hook
611/// [`IntoHook`]: crate::IntoHook
612/// [`UpdateHook`]: crate::UpdateHook
613/// [`UpdateHookUninitialized`]: trait@crate::UpdateHookUninitialized
614#[macro_export]
615macro_rules! impl_hook {
616    (
617        // this empty generic helps with rustfmt
618        impl <$(__)?> $($rest:tt)*
619    ) => {
620        $crate::__impl_impl_hook_generics_consumed! {
621            before_gt {}
622            gt_and_rest {> $($rest)*}
623        }
624    };
625    (
626        impl < $($generics_and_rest:tt)*
627    ) => {
628        $crate::__private::consume_till_outer_gt! {
629            on_finish { $crate::__impl_impl_hook_generics_consumed! }
630            input { $($generics_and_rest)* }
631        }
632    };
633    (
634        impl $($rest:tt)*
635    ) => {
636        $crate::__impl_impl_hook_generics_consumed! {
637            before_gt {}
638            gt_and_rest {> $($rest)*}
639        }
640    };
641}
642
643/// Expands to an opaque type [`impl Hook`](trait@crate::Hook)
644/// with type of [`Value`](crate::HookValue::Value).
645#[macro_export]
646macro_rules! Hook {
647    ($value:ty $(, $($bounds:tt)*)? ) => {
648        $crate::__impl_capture_lifetimes![
649            [
650                impl $crate::Hook + for<'hook> $crate::HookValue<'hook, Value = $value>
651            ]
652            { $($($bounds)*)? }
653        ]
654    };
655}
656
657/// Expands to an opaque type [`impl UpdateHookUninitialized`](trait@crate::UpdateHookUninitialized)
658/// with type of [`Hook`](crate::IntoHook::Hook)`::`[`Value`](crate::HookValue::Value).
659#[macro_export]
660macro_rules! UpdateHookUninitialized {
661    ($value:ty $(, $($bounds:tt)*)?) => {
662        impl $crate::UpdateHookUninitialized<
663            Uninitialized = $crate::__impl_capture_lifetimes![
664                [
665                    impl $crate::HookPollNextUpdate
666                        + $crate::HookUnmount
667                        + ::core::default::Default
668                ]
669                { $($($bounds)*)? }
670            ],
671            Hook = $crate::Hook![$value $(, $($bounds)*)?]
672        >
673    };
674    // Implementation details
675    (@ extract_lifetimes_from_generics {
676        value! { $value:ty }
677        $(generics_info! {
678            $({
679                $(lifetime_attrs $lifetime_attrs:tt)?
680                lifetime {$lt:lifetime}
681                $($rest:tt)*
682            })*
683            $({
684                $(const_attrs $const_attrs:tt)?
685                $(type_attrs $type_attrs:tt)?
686                $(const $const:tt)?
687                name $name:tt
688                $($rest2:tt)*
689            })*
690        })?
691        // explicitly specified lifetime bounds
692        bounds! { $($bounds:tt)* }
693    }) => {
694        $crate::UpdateHookUninitialized![
695            $value,
696            $(
697                $( $lt + )*
698            )?
699            $($bounds)*
700        ]
701    };
702}