lgui-core 0.2.0

Platform-neutral runtime and UI primitives for LGUI
Documentation
use std::{
    cell::RefCell,
    future::Future,
    pin::Pin,
    task::{Context, Poll},
};

use super::ApplicationContext;

thread_local! {
    static CURRENT_APPLICATION: RefCell<Vec<ApplicationContext>> = const { RefCell::new(Vec::new()) };
}

pub(crate) struct ApplicationScopeFuture<F> {
    application: ApplicationContext,
    future: Pin<Box<F>>,
}

impl<F> ApplicationScopeFuture<F> {
    pub(crate) fn new(application: ApplicationContext, future: F) -> Self {
        Self {
            application,
            future: Box::pin(future),
        }
    }
}

impl<F> Future for ApplicationScopeFuture<F>
where
    F: Future,
{
    type Output = F::Output;

    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
        let this = self.get_mut();
        let _guard = enter(this.application.clone());
        this.future.as_mut().poll(cx)
    }
}

struct ApplicationScopeGuard;

fn enter(application: ApplicationContext) -> ApplicationScopeGuard {
    CURRENT_APPLICATION.with(|current| current.borrow_mut().push(application));
    ApplicationScopeGuard
}

pub(crate) fn try_current_application() -> Option<ApplicationContext> {
    CURRENT_APPLICATION.with(|current| current.borrow().last().cloned())
}

pub(crate) fn current_application(operation: &str) -> ApplicationContext {
    try_current_application().unwrap_or_else(|| {
        panic!(
            "`{operation}` requires an active LGUI Application scope; run the future through ApplicationContext::scope or an LGUI task API"
        )
    })
}

impl Drop for ApplicationScopeGuard {
    fn drop(&mut self) {
        CURRENT_APPLICATION.with(|current| {
            current
                .borrow_mut()
                .pop()
                .expect("LGUI Application scope stack underflow");
        });
    }
}

#[cfg(test)]
#[path = "scope_test.rs"]
mod tests;