hooks 3.0.0-alpha.15

Compile-time, async hooks
Documentation
#[cfg(feature = "use_shared_set")]
pub use shared::*;

#[cfg(feature = "use_gen_set")]
pub use gen::*;

use super::UpdateState;

#[derive(Debug)]
pub struct Set<T> {
    pub new_value: Option<T>,
}

impl<S> Set<S> {
    pub fn set(&mut self, new_value: S) {
        self.new_value = Some(new_value)
    }
}

impl<T> Default for Set<T> {
    fn default() -> Self {
        Self { new_value: None }
    }
}

impl<T> UpdateState<T> for Set<T> {
    fn update_state(&mut self, state: &mut T) -> bool {
        if let Some(this) = self.new_value.take() {
            *state = this;
            true
        } else {
            false
        }
    }
}

#[cfg(feature = "use_shared_set")]
mod shared {
    use super::{
        super::{
            use_shared_update_state, use_shared_update_state_with, SharedUpdateState,
            UseSharedUpdateState, UseSharedUpdateStateWith,
        },
        Set,
    };

    pub type SharedSet<S> = SharedUpdateState<Set<S>>;

    impl<S> SharedSet<S> {
        pub fn set(&self, new_value: S) {
            self.map_mut_update_state(|v| v.set(new_value))
        }
    }

    pub type UseSharedSet<S> = UseSharedUpdateState<S, Set<S>>;
    pub fn use_shared_set<S>(initial_state: S) -> UseSharedSet<S> {
        use_shared_update_state(initial_state, Set::default())
    }

    pub type UseSharedSetWith<S, F> = UseSharedUpdateStateWith<S, Set<S>, F>;
    pub fn use_shared_set_with<S>(
        get_initial_state: impl FnOnce() -> S,
    ) -> UseSharedSetWith<S, impl FnOnce() -> (S, SharedSet<S>)> {
        use_shared_update_state_with(move || (get_initial_state(), Set::default()))
    }

    #[cfg(test)]
    mod tests {
        use crate::{Hook, HookPollNextUpdateExt, IntoHook};

        #[test]
        fn set() {
            futures_lite::future::block_on(async {
                let hook = super::use_shared_set(1).into_hook();
                futures_lite::pin!(hook);

                assert!(!std::future::poll_fn(|cx| hook.poll_next_update(cx)).await);

                let (state, updater) = hook.as_mut().use_hook();
                assert_eq!(*state, 1);
                updater.set(2);
                assert_eq!(*state, 1);

                assert!(std::future::poll_fn(|cx| hook.poll_next_update(cx)).await);
                let (state, _updater) = hook.as_mut().use_hook();
                assert_eq!(*state, 2);

                assert!(!std::future::poll_fn(|cx| hook.poll_next_update(cx)).await);
            });
        }

        #[cfg(feature = "futures-core")]
        #[cfg(feature = "use_effect")]
        #[test]
        fn hook_fn_state_2() {
            use futures_lite::StreamExt;
            use hooks_core::{hook_fn, IntoHook};

            use crate::{use_shared_set, use_shared_set_with};

            hook_fn!(
                fn use_state_2() -> (i32, i32) {
                    let (state_1, updater_1) = h!(use_shared_set(1));
                    let (state_2, updater_2) = h!(use_shared_set_with(|| *state_1 + 1));

                    let ret = (*state_1, *state_2);

                    let updater_1 = updater_1.clone();
                    let updater_2 = updater_2.clone();
                    h![crate::use_effect(
                        move |(v1, v2): &_| {
                            if *v2 > 10 {
                                return;
                            }
                            updater_1.set(*v2);
                            updater_2.set(*v1 + *v2);
                        },
                        ret,
                    )];

                    ret
                }
            );

            futures_lite::future::block_on(async {
                let values = use_state_2().into_hook_values().collect::<Vec<_>>().await;

                assert_eq!(values, [(1, 2), (2, 3), (3, 5), (5, 8), (8, 13)]);
            });
        }

        #[cfg(feature = "futures-core")]
        #[cfg(feature = "use_effect")]
        #[cfg(feature = "proc-macro")]
        #[test]
        fn state_2() {
            use futures_lite::StreamExt;
            use hooks_core::IntoHook;
            use hooks_macro::hook;

            use super::{use_shared_set, use_shared_set_with};

            #[hook(hooks_core_path = "::hooks_core")]
            fn use_state_2() -> (i32, i32) {
                let (state_1, updater_1) = use_shared_set(1);
                let (state_2, updater_2) = use_shared_set_with(|| *state_1 + 1);

                let ret = (*state_1, *state_2);

                let updater_1 = updater_1.clone();
                let updater_2 = updater_2.clone();
                crate::use_effect(
                    move |(v1, v2): &_| {
                        if *v2 > 10 {
                            return;
                        }
                        updater_1.set(*v2);
                        updater_2.set(*v1 + *v2);
                    },
                    ret,
                );

                ret
            }

            futures_lite::future::block_on(async {
                let values = use_state_2().into_hook_values().collect::<Vec<_>>().await;

                assert_eq!(values, [(1, 2), (2, 3), (3, 5), (5, 8), (8, 13)]);
            });
        }
    }
}

