future_form_ffi 0.1.0

FFI support for future_form: host-driven polling, opaque handles, and effect slots
Documentation
//! The [`PollOnce`] extension trait for host-driven polling.
//!
//! This trait adds a [`poll_once`](PollOnce::poll_once) method to boxed
//! futures, allowing foreign hosts (Go, Java, Python, C, Swift) to drive
//! Rust async state machines without an async runtime.
//!
//! # Example
//!
//! ```rust
//! use core::task::Poll;
//! use future_form::{FutureForm, Sendable, Local};
//! use future_form_ffi::poll_once::PollOnce;
//!
//! // Works on Sendable futures
//! let mut fut = Sendable::from_future(async { 42u32 });
//! assert_eq!(fut.poll_once(), Poll::Ready(42));
//!
//! // Works on Local futures too
//! let mut fut = Local::from_future(async { 42u32 });
//! assert_eq!(fut.poll_once(), Poll::Ready(42));
//! ```

use alloc::boxed::Box;
use core::{
    pin::Pin,
    task::{Context, Poll, Waker},
};

use futures::future::{BoxFuture, LocalBoxFuture};

/// Poll a boxed future once using a no-op waker.
///
/// This is the core primitive for host-driven (sans-IO) async execution.
/// Instead of handing a future to an async runtime, the foreign host calls
/// `poll_once()` repeatedly until the future resolves.
///
/// # Why a trait instead of [`Future`]?
///
/// `PollOnce` looks structurally similar to [`Future::poll`], but differs
/// in two ways that make it a better fit for FFI host-driven polling:
///
/// |          | [`Future::poll`]                   | `PollOnce::poll_once`       |
/// |----------|------------------------------------|-----------------------------|
/// | Receiver | `Pin<&mut Self>`                   | `&mut self`                 |
/// | Waker    | Caller supplies `&mut Context<'_>` | Hardcoded [`Waker::noop()`] |
///
/// **`&mut self` instead of `Pin<&mut Self>`** — the implementations only
/// exist for _already-pinned_ types (`BoxFuture` is
/// `Pin<Box<dyn Future>>`), so the caller never needs to pin anything
/// manually. This keeps FFI bridge code simple.
///
/// **No `Context` / `Waker` parameter** — host-driven polling has no
/// async runtime to wake. The host decides when to re-poll (typically
/// after fulfilling an effect). Baking in [`Waker::noop()`] removes a
/// concept that is meaningless in the FFI context.
///
/// A free function could provide the same _functionality_, but a trait
/// gives uniform `.poll_once()` method syntax across [`BoxFuture`] and
/// [`LocalBoxFuture`] without requiring wrapper boilerplate in every
/// bridge crate.
///
/// # Noop waker
///
/// [`Waker::noop()`] does nothing on `wake()`. This is correct for
/// host-driven polling because the host controls the polling cadence.
///
/// Futures that depend on wake notifications to make progress (e.g., tokio
/// channels, timers) will _not_ advance under noop polling. Host-driven
/// futures should use [`core::future::poll_fn`] and check shared state
/// (atomics, mutexes) directly.
///
/// # Example
///
/// ```rust
/// use core::task::Poll;
/// use future_form::{FutureForm, Sendable, Local};
/// use future_form_ffi::poll_once::PollOnce;
///
/// // Works on Sendable futures
/// let mut fut = Sendable::from_future(async { 42u32 });
/// assert_eq!(fut.poll_once(), Poll::Ready(42));
///
/// // Works on Local futures too
/// let mut fut = Local::from_future(async { 42u32 });
/// assert_eq!(fut.poll_once(), Poll::Ready(42));
/// ```
pub trait PollOnce {
    /// The output type of the underlying future.
    type Output;

    /// Poll the future once using a no-op waker.
    ///
    /// Returns [`Poll::Ready(T)`] when the future has resolved, or
    /// [`Poll::Pending`] if it needs to be polled again (e.g., after the
    /// host has fulfilled an effect).
    fn poll_once(&mut self) -> Poll<Self::Output>;
}

impl<T> PollOnce for BoxFuture<'_, T> {
    type Output = T;

    fn poll_once(&mut self) -> Poll<T> {
        let waker = Waker::noop();
        let mut cx = Context::from_waker(waker);
        Pin::new(self).poll(&mut cx)
    }
}

impl<T> PollOnce for LocalBoxFuture<'_, T> {
    type Output = T;

    fn poll_once(&mut self) -> Poll<T> {
        let waker = Waker::noop();
        let mut cx = Context::from_waker(waker);
        Pin::new(self).poll(&mut cx)
    }
}

impl<T> PollOnce for Box<BoxFuture<'_, T>> {
    type Output = T;

    fn poll_once(&mut self) -> Poll<T> {
        (**self).poll_once()
    }
}

impl<T> PollOnce for Box<LocalBoxFuture<'_, T>> {
    type Output = T;

    fn poll_once(&mut self) -> Poll<T> {
        (**self).poll_once()
    }
}