hooks_core/
fn_hook.rs

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                        // value is dropped
162                        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}