vertigo 0.1.1

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

use crate::ApiImport;
use crate::struct_mut::ValueMut;

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

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

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

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

        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();

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

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

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

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

        unsafe fn raw_drop(ptr: *const ()) {
            drop(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;
            }
        });
    }
}