get types working with nested ErrorBoundary/Suspense

This commit is contained in:
Greg Johnston 2024-04-15 11:11:52 -04:00
parent e11eea1af1
commit 851e1f73fd
32 changed files with 270 additions and 363 deletions

View file

@ -31,30 +31,30 @@ pub fn ErrorTemplate(
// Only the response code for the first error is actually sent from the server
// this may be customized by the specific application
#[cfg(feature = "ssr")]
/*#[cfg(feature = "ssr")]
{
let response = use_context::<ResponseOptions>();
if let Some(response) = response {
response.set_status(errors[0].status_code());
}
}
}*/
view! {
<h1>"Errors"</h1>
<For
// a function that returns the items we're iterating over; a signal is fine
each= move || {errors.clone().into_iter().enumerate()}
// a unique key for each item as a reference
key=|(index, _error)| *index
// renders each item to a view
children=move |error| {
let error_string = error.1.to_string();
let error_code= error.1.status_code();
view! {
<h2>{error_code.to_string()}</h2>
<p>"Error: " {error_string}</p>
}
}
/>
<h1>"Errors"</h1>
<For
// a function that returns the items we're iterating over; a signal is fine
each=move || { errors.clone().into_iter().enumerate() }
// a unique key for each item as a reference
key=|(index, _error)| *index
// renders each item to a view
children=move |error| {
let error_string = error.1.to_string();
let error_code = error.1.status_code();
view! {
<h2>{error_code.to_string()}</h2>
<p>"Error: " {error_string}</p>
}
}
/>
}
}

View file

