1use std::{marker::PhantomData, pin::Pin};
2
3use crate::{HookPollNextUpdate, HookUnmount};
4
5mod sealed {
6 pub trait Initialized: Default {
7 fn is_initialized(this: &Self) -> bool;
8 fn mark_as_initialized(this: &mut Self);
9 }
10
11 impl Initialized for () {
12 #[inline(always)]
13 fn is_initialized(_: &Self) -> bool {
14 true
15 }
16
17 #[inline(always)]
18 fn mark_as_initialized(_: &mut Self) {}
19 }
20
21 impl Initialized for bool {
22 #[inline(always)]
23 fn is_initialized(this: &Self) -> bool {
24 *this
25 }
26
27 #[inline(always)]
28 fn mark_as_initialized(this: &mut Self) {
29 *this = true;
30 }
31 }
32}
33
34pin_project_lite::pin_project![
35 #[derive(Default)]
36 pub struct FnHook<InnerHook: Default, U, I: sealed::Initialized> {
37 #[pin]
38 pub inner_hook: InnerHook,
39 pub use_hook: U,
40 pub initialized: I,
41 }
42];
43
44crate::impl_hook![
45 impl<InnerHook, U, I: sealed::Initialized> FnHook<InnerHook, U, I>
46 where
47 InnerHook: Default + HookPollNextUpdate + HookUnmount,
48 for<'hook> U: FnMutOneArg<Pin<&'hook mut InnerHook>>,
49 {
50 fn poll_next_update(self, cx: _) {
51 let this = self.project();
52 if I::is_initialized(this.initialized) {
53 this.inner_hook.poll_next_update(cx)
54 } else {
55 std::task::Poll::Ready(true)
56 }
57 }
58
59 fn unmount(self) {
60 let this = self.project();
61 if I::is_initialized(this.initialized) {
62 this.inner_hook.unmount()
63 }
64 }
65 }
66];
67
68impl<
69 'hook,
70 InnerHook: Default + HookPollNextUpdate + HookUnmount,
71 U: FnMutOneArg<Pin<&'hook mut InnerHook>>,
72 I: sealed::Initialized,
73 > crate::HookValue<'hook> for FnHook<InnerHook, U, I>
74{
75 type Value = U::FnOutput;
76}
77
78impl<
79 InnerHook: Default + HookPollNextUpdate + HookUnmount,
80 U: for<'hook> FnMutOneArg<Pin<&'hook mut InnerHook>>,
81 I: sealed::Initialized,
82 > crate::Hook for FnHook<InnerHook, U, I>
83{
84 #[inline]
85 fn use_hook(
86 self: Pin<&mut Self>,
87 ) -> <U as FnMutOneArg<std::pin::Pin<&mut InnerHook>>>::FnOutput {
88 let this = self.project();
89 I::mark_as_initialized(this.initialized);
90 this.use_hook.call_mut_with_one_arg(this.inner_hook)
91 }
92}
93
94pin_project_lite::pin_project![
95 pub struct FnHookUninitialized<InnerHook: Default, U> {
96 #[pin]
97 inner_hook: InnerHook,
98 use_hook: Option<U>,
99 }
100];
101
102impl<InnerHook: Default, U> Default for FnHookUninitialized<InnerHook, U> {
103 fn default() -> Self {
104 Self {
105 inner_hook: Default::default(),
106 use_hook: None,
107 }
108 }
109}
110
111crate::impl_hook![
112 impl<InnerHook, U> FnHookUninitialized<InnerHook, U>
113 where
114 InnerHook: Default + HookPollNextUpdate + HookUnmount,
115 for<'hook> U: FnMutOneArg<Pin<&'hook mut InnerHook>>,
116 {
117 fn poll_next_update(self, cx: _) {
118 let this = self.project();
119 if this.use_hook.is_some() {
120 this.inner_hook.poll_next_update(cx)
121 } else {
122 std::task::Poll::Ready(true)
123 }
124 }
125 fn unmount(self) {
126 let this = self.project();
127 if this.use_hook.is_some() {
128 this.inner_hook.unmount()
129 }
130 }
131 }
132];
133
134pub mod use_fn_hook {
135 use super::*;
136 pub mod pin {
137 use super::super::*;
138 pub struct UseFnHook<
139 InnerHook: Default + HookPollNextUpdate + HookUnmount,
140 U: for<'hook> FnMutOneArg<Pin<&'hook mut InnerHook>>,
141 >(pub U, pub PhantomData<InnerHook>);
142
143 crate::impl_hook![
144 impl<InnerHook, U> UseFnHook<InnerHook, U>
145 where
146 InnerHook: Default + HookPollNextUpdate + HookUnmount,
147 for<'hook> U: FnMutOneArg<Pin<&'hook mut InnerHook>>,
148 {
149 fn into_hook(self) -> FnHook<InnerHook, U, bool> {
150 FnHook {
151 inner_hook: Default::default(),
152 use_hook: self.0,
153 initialized: false,
154 }
155 }
156
157 fn update_hook(self, hook: _) {
158 let hook = hook.project();
159 if !*hook.initialized {
160 *hook.initialized = true;
161 let _ = hook.use_hook.call_mut_with_one_arg(hook.inner_hook);
163 }
164 *hook.use_hook = self.0;
165 }
166
167 #[inline]
168 fn h(self, hook: FnHookUninitialized<InnerHook, U>) {
169 let hook = hook.project();
170 let use_hook = hook.use_hook.insert(self.0);
171 use_hook.call_mut_with_one_arg(hook.inner_hook)
172 }
173 }
174 ];
175 }
176
177 pub fn pin<
178 Value: for<'hook> crate::HookValue<'hook>,
179 InnerHook: Default + HookPollNextUpdate + HookUnmount,
180 U: for<'hook> FnMut(Pin<&'hook mut InnerHook>) -> <Value as crate::HookValue<'hook>>::Value,
181 >(
182 use_hook: U,
183 ) -> pin::UseFnHook<InnerHook, U> {
184 pin::UseFnHook(use_hook, PhantomData)
185 }
186
187 pub(super) mod prelude_name {
188 pub use super::pin as use_fn_hook;
189 }
190}
191
192pub use use_fn_hook::prelude_name::*;
193
194pub trait FnMutOneArg<Arg> {
195 type FnOutput;
196 fn call_mut_with_one_arg(&mut self, arg: Arg) -> Self::FnOutput;
197}
198
199impl<F, Arg, R> FnMutOneArg<Arg> for F
200where
201 F: FnMut(Arg) -> R,
202{
203 type FnOutput = R;
204
205 #[inline(always)]
206 fn call_mut_with_one_arg(&mut self, arg: Arg) -> Self::FnOutput {
207 self(arg)
208 }
209}