#[cfg(feature = "use_gen_set")]
mod gen {
    use super::{
        super::{
            use_gen_update_state, use_gen_update_state_with, GenUpdateStateKey,
            GenUpdateStateOwner, UseGenUpdateState, UseGenUpdateStateWith,
        },
        Set,
    };

    pub type GenSetOwner<S> = GenUpdateStateOwner<Set<S>>;
    pub type GenSetKey<S> = GenUpdateStateKey<Set<S>>;

    impl<S> GenSetKey<S> {
        pub fn set(&self, new_value: S) {
            self.map_mut_update_state(|v| v.set(new_value))
        }
    }

    pub type UseGenSet<S> = UseGenUpdateState<S, Set<S>>;
    pub fn use_gen_set<S>(initial_state: S) -> UseGenSet<S> {
        use_gen_update_state(initial_state, Set::default())
    }

    pub type UseGenSetWith<F> = UseGenUpdateStateWith<F>;
    pub fn use_gen_set_with<S>(
        get_initial_state: impl FnOnce() -> S,
    ) -> UseGenSetWith<impl FnOnce() -> (S, Set<S>)> {
        use_gen_update_state_with(move || (get_initial_state(), Set::default()))
    }

    #[cfg(test)]
    mod tests {
        use crate::{utils::testing::assert_always_pending, Hook, HookPollNextUpdateExt, IntoHook};

        #[test]
        fn set() {
            futures_lite::future::block_on(async {
                let hook = super::use_gen_set(1).into_hook();
                futures_lite::pin!(hook);

                assert_always_pending(|| std::future::poll_fn(|cx| hook.poll_next_update(cx)));

                let (state, updater) = hook.as_mut().use_hook();
                assert_eq!(*state, 1);
                updater.set(2);
                assert_eq!(*state, 1);

                assert!(std::future::poll_fn(|cx| hook.poll_next_update(cx)).await);
                let (state, _updater) = hook.as_mut().use_hook();
                assert_eq!(*state, 2);

                assert_always_pending(|| std::future::poll_fn(|cx| hook.poll_next_update(cx)));
            });
        }

        #[cfg(feature = "futures-core")]
        #[cfg(feature = "use_effect")]
        #[test]
        fn hook_fn_state_2() {
            use futures_lite::StreamExt;
            use hooks_core::{hook_fn, IntoHook};

            use super::{use_gen_set, use_gen_set_with};

            hook_fn!(
                fn use_state_2() -> (i32, i32) {
                    let (state_1, updater_1) = h!(use_gen_set(1));
                    let (state_2, updater_2) = h!(use_gen_set_with(|| *state_1 + 1));

                    let ret = (*state_1, *state_2);

                    h![crate::use_effect(
                        move |(v1, v2): &_| {
                            if *v2 > 10 {
                                return;
                            }
                            updater_1.set(*v2);
                            updater_2.set(*v1 + *v2);
                        },
                        ret,
                    )];

                    ret
                }
            );

            futures_lite::future::block_on(async {
                let mut hook_values = use_state_2().into_hook_values();
                let values = (&mut hook_values).take(5).collect::<Vec<_>>().await;

                assert_eq!(values, [(1, 2), (2, 3), (3, 5), (5, 8), (8, 13)]);

                assert_always_pending(|| hook_values.next())
            });
        }

        #[cfg(feature = "futures-core")]
        #[cfg(feature = "use_effect")]
        #[cfg(feature = "proc-macro")]
        #[test]
        fn state_2() {
            use futures_lite::StreamExt;
            use hooks_core::IntoHook;
            use hooks_macro::hook;

            use super::{use_gen_set, use_gen_set_with};

            #[hook(hooks_core_path = "::hooks_core")]
            fn use_state_2() -> (i32, i32) {
                let (state_1, updater_1) = use_gen_set(1);
                let (state_2, updater_2) = use_gen_set_with(|| *state_1 + 1);

                let ret = (*state_1, *state_2);

                crate::use_effect(
                    move |(v1, v2): &_| {
                        if *v2 > 10 {
                            return;
                        }
                        updater_1.set(*v2);
                        updater_2.set(*v1 + *v2);
                    },
                    ret,
                );

                ret
            }

            futures_lite::future::block_on(async {
                let mut hook_values = use_state_2().into_hook_values();
                let values = (&mut hook_values).take(5).collect::<Vec<_>>().await;

                assert_eq!(values, [(1, 2), (2, 3), (3, 5), (5, 8), (8, 13)]);

                assert_always_pending(|| hook_values.next())
            });
        }
    }
}