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}