topcoat-view 0.8.1

A modular, batteries-included Rust web framework for server-rendered apps.
Documentation
use std::{
    pin::Pin,
    task::{Context, Poll, ready},
};

use pin_project_lite::pin_project;
use topcoat_core::error::Result;

use crate::{View, ViewFirst, ViewSwap};

pin_project! {
    /// A [`View`] built from a [`Future`] that resolves to one.
    ///
    /// The view awaits the future first and then polls the view it resolved
    /// to in place. A component invocation becomes one: the component's
    /// body is a future returning its view.
    #[project = ThenViewProj]
    pub enum ThenView<F, V> {
        Future { #[pin] future: F },
        View { #[pin] view: V },
    }
}

impl<F, V> ThenView<F, V>
where
    F: Future<Output = Result<V>>,
{
    #[must_use]
    pub fn new(future: F) -> Self {
        Self::Future { future }
    }
}

impl<F, V> View for ThenView<F, V>
where
    F: Future<Output = Result<V>> + Send,
    V: View,
{
    fn poll_first(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<ViewFirst>> {
        loop {
            match self.as_mut().project() {
                ThenViewProj::Future { future } => {
                    let view = ready!(future.poll(cx))?;
                    self.as_mut().set(Self::View { view });
                }
                ThenViewProj::View { view } => return view.poll_first(cx),
            }
        }
    }

    fn poll_swap(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<Option<ViewSwap>>> {
        match self.project() {
            ThenViewProj::Future { .. } => panic!(
                "called `.poll_swap` on a `ThenView` that has not yet emitted any `First` content"
            ),
            ThenViewProj::View { view } => view.poll_swap(cx),
        }
    }
}