@ -120,9 +120,39 @@ pub fn Todos() -> impl IntoView {
<div>
// fallback=move || view! { <p>"Loading..."</p> }>
<Suspense>
<Suspense fallback=move || view! { <p>"Loading..."</p> }>
<ErrorBoundary fallback=|errors| view! { <ErrorTemplate errors/> }>
<ul>"foo"// {existing_todos}
// {existing_todos}
<ul>
{move || {
async move {
todos
.await
.map(|todos| {
if todos.is_empty() {
Either::Left(view! { <p>"No tasks were found."</p> })
} else {
Either::Right(
todos
.into_iter()
.map(move |todo| {
view! {
<li>
{todo.title} <ActionForm action=delete_todo>
<input type="hidden" name="id" value=todo.id/>
<input type="submit" value="X"/>
</ActionForm>
</li>
}
})
.collect::<Vec<_>>(),
)
}
})
}
.wait()
}}
// {pending_todos}
</ul>
</ErrorBoundary>

View file

@ -194,15 +194,30 @@ where
impl<Chil, Fal, Rndr> RenderHtml<Rndr> for ErrorBoundaryView<Chil, Fal, Rndr>
where
Chil: RenderHtml<Rndr>,
Fal: RenderHtml<Rndr>,
Fal: RenderHtml<Rndr> + Send,
Rndr: Renderer,
{
type AsyncOutput = std::future::Ready<()>; //ErrorBoundaryView<Chil::AsyncOutput, Fal, Rndr>;
type AsyncOutput = ErrorBoundaryView<Chil::AsyncOutput, Fal, Rndr>;
const MIN_LENGTH: usize = Chil::MIN_LENGTH;
fn resolve(self) -> Self::AsyncOutput {
todo!()
async fn resolve(self) -> Self::AsyncOutput {
let ErrorBoundaryView {
errors_empty,
children,
fallback,
..
} = self;
let children = match children {
None => None,
Some(children) => Some(children.resolve().await),
};
ErrorBoundaryView {
errors_empty,
children,
fallback,
rndr: PhantomData,
}
}
fn to_html_with_buf(self, buf: &mut String, position: &mut Position) {

View file

@ -1,3 +1,4 @@
use crate::into_view::IntoView;
use leptos_macro::component;
use reactive_graph::owner::Owner;
use std::{hash::Hash, marker::PhantomData};
@ -44,7 +45,6 @@ use tachys::{
/// }
/// ```
#[cfg_attr(feature = "tracing", tracing::instrument(level = "trace", skip_all))]
#[component(transparent)]
pub fn For<Rndr, IF, I, T, EF, N, KF, K>(
/// Items over which the component should iterate.
each: IF,
@ -52,17 +52,15 @@ pub fn For<Rndr, IF, I, T, EF, N, KF, K>(
key: KF,
/// A function that takes the item, and returns the view that will be displayed for each item.
children: EF,
#[prop(optional)] _rndr: PhantomData<Rndr>,
) -> impl RenderHtml<Rndr>
) -> impl IntoView
where
IF: Fn() -> I + 'static,
I: IntoIterator<Item = T>,
EF: Fn(T) -> N + Clone + 'static,
N: RenderHtml<Rndr> + 'static,
KF: Fn(&T) -> K + Clone + 'static,
IF: Fn() -> I + Send + 'static,
I: IntoIterator<Item = T> + Send,
EF: Fn(T) -> N + Send + Clone + 'static,
N: IntoView + 'static,
KF: Fn(&T) -> K + Send + Clone + 'static,
K: Eq + Hash + 'static,
T: 'static,
Rndr: Renderer + 'static,
T: Send + 'static,
{
// this takes the owner of the For itself
// this will end up with N + 1 children
@ -72,13 +70,14 @@ where
// this means
// a) the reactive owner for each row will not be cleared when the whole list updates
// b) context provided in each row will not wipe out the others
let parent = Owner::current().expect("no reactive owner");
/*let parent = Owner::current().expect("no reactive owner");
let children = move |child| {
let owner = parent.with(Owner::new);
let view = owner.with(|| children(child));
OwnedView::new_with_owner(view, owner)
};
move || keyed(each(), key.clone(), children.clone())
move || keyed(each(), key.clone(), children.clone())*/
"todo"
}
#[component]
@ -90,7 +89,7 @@ pub fn FlatFor<Rndr, IF, I, T, EF, N, KF, K>(
/// A function that takes the item, and returns the view that will be displayed for each item.
children: EF,
#[prop(optional)] _rndr: PhantomData<Rndr>,
) -> impl RenderHtml<Rndr>
) -> impl IntoView
where
IF: Fn() -> I + 'static,
I: IntoIterator<Item = T>,
@ -101,7 +100,8 @@ where
T: 'static,
Rndr: Renderer + 'static,
{
move || keyed(each(), key.clone(), children.clone())
//move || keyed(each(), key.clone(), children.clone())
"bar"
}
#[cfg(test)]
@ -119,11 +119,7 @@ mod tests {
let values = RwSignal::new(vec![1, 2, 3, 4, 5]);
let list: HtmlElement<_, _, _, MockDom> = view! {
<ol>
<For
each=move || values.get()
key=|i| *i
let:i
>
<For each=move || values.get() key=|i| *i let:i>
<li>{i}</li>
</For>
</ol>

View file

@ -24,7 +24,6 @@ impl<T> View<T> {
pub trait IntoView
where
Self: Sized + Render<Dom> + RenderHtml<Dom> + Send,
<Self::AsyncOutput as Future>::Output: RenderHtml<Dom>,
{
fn into_view(self) -> View<Self>;
}
@ -32,8 +31,6 @@ where
impl<T> IntoView for T
where
T: Sized + Render<Dom> + RenderHtml<Dom> + Send, //+ AddAnyAttr<Dom>,
T::AsyncOutput: Send,
<T::AsyncOutput as Future>::Output: RenderHtml<Dom>,
{
fn into_view(self) -> View<Self> {
View(self)
@ -69,8 +66,8 @@ impl<T: IntoView> RenderHtml<Dom> for View<T> {
const MIN_LENGTH: usize = <T as RenderHtml<Dom>>::MIN_LENGTH;
fn resolve(self) -> Self::AsyncOutput {
self.0.resolve()
async fn resolve(self) -> Self::AsyncOutput {
self.0.resolve().await
}
fn to_html_with_buf(self, buf: &mut String, position: &mut Position) {

View file

@ -1,22 +1,27 @@
use crate::children::{ChildrenFnMut, ViewFn};
use crate::{
children::{TypedChildrenMut, ViewFn},
IntoView,
};
use leptos_macro::component;
use reactive_graph::{computed::ArcMemo, traits::Get};
use tachys::{either::Either, renderer::dom::Dom, view::RenderHtml};
#[component]
pub fn Show<W>(
pub fn Show<W, C>(
/// The children will be shown whenever the condition in the `when` closure returns `true`.
mut children: ChildrenFnMut,
children: TypedChildrenMut<C>,
/// A closure that returns a bool that determines whether this thing runs
when: W,
/// A closure that returns what gets rendered if the when statement is false. By default this is the empty view.
#[prop(optional, into)]
fallback: ViewFn,
) -> impl RenderHtml<Dom>
) -> impl IntoView
where
W: Fn() -> bool + Send + Sync + 'static,
C: IntoView + 'static,
{
let memoized_when = ArcMemo::new(move |_| when());
let mut children = children.into_inner();
move || match memoized_when.get() {
true => Either::Left(children()),

View file

@ -116,18 +116,16 @@ impl<const TRANSITION: bool, Fal, Chil, Rndr> RenderHtml<Rndr>
where
Fal: RenderHtml<Rndr> + Send + 'static,
Chil: RenderHtml<Rndr> + Send + 'static,
Chil::AsyncOutput: Send + 'static,
<Chil::AsyncOutput as Future>::Output: RenderHtml<Rndr>,
Rndr: Renderer + 'static,
{
// i.e., if this is the child of another Suspense during SSR, don't wait for it: it will handle
// itself
type AsyncOutput = Ready<Self>;
type AsyncOutput = Self;
const MIN_LENGTH: usize = Chil::MIN_LENGTH;
fn resolve(self) -> Self::AsyncOutput {
ready(self)
async fn resolve(self) -> Self::AsyncOutput {
self
}
fn to_html_with_buf(self, buf: &mut String, position: &mut Position) {
@ -371,7 +369,7 @@ where
Fut::Output: RenderHtml<Rndr>,
Rndr: Renderer + 'static,
{
type AsyncOutput = Fut;
type AsyncOutput = Fut::Output;
const MIN_LENGTH: usize = Fut::Output::MIN_LENGTH;
@ -425,7 +423,7 @@ where
SuspendState { inner }
}
fn resolve(self) -> Self::AsyncOutput {
self.fut
async fn resolve(self) -> Self::AsyncOutput {
self.fut.await
}
}

View file

@ -125,12 +125,12 @@ impl Render<Dom> for BodyView {
}
impl RenderHtml<Dom> for BodyView {
type AsyncOutput = Ready<Self>;
type AsyncOutput = Self;
const MIN_LENGTH: usize = 0;
fn resolve(self) -> Self::AsyncOutput {
ready(self)
async fn resolve(self) -> Self::AsyncOutput {
self
}
fn to_html_with_buf(self, buf: &mut String, position: &mut Position) {}

View file

@ -138,12 +138,12 @@ impl Render<Dom> for HtmlView {
}
impl RenderHtml<Dom> for HtmlView {
type AsyncOutput = Ready<Self>;
type AsyncOutput = Self;
const MIN_LENGTH: usize = 0;
fn resolve(self) -> Self::AsyncOutput {
ready(self)
async fn resolve(self) -> Self::AsyncOutput {
self
}
fn to_html_with_buf(self, _buf: &mut String, _position: &mut Position) {

View file

@ -354,12 +354,12 @@ where
At: Attribute<Dom>,
Ch: RenderHtml<Dom> + Send,
{
type AsyncOutput = Ready<Self>;
type AsyncOutput = Self;
const MIN_LENGTH: usize = 0;
fn resolve(self) -> Self::AsyncOutput {
ready(self)
async fn resolve(self) -> Self::AsyncOutput {
self
}
fn to_html_with_buf(self, _buf: &mut String, _position: &mut Position) {
@ -460,12 +460,12 @@ impl Render<Dom> for MetaTagsView {
}
impl RenderHtml<Dom> for MetaTagsView {
type AsyncOutput = Ready<Self>;
type AsyncOutput = Self;
const MIN_LENGTH: usize = 0;
fn resolve(self) -> Self::AsyncOutput {
ready(self)
async fn resolve(self) -> Self::AsyncOutput {
self
}
fn to_html_with_buf(self, buf: &mut String, position: &mut Position) {

View file

@ -233,12 +233,12 @@ impl Render<Dom> for TitleView {
}
impl RenderHtml<Dom> for TitleView {
type AsyncOutput = Ready<Self>;
type AsyncOutput = Self;
const MIN_LENGTH: usize = 0;
fn resolve(self) -> Self::AsyncOutput {
ready(self)
async fn resolve(self) -> Self::AsyncOutput {
self
}
fn to_html_with_buf(self, buf: &mut String, position: &mut Position) {

View file

@ -207,13 +207,13 @@ where
Children::Match: std::fmt::Debug,
<Children::Match as MatchInterface<Rndr>>::Child: std::fmt::Debug,
{
type AsyncOutput = Ready<Self>;
type AsyncOutput = Self;
// TODO probably pick a max length here
const MIN_LENGTH: usize = Children::View::MIN_LENGTH;
fn resolve(self) -> Self::AsyncOutput {
ready(self)
async fn resolve(self) -> Self::AsyncOutput {
self
}
fn to_html_with_buf(self, buf: &mut String, position: &mut Position) {
@ -666,12 +666,12 @@ impl<R> RenderHtml<R> for Outlet<R>
where
R: Renderer + 'static,
{
type AsyncOutput = Ready<Self>;
type AsyncOutput = Self;
const MIN_LENGTH: usize = 0; // TODO
fn resolve(self) -> Self::AsyncOutput {
ready(self)
async fn resolve(self) -> Self::AsyncOutput {
self
}
fn html_len(&self) -> usize {
@ -949,12 +949,12 @@ where
Matcher::View: Sized + 'static,
R: Renderer + 'static,
{
type AsyncOutput = Ready<Self>;
type AsyncOutput = Self;
const MIN_LENGTH: usize = Matcher::View::MIN_LENGTH;
fn resolve(self) -> Self::AsyncOutput {
ready(self)
async fn resolve(self) -> Self::AsyncOutput {
self
}
fn html_len(&self) -> usize {
@ -1225,13 +1225,13 @@ where
Fallback::State: 'static,
Rndr: Renderer + 'static,
{
type AsyncOutput = Ready<Self>;
type AsyncOutput = Self;
const MIN_LENGTH: usize =
<Children::Match as MatchInterface<Rndr>>::View::MIN_LENGTH;
fn resolve(self) -> Self::AsyncOutput {
ready(self)
async fn resolve(self) -> Self::AsyncOutput {
self
}
fn to_html_with_buf(self, buf: &mut String, position: &mut Position) {

View file

@ -166,11 +166,11 @@ where
Fut::Output: RenderHtml<Rndr> + Send,
Rndr: Renderer + 'static,
{
type AsyncOutput = Fut;
type AsyncOutput = Fut::Output;
const MIN_LENGTH: usize = Fal::MIN_LENGTH;
fn resolve(self) -> Self::AsyncOutput {
fn resolve(self) -> impl Future<Output = Self::AsyncOutput> + Send {
self.fut
}

View file

@ -98,9 +98,9 @@ where
impl<E, At, Ch, Rndr> AddAnyAttr<Rndr> for HtmlElement<E, At, Ch, Rndr>
where
E: ElementType + CreateElement<Rndr>,
At: Attribute<Rndr>,
Ch: RenderHtml<Rndr>,
E: ElementType + CreateElement<Rndr> + Send,
At: Attribute<Rndr> + Send,
Ch: RenderHtml<Rndr> + Send,
Rndr: Renderer,
{
type Output<SomeNewAttr: Attribute<Rndr>> = HtmlElement<
@ -218,49 +218,15 @@ where
Ok(())
}
}
pin_project_lite::pin_project! {
pub struct HtmlElementFuture<E, At, Ch, Rndr> {
sync_fields: Option<(E, At)>,
#[pin]
children_fut: Ch,
rndr: PhantomData<Rndr>,
}
}
impl<E, At, Ch, Rndr> Future for HtmlElementFuture<E, At, Ch, Rndr>
where
Ch: Future,
{
type Output = HtmlElement<E, At, Ch::Output, Rndr>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.project();
match this.children_fut.poll(cx) {
Poll::Ready(children) => {
let (tag, attributes) = this.sync_fields.take().expect(
"tried to take synchronous fields from HtmlElementFuture \
more than once",
);
Poll::Ready(HtmlElement {
tag,
attributes,
children,
rndr: PhantomData,
})
}
Poll::Pending => Poll::Pending,
}
}
}
impl<E, At, Ch, Rndr> RenderHtml<Rndr> for HtmlElement<E, At, Ch, Rndr>
where
E: ElementType + CreateElement<Rndr>,
At: Attribute<Rndr>,
Ch: RenderHtml<Rndr>,
E: ElementType + CreateElement<Rndr> + Send,
At: Attribute<Rndr> + Send,
Ch: RenderHtml<Rndr> + Send,
Rndr: Renderer,
{
type AsyncOutput = HtmlElementFuture<E, At, Ch::AsyncOutput, Rndr>;
type AsyncOutput = HtmlElement<E, At, Ch::AsyncOutput, Rndr>;
const MIN_LENGTH: usize = if E::SELF_CLOSING {
3 // < ... />
@ -275,11 +241,12 @@ where
+ E::TAG.len()
};
fn resolve(self) -> Self::AsyncOutput {
HtmlElementFuture {
sync_fields: Some((self.tag, self.attributes)),
children_fut: self.children.resolve(),
rndr: self.rndr,
async fn resolve(self) -> Self::AsyncOutput {
HtmlElement {
tag: self.tag,
rndr: PhantomData,
attributes: self.attributes,
children: self.children.resolve().await,
}
}

View file

@ -83,8 +83,8 @@ where
+ "data-component".len()
+ View::MIN_LENGTH;
fn resolve(self) -> Self::AsyncOutput {
self.view.resolve()
async fn resolve(self) -> Self::AsyncOutput {
self.view.resolve().await
}
fn to_html_with_buf(self, buf: &mut String, position: &mut Position) {
@ -187,9 +187,9 @@ where
+ "</>".len()
+ View::MIN_LENGTH;
fn resolve(self) -> Self::AsyncOutput {
async fn resolve(self) -> Self::AsyncOutput {
// TODO should this be wrapped?
self.view.resolve()
self.view.resolve().await
}
fn to_html_with_buf(self, buf: &mut String, position: &mut Position) {

View file

@ -52,12 +52,12 @@ impl<R> RenderHtml<R> for Doctype<R>
where
R: Renderer + Send,
{
type AsyncOutput = Ready<Self>;
type AsyncOutput = Self;
const MIN_LENGTH: usize = "<!DOCTYPE html>".len();
fn resolve(self) -> Self::AsyncOutput {
ready(self)
async fn resolve(self) -> Self::AsyncOutput {
self
}
fn to_html_with_buf(self, buf: &mut String, _position: &mut Position) {

View file

@ -47,12 +47,12 @@ impl<R> RenderHtml<R> for Oco<'static, str>
where
R: Renderer,
{
type AsyncOutput = Ready<Self>;
type AsyncOutput = Self;
const MIN_LENGTH: usize = 0;
fn resolve(self) -> Self::AsyncOutput {
ready(self)
async fn resolve(self) -> Self::AsyncOutput {
self
}
fn to_html_with_buf(self, buf: &mut String, position: &mut Position) {

View file

@ -87,7 +87,7 @@ Ok(())
R: Renderer,
G: Deref<Target = $child_type> + Send
{
type AsyncOutput = Ready<Self>;
type AsyncOutput = Self;
const MIN_LENGTH: usize = 0;
@ -133,8 +133,8 @@ Ok(())
[<ReadGuard $child_type:camel State>](node, *self)
}
fn resolve(self) -> Self::AsyncOutput {
ready(self)
async fn resolve(self) -> Self::AsyncOutput {
self
}
}
@ -247,7 +247,7 @@ where
G: Deref<Target = String> + Send,
{
type AsyncOutput = Ready<Self>;
type AsyncOutput = Self;
const MIN_LENGTH: usize = 0;
@ -269,8 +269,8 @@ where
}
}
fn resolve(self) -> Self::AsyncOutput {
ready(self)
async fn resolve(self) -> Self::AsyncOutput {
self
}
}

View file

@ -207,7 +207,7 @@ where
impl<F, V, R> RenderHtml<R> for F
where
F: FnMut() -> V + 'static,
F: FnMut() -> V + Send + 'static,
V: RenderHtml<R>,
V::State: 'static,
V::FallibleState: 'static,
@ -217,8 +217,8 @@ where
const MIN_LENGTH: usize = 0;
fn resolve(mut self) -> Self::AsyncOutput {
self().resolve()
async fn resolve(mut self) -> Self::AsyncOutput {
self().resolve().await
}
fn html_len(&self) -> usize {
@ -263,7 +263,7 @@ where
impl<F, V, R> AddAnyAttr<R> for F
where
F: FnMut() -> V + 'static,
F: FnMut() -> V + Send + 'static,
V: RenderHtml<R>,
V::State: 'static,
V::FallibleState: 'static,
@ -484,12 +484,12 @@ mod stable {
V::FallibleState: 'static,
R: Renderer + 'static,
{
type AsyncOutput = Ready<Self>;
type AsyncOutput = Self;
const MIN_LENGTH: usize = 0;
fn resolve(self) -> Self::AsyncOutput {
ready(self)
async fn resolve(self) -> Self::AsyncOutput {
self
}
fn html_len(&self) -> usize {
@ -611,12 +611,12 @@ mod stable {
V::FallibleState: 'static,
R: Renderer + 'static,
{
type AsyncOutput = Ready<Self>;
type AsyncOutput = Self;
const MIN_LENGTH: usize = 0;
fn resolve(self) -> Self::AsyncOutput {
ready(self)
async fn resolve(self) -> Self::AsyncOutput {
self
}
fn html_len(&self) -> usize {

View file

@ -99,7 +99,7 @@ where
R: Renderer,
{
// TODO
type AsyncOutput = std::future::Ready<OwnedView<T::AsyncOutput, R>>;
type AsyncOutput = OwnedView<T::AsyncOutput, R>;
const MIN_LENGTH: usize = T::MIN_LENGTH;
@ -136,7 +136,7 @@ where
OwnedViewState::new(state, self.owner)
}
fn resolve(self) -> Self::AsyncOutput {
async fn resolve(self) -> Self::AsyncOutput {
todo!()
}
}

View file

@ -287,9 +287,9 @@ impl<R> RenderHtml<R> for AnyView<R>
where
R: Renderer + 'static,
{
type AsyncOutput = Ready<Self>;
type AsyncOutput = Self;
fn resolve(self) -> Self::AsyncOutput {
async fn resolve(self) -> Self::AsyncOutput {
// we probably do need a function for this
todo!()
}

View file

@ -136,16 +136,12 @@ where
B: RenderHtml<Rndr>,
Rndr: Renderer,
{
type AsyncOutput = EitherFuture<A::AsyncOutput, B::AsyncOutput>;
type AsyncOutput = Either<A::AsyncOutput, B::AsyncOutput>;
fn resolve(self) -> Self::AsyncOutput {
async fn resolve(self) -> Self::AsyncOutput {
match self {
Either::Left(left) => EitherFuture::Left {
inner: left.resolve(),
},
Either::Right(right) => EitherFuture::Right {
inner: right.resolve(),
},
Either::Left(left) => Either::Left(left.resolve().await),
Either::Right(right) => Either::Right(right.resolve().await),
}
}
@ -305,11 +301,11 @@ where
B: RenderHtml<Rndr>,
Rndr: Renderer,
{
type AsyncOutput = EitherFuture<A::AsyncOutput, B::AsyncOutput>;
type AsyncOutput = Either<A::AsyncOutput, B::AsyncOutput>;
const MIN_LENGTH: usize = 0;
fn resolve(self) -> Self::AsyncOutput {
async fn resolve(self) -> Self::AsyncOutput {
todo!()
}
@ -506,13 +502,13 @@ macro_rules! tuples {
$($ty: RenderHtml<Rndr>,)*
Rndr: Renderer,
{
type AsyncOutput = [<EitherOf $num Future>]<$($ty::AsyncOutput,)*>;
type AsyncOutput = [<EitherOf $num>]<$($ty::AsyncOutput,)*>;
const MIN_LENGTH: usize = max_usize(&[$($ty ::MIN_LENGTH,)*]);
fn resolve(self) -> Self::AsyncOutput {
async fn resolve(self) -> Self::AsyncOutput {
match self {
$([<EitherOf $num>]::$ty(this) => [<EitherOf $num Future>]::$ty { inner: this.resolve() },)*
$([<EitherOf $num>]::$ty(this) => [<EitherOf $num>]::$ty(this.resolve().await),)*
}
}

View file

@ -138,16 +138,14 @@ where
R: Renderer,
E: Into<AnyError> + Send + 'static,
{
type AsyncOutput = ResultFuture<T::AsyncOutput, E>;
type AsyncOutput = Result<T::AsyncOutput, E>;
const MIN_LENGTH: usize = T::MIN_LENGTH;
fn resolve(self) -> Self::AsyncOutput {
async fn resolve(self) -> Self::AsyncOutput {
match self {
Ok(view) => ResultFuture::Ok {
inner: view.resolve(),
},
Err(e) => ResultFuture::Err { inner: Some(e) },
Ok(view) => Ok(view.resolve().await),
Err(e) => Err(e),
}
}
@ -204,37 +202,6 @@ where
}
}
pin_project! {
#[project = ResultFutureProj]
pub enum ResultFuture<T, E> {
Ok {
#[pin]
inner: T
},
Err { inner: Option<E> }
}
}
impl<T, E> Future for ResultFuture<T, E>
where
T: Future,
{
type Output = Result<T::Output, E>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.project();
match this {
ResultFutureProj::Err { inner } => Poll::Ready(Err(inner
.take()
.expect("ResultFuture polled twice"))),
ResultFutureProj::Ok { inner } => match inner.poll(cx) {
Poll::Ready(value) => Poll::Ready(Ok(value)),
Poll::Pending => Poll::Pending,
},
}
}
}
/*
pub trait TryCatchBoundary<Fal, FalFn, Rndr>
where
@ -384,7 +351,7 @@ where
const MIN_LENGTH: usize = Fal::MIN_LENGTH;
fn resolve(self) -> Self::AsyncOutput {
async fn resolve(self) -> Self::AsyncOutput {
todo!()
}

View file

@ -85,16 +85,14 @@ where
T: RenderHtml<R>,
R: Renderer,
{
type AsyncOutput = OptionFuture<T::AsyncOutput>;
type AsyncOutput = Option<T::AsyncOutput>;
const MIN_LENGTH: usize = T::MIN_LENGTH;
fn resolve(self) -> Self::AsyncOutput {
async fn resolve(self) -> Self::AsyncOutput {
match self {
None => OptionFuture::None,
Some(value) => OptionFuture::Some {
inner: value.resolve(),
},
None => None,
Some(value) => Some(value.resolve().await),
}
}
@ -200,35 +198,6 @@ where
}
}
pin_project! {
#[project = OptionFutureProj]
pub enum OptionFuture<T> {
None,
Some {
#[pin]
inner: T
}
}
}
impl<T> Future for OptionFuture<T>
where
T: Future,
{
type Output = Option<T::Output>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.project();
match this {
OptionFutureProj::None => Poll::Ready(None),
OptionFutureProj::Some { inner } => match inner.poll(cx) {
Poll::Pending => Poll::Pending,
Poll::Ready(value) => Poll::Ready(Some(value)),
},
}
}
}
impl<T, R> Render<R> for Vec<T>
where
T: Render<R>,
@ -364,15 +333,17 @@ where
impl<T, R> RenderHtml<R> for Vec<T>
where
T: RenderHtml<R>,
<T::AsyncOutput as Future>::Output: Send,
R: Renderer,
{
type AsyncOutput = JoinAll<T::AsyncOutput>;
type AsyncOutput = Vec<T::AsyncOutput>;
const MIN_LENGTH: usize = 0;
fn resolve(self) -> Self::AsyncOutput {
async fn resolve(self) -> Self::AsyncOutput {
futures::future::join_all(self.into_iter().map(T::resolve))
.await
.into_iter()
.collect()
}
fn html_len(&self) -> usize {

View file

@ -8,6 +8,7 @@ use drain_filter_polyfill::VecExt as VecDrainFilterExt;
use indexmap::IndexSet;
use rustc_hash::FxHasher;
use std::{
future::Future,
hash::{BuildHasherDefault, Hash},
marker::PhantomData,
};
@ -143,19 +144,25 @@ where
impl<T, I, K, KF, VF, V, Rndr> RenderHtml<Rndr>
for Keyed<T, I, K, KF, VF, V, Rndr>
where
I: IntoIterator<Item = T>,
I: IntoIterator<Item = T> + Send,
K: Eq + Hash + 'static,
KF: Fn(&T) -> K,
KF: Fn(&T) -> K + Send,
V: RenderHtml<Rndr>,
VF: Fn(T) -> V,
VF: Fn(T) -> V + Send,
Rndr: Renderer,
{
type AsyncOutput = std::future::Ready<()>;
type AsyncOutput = Vec<V::AsyncOutput>; // TODO
const MIN_LENGTH: usize = 0;
fn resolve(self) -> Self::AsyncOutput {
todo!()
async fn resolve(self) -> Self::AsyncOutput {
futures::future::join_all(self.items.into_iter().map(|item| {
let view = (self.view_fn)(item);
view.resolve()
}))
.await
.into_iter()
.collect()
}
fn to_html_with_buf(self, buf: &mut String, position: &mut Position) {

View file

@ -68,13 +68,13 @@ impl std::error::Error for NeverError {}
/// whole view piece by piece.
pub trait RenderHtml<R: Renderer>
where
Self: Render<R>,
Self: Render<R> + Send,
{
type AsyncOutput: Future + Send; //RenderHtml<R> + Send;
type AsyncOutput: RenderHtml<R>;
const MIN_LENGTH: usize;
fn resolve(self) -> Self::AsyncOutput;
fn resolve(self) -> impl Future<Output = Self::AsyncOutput> + Send;
/// An estimated length for this view, when rendered to HTML.
///

View file

@ -76,12 +76,12 @@ macro_rules! render_primitive {
where
R: Renderer,
{
type AsyncOutput = Ready<Self>;
type AsyncOutput = Self;
const MIN_LENGTH: usize = 0;
fn resolve(self) -> Self::AsyncOutput {
ready(self)
async fn resolve(self) -> Self::AsyncOutput {
self
}
fn to_html_with_buf(self, buf: &mut String, position: &mut Position) {

View file

@ -161,12 +161,12 @@ where
R::Text: Mountable<R>,
{
type AsyncOutput = Ready<Self>;
type AsyncOutput = Self;
const MIN_LENGTH: usize = V.len();
fn resolve(self) -> Self::AsyncOutput {
ready(self)
fn resolve(self) -> futures::future::Ready<Self::AsyncOutput> {
futures::future::ready(self)
}
fn to_html_with_buf(self, buf: &mut String, position: &mut Position) {

View file

@ -51,12 +51,12 @@ impl<'a, R> RenderHtml<R> for &'a str
where
R: Renderer,
{
type AsyncOutput = Ready<Self>;
type AsyncOutput = Self;
const MIN_LENGTH: usize = 0;
fn resolve(self) -> Self::AsyncOutput {
ready(self)
async fn resolve(self) -> Self::AsyncOutput {
self
}
fn html_len(&self) -> usize {
@ -185,10 +185,10 @@ where
R: Renderer,
{
const MIN_LENGTH: usize = 0;
type AsyncOutput = Ready<Self>;
type AsyncOutput = Self;
fn resolve(self) -> Self::AsyncOutput {
ready(self)
async fn resolve(self) -> Self::AsyncOutput {
self
}
fn html_len(&self) -> usize {
@ -291,12 +291,12 @@ impl<R> RenderHtml<R> for Rc<str>
where
R: Renderer,
{
type AsyncOutput = Ready<Self>;
type AsyncOutput = Self;
const MIN_LENGTH: usize = 0;
fn resolve(self) -> Self::AsyncOutput {
ready(self)
async fn resolve(self) -> Self::AsyncOutput {
self
}
fn html_len(&self) -> usize {
@ -397,12 +397,12 @@ impl<R> RenderHtml<R> for Arc<str>
where
R: Renderer,
{
type AsyncOutput = Ready<Self>;
type AsyncOutput = Self;
const MIN_LENGTH: usize = 0;
fn resolve(self) -> Self::AsyncOutput {
ready(self)
async fn resolve(self) -> Self::AsyncOutput {
self
}
fn html_len(&self) -> usize {
@ -503,12 +503,12 @@ impl<'a, R> RenderHtml<R> for Cow<'a, str>
where
R: Renderer,
{
type AsyncOutput = Ready<Self>;
type AsyncOutput = Self;
const MIN_LENGTH: usize = 0;
fn resolve(self) -> Self::AsyncOutput {
ready(self)
async fn resolve(self) -> Self::AsyncOutput {
self
}
fn html_len(&self) -> usize {

View file

@ -86,7 +86,7 @@ where
self.view.hydrate::<FROM_SERVER>(cursor, position)
}
fn resolve(self) -> Self::AsyncOutput {
async fn resolve(self) -> Self::AsyncOutput {
todo!()
}
}

View file

@ -41,7 +41,7 @@ impl<R> RenderHtml<R> for ()
where
R: Renderer,
{
type AsyncOutput = Ready<()>;
type AsyncOutput = ();
const MIN_LENGTH: usize = 0;
@ -54,9 +54,7 @@ where
) -> Self::State {
}
fn resolve(self) -> Self::AsyncOutput {
ready(())
}
async fn resolve(self) -> Self::AsyncOutput {}
}
impl<Rndr> AddAnyAttr<Rndr> for ()
@ -140,7 +138,7 @@ where
A: RenderHtml<R>,
R: Renderer,
{
type AsyncOutput = A::AsyncOutput;
type AsyncOutput = (A::AsyncOutput,);
const MIN_LENGTH: usize = A::MIN_LENGTH;
@ -170,8 +168,8 @@ where
self.0.hydrate::<FROM_SERVER>(cursor, position)
}
fn resolve(self) -> Self::AsyncOutput {
self.0.resolve()
async fn resolve(self) -> Self::AsyncOutput {
(self.0.resolve().await,)
}
}
@ -220,7 +218,7 @@ where
}
macro_rules! impl_view_for_tuples {
($joiner:ident => $first:ident, $($ty:ident),* $(,)?) => {
($first:ident, $($ty:ident),* $(,)?) => {
impl<$first, $($ty),*, Rndr> Render<Rndr> for ($first, $($ty,)*)
where
$first: Render<Rndr>,
@ -274,7 +272,7 @@ macro_rules! impl_view_for_tuples {
$($ty: RenderHtml<Rndr>),*,
Rndr: Renderer,
{
type AsyncOutput = $joiner<$first::AsyncOutput, $($ty::AsyncOutput,)*>;
type AsyncOutput = ($first::AsyncOutput, $($ty::AsyncOutput,)*);
const MIN_LENGTH: usize = $first::MIN_LENGTH $(+ $ty::MIN_LENGTH)*;
@ -317,13 +315,13 @@ macro_rules! impl_view_for_tuples {
}
}
fn resolve(self) -> Self::AsyncOutput {
async fn resolve(self) -> Self::AsyncOutput {
#[allow(non_snake_case)]
let ($first, $($ty,)*) = self;
$joiner::from((
futures::join!(
$first.resolve(),
$($ty.resolve()),*
))
)
}
}
@ -421,86 +419,46 @@ macro_rules! impl_view_for_tuples {
)
}
}
paste::paste! {
pin_project! {
pub struct $joiner<$first, $($ty,)*> {
#[pin]
[<$first:lower>]: $first,
$(
#[pin]
pub [<$ty:lower>]: $ty,
)*
}
}
impl<$first, $($ty,)*> From<($first, $($ty,)*)> for $joiner<$first, $($ty,)*> {
fn from(value: ($first, $($ty,)*)) -> Self {
#[allow(non_snake_case)]
let ($first, $($ty,)*) = value;
paste::paste! {
$joiner {
[<$first:lower>]: $first,
$([<$ty:lower>]: $ty,)*
}
}
}
}
impl<$first, $($ty,)*> Future for $joiner<$first, $($ty,)*>
where $first: Future, $($ty: Future,)*
{
type Output = ($first::Output, $($ty::Output,)*);
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.project();
match (this.[<$first:lower>].poll(cx), $(this.[<$ty:lower>].poll(cx),)*) {
(Poll::Ready($first), $(Poll::Ready($ty),)*) => Poll::Ready(($first, $($ty,)*)),
_ => Poll::Pending
}
}
}
}
};
};
}
impl_view_for_tuples!(Join2 => A, B);
impl_view_for_tuples!(Join3 => A, B, C);
impl_view_for_tuples!(Join4 => A, B, C, D);
impl_view_for_tuples!(Join5 => A, B, C, D, E);
impl_view_for_tuples!(Join6 => A, B, C, D, E, F);
impl_view_for_tuples!(Join7 => A, B, C, D, E, F, G);
impl_view_for_tuples!(Join8 => A, B, C, D, E, F, G, H);
impl_view_for_tuples!(Join9 => A, B, C, D, E, F, G, H, I);
impl_view_for_tuples!(Join10 => A, B, C, D, E, F, G, H, I, J);
impl_view_for_tuples!(Join11 => A, B, C, D, E, F, G, H, I, J, K);
impl_view_for_tuples!(Join12 => A, B, C, D, E, F, G, H, I, J, K, L);
impl_view_for_tuples!(Join13 => A, B, C, D, E, F, G, H, I, J, K, L, M);
impl_view_for_tuples!(Join14 => A, B, C, D, E, F, G, H, I, J, K, L, M, N);
impl_view_for_tuples!(Join15 => A, B, C, D, E, F, G, H, I, J, K, L, M, N, O);
impl_view_for_tuples!(Join16 => A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P);
impl_view_for_tuples!(Join17 => A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q);
impl_view_for_tuples!(Join18 => A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R);
impl_view_for_tuples!(Join19 => A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S);
impl_view_for_tuples!(Join20 =>
impl_view_for_tuples!(A, B);
impl_view_for_tuples!(A, B, C);
impl_view_for_tuples!(A, B, C, D);
impl_view_for_tuples!(A, B, C, D, E);
impl_view_for_tuples!(A, B, C, D, E, F);
impl_view_for_tuples!(A, B, C, D, E, F, G);
impl_view_for_tuples!(A, B, C, D, E, F, G, H);
impl_view_for_tuples!(A, B, C, D, E, F, G, H, I);
impl_view_for_tuples!(A, B, C, D, E, F, G, H, I, J);
impl_view_for_tuples!(A, B, C, D, E, F, G, H, I, J, K);
impl_view_for_tuples!(A, B, C, D, E, F, G, H, I, J, K, L);
impl_view_for_tuples!(A, B, C, D, E, F, G, H, I, J, K, L, M);
impl_view_for_tuples!(A, B, C, D, E, F, G, H, I, J, K, L, M, N);
impl_view_for_tuples!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O);
impl_view_for_tuples!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P);
impl_view_for_tuples!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q);
impl_view_for_tuples!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R);
impl_view_for_tuples!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S);
impl_view_for_tuples!(
A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T
);
impl_view_for_tuples!(Join21 =>
impl_view_for_tuples!(
A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U
);
impl_view_for_tuples!(Join22 =>
impl_view_for_tuples!(
A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V
);
impl_view_for_tuples!(Join23 =>
impl_view_for_tuples!(
A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W
);
impl_view_for_tuples!(Join24 =>
impl_view_for_tuples!(
A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X
);
impl_view_for_tuples!(Join25 =>
impl_view_for_tuples!(
A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y
);
impl_view_for_tuples!(Join26 =>
impl_view_for_tuples!(
A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y,
Z
);