use std::borrow::Cow;
use std::pin::Pin;
use http::Method;
use topcoat_core::{context::Cx, error::Result};
use topcoat_view::View;
use crate::{Body, IntoResponse, Path, Route, RouteFuture};
pub type PageRenderFn = for<'cx> fn(
cx: &'cx Cx,
body: Body,
) -> Pin<Box<dyn Future<Output = Result<View>> + Send + 'cx>>;
#[derive(Debug, Clone)]
pub struct PageFn {
path: Cow<'static, Path>,
render: PageRenderFn,
}
impl PageFn {
pub const fn new(path: Cow<'static, Path>, render: PageRenderFn) -> Self {
Self { path, render }
}
#[must_use]
pub fn path(&self) -> &Path {
&self.path
}
pub fn render<'cx>(
&self,
cx: &'cx Cx,
body: Body,
) -> Pin<Box<dyn Future<Output = Result<View>> + Send + 'cx>> {
(self.render)(cx, body)
}
}
#[cfg(feature = "discover")]
inventory::collect!(PageFn);
pub type LayoutRenderFn = for<'cx> fn(
cx: &'cx Cx,
slot: Slot<'cx>,
) -> Pin<Box<dyn Future<Output = Result<View>> + Send + 'cx>>;
pub type Slot<'cx> = Pin<Box<dyn Future<Output = Result<View>> + Send + 'cx>>;
#[derive(Debug, Clone)]
pub struct LayoutFn {
path: Cow<'static, Path>,
render: LayoutRenderFn,
}
impl LayoutFn {
pub const fn new(path: Cow<'static, Path>, render: LayoutRenderFn) -> Self {
Self { path, render }
}
#[must_use]
pub fn path(&self) -> &Path {
&self.path
}
pub fn render<'cx>(
&self,
cx: &'cx Cx,
slot: Slot<'cx>,
) -> Pin<Box<dyn Future<Output = Result<View>> + Send + 'cx>> {
(self.render)(cx, slot)
}
}
#[cfg(feature = "discover")]
inventory::collect!(LayoutFn);
pub struct PageWithLayouts {
page: PageFn,
layouts: Vec<LayoutFn>,
}
impl PageWithLayouts {
#[must_use]
pub fn new(page: PageFn, layouts: Vec<LayoutFn>) -> Self {
Self { page, layouts }
}
}
impl Route for PageWithLayouts {
fn method(&self) -> Method {
Method::GET
}
fn path(&self) -> &Path {
&self.page.path
}
fn handle<'cx>(&'cx self, cx: &'cx Cx, body: Body) -> RouteFuture<'cx> {
Box::pin(async move {
let mut render = self.page.render(cx, body);
for layout in self.layouts.iter().rev() {
render = layout.render(cx, render);
}
let view = render.await?;
view.into_response(cx)
})
}
}