vertigo 0.11.4

Reactive Real-DOM library with SSR for Rust
Documentation
use std::{
    future::Future,
    mem::ManuallyDrop,
    pin::Pin,
    rc::Rc,
    task::{Context, RawWaker, RawWakerVTable, Waker},
};

use crate::{computed::struct_mut::ValueMut, driver_module::api::api_timers};

#[inline]
pub fn spawn_local<F>(future: F)
where
    F: Future<Output = ()> + 'static,
{
    Task::spawn(Box::pin(future));
}

struct Inner {
    future: Pin<Box<dyn Future<Output = ()>>>,
    waker: Waker,
}

pub(crate) struct Task {
    inner: ValueMut<Option<Inner>>,
}

impl Task {
    pub(crate) fn spawn(future: Pin<Box<dyn Future<Output = ()>>>) {
        let this = Rc::new(Self {
            inner: ValueMut::new(None),
        });

        let waker = unsafe { Waker::from_raw(Task::into_raw_waker(Rc::clone(&this))) };

        this.inner.set(Some(Inner { future, waker }));

        Task::wake_by_ref(&this);
    }

    fn wake_by_ref(this: &Rc<Self>) {
        let this_clone = this.clone();

        api_timers().set_timeout_and_detach(0, move || {
            this_clone.run();
        });
    }

    fn into_raw_waker(this: Rc<Self>) -> RawWaker {
        fn raw_clone(ptr: *const ()) -> RawWaker {
            let ptr = ManuallyDrop::new(unsafe { Rc::from_raw(ptr as *const Task) });
            Task::into_raw_waker((*ptr).clone())
        }

        fn raw_wake(ptr: *const ()) {
            let ptr = unsafe { Rc::from_raw(ptr as *const Task) };
            Task::wake_by_ref(&ptr);
        }

        fn raw_wake_by_ref(ptr: *const ()) {
            let ptr = ManuallyDrop::new(unsafe { Rc::from_raw(ptr as *const Task) });
            Task::wake_by_ref(&ptr);
        }

        fn raw_drop(ptr: *const ()) {
            drop(unsafe { Rc::from_raw(ptr as *const Task) });
        }

        const VTABLE: RawWakerVTable =
            RawWakerVTable::new(raw_clone, raw_wake, raw_wake_by_ref, raw_drop);

        RawWaker::new(Rc::into_raw(this) as *const (), &VTABLE)
    }

    fn run(&self) {
        self.inner.change(|borrow| {
            let inner = match borrow.as_mut() {
                Some(inner) => inner,
                None => return,
            };

            let poll = {
                let mut cx = Context::from_waker(&inner.waker);
                inner.future.as_mut().poll(&mut cx)
            };

            if poll.is_ready() {
                *borrow = None;
            }
        });
    }
}