mirror of
https://github.com/leptos-rs/leptos
synced 2024-11-10 06:44:17 +00:00
progress on error boundary that works with nested reactivity
This commit is contained in:
parent
f584154156
commit
a7162d7907
24 changed files with 746 additions and 279 deletions
|
@ -1,48 +1,59 @@
|
|||
use leptos::*;
|
||||
use leptos::{component, create_signal, prelude::*, view, IntoView};
|
||||
|
||||
#[component]
|
||||
pub fn App() -> impl IntoView {
|
||||
let (value, set_value) = create_signal(Ok(0));
|
||||
|
||||
// when input changes, try to parse a number from the input
|
||||
let on_input =
|
||||
move |ev| set_value.set(event_target_value(&ev).parse::<i32>());
|
||||
let (value, set_value) = create_signal(Ok(0)); //"foo".parse::<i32>());
|
||||
let guard = value.read();
|
||||
|
||||
view! {
|
||||
<h1>"Error Handling"</h1>
|
||||
<label>
|
||||
"Type a number (or something that's not a number!)"
|
||||
<input type="number" on:input=on_input/>
|
||||
// If an `Err(_) had been rendered inside the <ErrorBoundary/>,
|
||||
// the fallback will be displayed. Otherwise, the children of the
|
||||
// <ErrorBoundary/> will be displayed.
|
||||
<ErrorBoundary
|
||||
// the fallback receives a signal containing current errors
|
||||
fallback=|errors| view! {
|
||||
<div class="error">
|
||||
<p>"Not a number! Errors: "</p>
|
||||
// we can render a list of errors
|
||||
// as strings, if we'd like
|
||||
<ul>
|
||||
{move || errors.get()
|
||||
.into_iter()
|
||||
.map(|(_, e)| view! { <li>{e.to_string()}</li>})
|
||||
.collect_view()
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<p>
|
||||
"You entered "
|
||||
// because `value` is `Result<i32, _>`,
|
||||
// it will render the `i32` if it is `Ok`,
|
||||
// and render nothing and trigger the error boundary
|
||||
// if it is `Err`. It's a signal, so this will dynamically
|
||||
// update when `value` changes
|
||||
<strong>{value}</strong>
|
||||
</p>
|
||||
</ErrorBoundary>
|
||||
</label>
|
||||
}
|
||||
<h1>"Error Handling"</h1>
|
||||
<label>
|
||||
"Type a number (or something that's not a number!)"
|
||||
<input
|
||||
type="text"
|
||||
value=move || value.get().map(|n| n.to_string()).unwrap_or_default()// TODO guard support here
|
||||
// when input changes, try to parse a number from the input
|
||||
on:input:target=move |ev| set_value.set(ev.target().value().parse::<i32>())
|
||||
/>
|
||||
|
||||
// If an `Err(_) had been rendered inside the <ErrorBoundary/>,
|
||||
// the fallback will be displayed. Otherwise, the children of the
|
||||
// <ErrorBoundary/> will be displayed.
|
||||
/* <ErrorBoundary
|
||||
// the fallback receives a signal containing current errors
|
||||
fallback=|errors| view! {
|
||||
<div class="error">
|
||||
<p>"Not a number! Errors: "</p>
|
||||
// we can render a list of errors
|
||||
// as strings, if we'd like
|
||||
<ul>
|
||||
{move || errors.get()
|
||||
.into_iter()
|
||||
.map(|(_, e)| view! { <li>{e.to_string()}</li>})
|
||||
.collect_view()
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
}
|
||||
>*/
|
||||
{move || view! {
|
||||
<p>
|
||||
"You entered "
|
||||
// because `value` is `Result<i32, _>`,
|
||||
// it will render the `i32` if it is `Ok`,
|
||||
// and render nothing and trigger the error boundary
|
||||
// if it is `Err`. It's a signal, so this will dynamically
|
||||
// update when `value` changes
|
||||
<strong>{move || value.get()}</strong>
|
||||
</p>}
|
||||
.catch(|e| view! {
|
||||
<p class="error">{e.to_string()}</p>
|
||||
})
|
||||
}
|
||||
//</ErrorBoundary>
|
||||
</label>
|
||||
}
|
||||
}
|
||||
|
||||
#[component]
|
||||
pub fn ErrorBoundary() -> impl IntoView {}
|
||||
|
|
|
@ -3,51 +3,6 @@ use leptos_dom::{Errors, HydrationCtx, IntoView};
|
|||
use leptos_macro::{component, view};
|
||||
use leptos_reactive::{provide_context, run_as_child, signal_prelude::*};
|
||||
|
||||
/// When you render a `Result<_, _>` in your view, in the `Err` case it will
|
||||
/// render nothing, and search up through the view tree for an `<ErrorBoundary/>`.
|
||||
/// This component lets you define a fallback that should be rendered in that
|
||||
/// error case, allowing you to handle errors within a section of the interface.
|
||||
///
|
||||
/// ```
|
||||
/// # use leptos_reactive::*;
|
||||
/// # use leptos_macro::*;
|
||||
/// # use leptos_dom::*; use leptos::*;
|
||||
/// # let runtime = create_runtime();
|
||||
/// # if false {
|
||||
/// let (value, set_value) = create_signal(Ok(0));
|
||||
/// let on_input =
|
||||
/// move |ev| set_value.set(event_target_value(&ev).parse::<i32>());
|
||||
///
|
||||
/// view! {
|
||||
/// <input type="text" on:input=on_input/>
|
||||
/// <ErrorBoundary
|
||||
/// fallback=move |_| view! { <p class="error">"Enter a valid number."</p>}
|
||||
/// >
|
||||
/// <p>"Value is: " {move || value.get()}</p>
|
||||
/// </ErrorBoundary>
|
||||
/// }
|
||||
/// # ;
|
||||
/// # }
|
||||
/// # runtime.dispose();
|
||||
/// ```
|
||||
///
|
||||
/// ## Interaction with `<Suspense/>`
|
||||
/// If you use this with a `<Suspense/>` or `<Transition/>` component, note that the
|
||||
/// `<ErrorBoundary/>` should go inside the `<Suspense/>`, not the other way around,
|
||||
/// if there’s a chance that the `<ErrorBoundary/>` will begin in the error state.
|
||||
/// This is a limitation of the current design of the two components and the way they
|
||||
/// hydrate. Placing the `<ErrorBoundary/>` outside the `<Suspense/>` means that
|
||||
/// it is rendered on the server without any knowledge of the suspended view, so it
|
||||
/// will always be rendered on the server as if there were no errors, but might need
|
||||
/// to be hydrated with errors, depending on the actual result.
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// view! {
|
||||
/// <Suspense fallback=move || view! { <p>"Loading..."</p> }>
|
||||
/// <ErrorBoundary fallback=|errors| view! { <ErrorTemplate errors=errors/>}>
|
||||
/// {move || {
|
||||
/// /* etc. */
|
||||
/// ```
|
||||
///
|
||||
/// ## Beginner's Tip: ErrorBoundary Requires Your Error To Implement std::error::Error.
|
||||
/// `ErrorBoundary` requires your `Result<T,E>` to implement [IntoView](https://docs.rs/leptos/latest/leptos/trait.IntoView.html).
|
||||
|
@ -72,13 +27,13 @@ use leptos_reactive::{provide_context, run_as_child, signal_prelude::*};
|
|||
/// [thiserror](https://docs.rs/thiserror/latest/thiserror/)
|
||||
#[component]
|
||||
pub fn ErrorBoundary<F, IV>(
|
||||
/// The components inside the tag which will get rendered
|
||||
/// The elements that will be rendered, which may include one or more `Result<_>` types.
|
||||
children: Children,
|
||||
/// A fallback that will be shown if an error occurs.
|
||||
fallback: F,
|
||||
) -> impl IntoView
|
||||
where
|
||||
F: Fn(RwSignal<Errors>) -> IV + 'static,
|
||||
F: Fn(Error) -> IV + 'static,
|
||||
IV: IntoView,
|
||||
{
|
||||
run_as_child(move || {
|
||||
|
|
|
@ -5,13 +5,18 @@ use tachys::{
|
|||
view::{Mountable, Position, PositionState, Render, RenderHtml},
|
||||
};
|
||||
|
||||
pub struct View<T>(T);
|
||||
pub struct View<T>(T)
|
||||
where
|
||||
T: Sized;
|
||||
|
||||
pub trait IntoView: Sized + Render<Dom> + RenderHtml<Dom> {
|
||||
fn into_view(self) -> View<Self>;
|
||||
}
|
||||
|
||||
impl<T: Render<Dom> + RenderHtml<Dom>> IntoView for T {
|
||||
impl<T: Render<Dom> + RenderHtml<Dom>> IntoView for T
|
||||
where
|
||||
T: Sized,
|
||||
{
|
||||
fn into_view(self) -> View<Self> {
|
||||
View(self)
|
||||
}
|
||||
|
@ -19,6 +24,8 @@ impl<T: Render<Dom> + RenderHtml<Dom>> IntoView for T {
|
|||
|
||||
impl<T: Render<Dom>> Render<Dom> for View<T> {
|
||||
type State = T::State;
|
||||
type FallibleState = T::FallibleState;
|
||||
type Error = T::Error;
|
||||
|
||||
fn build(self) -> Self::State {
|
||||
self.0.build()
|
||||
|
@ -27,6 +34,17 @@ impl<T: Render<Dom>> Render<Dom> for View<T> {
|
|||
fn rebuild(self, state: &mut Self::State) {
|
||||
self.0.rebuild(state)
|
||||
}
|
||||
|
||||
fn try_build(self) -> Result<Self::FallibleState, Self::Error> {
|
||||
self.0.try_build()
|
||||
}
|
||||
|
||||
fn try_rebuild(
|
||||
self,
|
||||
state: &mut Self::FallibleState,
|
||||
) -> Result<(), Self::Error> {
|
||||
self.0.try_rebuild(state)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: RenderHtml<Dom>> RenderHtml<Dom> for View<T> {
|
||||
|
|
|
@ -3,7 +3,7 @@ mod sets;
|
|||
mod source;
|
||||
mod subscriber;
|
||||
|
||||
pub(crate) use node::*;
|
||||
pub use node::*;
|
||||
pub(crate) use sets::*;
|
||||
pub use source::*;
|
||||
pub use subscriber::*;
|
||||
|
|
|
@ -6,7 +6,7 @@ thread_local! {
|
|||
static OBSERVER: RefCell<Option<AnySubscriber>> = const { RefCell::new(None) };
|
||||
}
|
||||
|
||||
pub(crate) struct Observer;
|
||||
pub struct Observer;
|
||||
|
||||
impl Observer {
|
||||
pub fn get() -> Option<AnySubscriber> {
|
||||
|
|
|
@ -4,7 +4,7 @@ use crate::{
|
|||
ssr::StreamBuilder,
|
||||
view::{
|
||||
either::{Either, EitherState},
|
||||
Mountable, Position, PositionState, Render, RenderHtml,
|
||||
Mountable, NeverError, Position, PositionState, Render, RenderHtml,
|
||||
},
|
||||
};
|
||||
use any_spawner::Executor;
|
||||
|
@ -63,7 +63,14 @@ where
|
|||
Fut::Output: Render<Rndr>,
|
||||
Rndr: Renderer + 'static,
|
||||
{
|
||||
type State = Arc<RwLock<EitherState<Fal, Fut::Output, Rndr>>>;
|
||||
type State = Arc<
|
||||
RwLock<
|
||||
EitherState<Fal::State, <Fut::Output as Render<Rndr>>::State, Rndr>,
|
||||
>,
|
||||
>;
|
||||
// TODO fallible state/error
|
||||
type FallibleState = Self::State;
|
||||
type Error = NeverError;
|
||||
|
||||
fn build(self) -> Self::State {
|
||||
// poll the future once immediately
|
||||
|
@ -90,7 +97,8 @@ where
|
|||
let state = Arc::clone(&state);
|
||||
async move {
|
||||
let value = fut.as_mut().await;
|
||||
Either::Right(value).rebuild(&mut *state.write());
|
||||
Either::<Fal, Fut::Output>::Right(value)
|
||||
.rebuild(&mut *state.write());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -101,7 +109,8 @@ where
|
|||
fn rebuild(self, state: &mut Self::State) {
|
||||
if !TRANSITION {
|
||||
// fall back to fallback state
|
||||
Either::Left(self.fallback).rebuild(&mut *state.write());
|
||||
Either::<Fal, Fut::Output>::Left(self.fallback)
|
||||
.rebuild(&mut *state.write());
|
||||
}
|
||||
|
||||
// spawn the future, and rebuild the state when it resolves
|
||||
|
@ -109,10 +118,22 @@ where
|
|||
let state = Arc::clone(state);
|
||||
async move {
|
||||
let value = self.fut.await;
|
||||
Either::Right(value).rebuild(&mut *state.write());
|
||||
Either::<Fal, Fut::Output>::Right(value)
|
||||
.rebuild(&mut *state.write());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn try_build(self) -> Result<Self::FallibleState, Self::Error> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn try_rebuild(
|
||||
self,
|
||||
state: &mut Self::FallibleState,
|
||||
) -> Result<(), Self::Error> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl<const TRANSITION: bool, Fal, Fut, Rndr> RenderHtml<Rndr>
|
||||
|
@ -211,7 +232,8 @@ where
|
|||
let state = Arc::clone(&state);
|
||||
async move {
|
||||
let value = fut.as_mut().await;
|
||||
Either::Right(value).rebuild(&mut *state.write());
|
||||
Either::<Fal, Fut::Output>::Right(value)
|
||||
.rebuild(&mut *state.write());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -223,10 +245,8 @@ where
|
|||
impl<Rndr, Fal, Output> Mountable<Rndr>
|
||||
for Arc<RwLock<EitherState<Fal, Output, Rndr>>>
|
||||
where
|
||||
Fal: Render<Rndr>,
|
||||
Fal::State: Mountable<Rndr>,
|
||||
Output: Render<Rndr>,
|
||||
Output::State: Mountable<Rndr>,
|
||||
Fal: Mountable<Rndr>,
|
||||
Output: Mountable<Rndr>,
|
||||
Rndr: Renderer,
|
||||
{
|
||||
fn unmount(&mut self) {
|
||||
|
|
|
@ -33,3 +33,34 @@ where
|
|||
Error(Box::new(value))
|
||||
}
|
||||
}
|
||||
|
||||
/// A generic wrapper for any error.
|
||||
#[derive(Debug)]
|
||||
#[repr(transparent)]
|
||||
pub struct AnyError(Box<dyn error::Error>);
|
||||
|
||||
impl AnyError {
|
||||
pub fn new(err: impl error::Error + 'static) -> Self {
|
||||
Self(Box::new(err))
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::Deref for AnyError {
|
||||
type Target = Box<dyn error::Error>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for AnyError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl error::Error for AnyError {
|
||||
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
|
||||
self.0.source()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,8 +4,7 @@ use crate::{
|
|||
renderer::{CastFrom, Renderer},
|
||||
ssr::StreamBuilder,
|
||||
view::{
|
||||
FallibleRender, Mountable, Position, PositionState, Render, RenderHtml,
|
||||
ToTemplate,
|
||||
Mountable, Position, PositionState, Render, RenderHtml, ToTemplate,
|
||||
},
|
||||
};
|
||||
use const_str_slice_concat::{
|
||||
|
@ -167,6 +166,8 @@ where
|
|||
Rndr: Renderer,
|
||||
{
|
||||
type State = ElementState<At::State, Ch::State, Rndr>;
|
||||
type FallibleState = ElementState<At::State, Ch::FallibleState, Rndr>;
|
||||
type Error = Ch::Error;
|
||||
|
||||
fn rebuild(self, state: &mut Self::State) {
|
||||
let ElementState {
|
||||
|
@ -188,17 +189,6 @@ where
|
|||
rndr: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E, At, Ch, Rndr> FallibleRender<Rndr> for HtmlElement<E, At, Ch, Rndr>
|
||||
where
|
||||
E: CreateElement<Rndr>,
|
||||
At: Attribute<Rndr>,
|
||||
Ch: FallibleRender<Rndr>,
|
||||
Rndr: Renderer,
|
||||
{
|
||||
type Error = Ch::Error;
|
||||
type FallibleState = ElementState<At::State, Ch::FallibleState, Rndr>;
|
||||
|
||||
fn try_build(self) -> Result<Self::FallibleState, Self::Error> {
|
||||
let el = Rndr::create_element(self.tag);
|
||||
|
|
|
@ -3,7 +3,7 @@ use crate::{
|
|||
prelude::{Render, RenderHtml},
|
||||
renderer::Renderer,
|
||||
ssr::StreamBuilder,
|
||||
view::{Position, PositionState},
|
||||
view::{NeverError, Position, PositionState},
|
||||
};
|
||||
use std::marker::PhantomData;
|
||||
|
||||
|
@ -48,6 +48,8 @@ where
|
|||
Rndr: Renderer,
|
||||
{
|
||||
type State = View::State;
|
||||
type FallibleState = View::FallibleState;
|
||||
type Error = View::Error;
|
||||
|
||||
fn build(self) -> Self::State {
|
||||
self.view.build()
|
||||
|
@ -56,6 +58,17 @@ where
|
|||
fn rebuild(self, state: &mut Self::State) {
|
||||
self.view.rebuild(state);
|
||||
}
|
||||
|
||||
fn try_build(self) -> Result<Self::FallibleState, Self::Error> {
|
||||
self.view.try_build()
|
||||
}
|
||||
|
||||
fn try_rebuild(
|
||||
self,
|
||||
state: &mut Self::FallibleState,
|
||||
) -> Result<(), Self::Error> {
|
||||
self.view.try_rebuild(state)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Rndr, View> RenderHtml<Rndr> for Island<Rndr, View>
|
||||
|
@ -141,10 +154,23 @@ where
|
|||
Rndr: Renderer,
|
||||
{
|
||||
type State = ();
|
||||
type FallibleState = Self::State;
|
||||
type Error = NeverError;
|
||||
|
||||
fn build(self) -> Self::State {}
|
||||
|
||||
fn rebuild(self, state: &mut Self::State) {}
|
||||
|
||||
fn try_build(self) -> Result<Self::FallibleState, Self::Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn try_rebuild(
|
||||
self,
|
||||
state: &mut Self::FallibleState,
|
||||
) -> Result<(), Self::Error> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<Rndr, View> RenderHtml<Rndr> for IslandChildren<Rndr, View>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::{
|
||||
renderer::Renderer,
|
||||
view::{Position, Render, RenderHtml},
|
||||
view::{NeverError, Position, Render, RenderHtml},
|
||||
};
|
||||
use std::marker::PhantomData;
|
||||
|
||||
|
@ -27,10 +27,23 @@ pub fn doctype<R: Renderer>(value: &'static str) -> Doctype<R> {
|
|||
|
||||
impl<R: Renderer> Render<R> for Doctype<R> {
|
||||
type State = ();
|
||||
type FallibleState = Self::State;
|
||||
type Error = NeverError;
|
||||
|
||||
fn build(self) -> Self::State {}
|
||||
|
||||
fn rebuild(self, _state: &mut Self::State) {}
|
||||
|
||||
fn try_build(self) -> Result<Self::FallibleState, Self::Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn try_rebuild(
|
||||
self,
|
||||
state: &mut Self::FallibleState,
|
||||
) -> Result<(), Self::Error> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<R> RenderHtml<R> for Doctype<R>
|
||||
|
|
|
@ -5,8 +5,8 @@ use crate::{
|
|||
prelude::RenderHtml,
|
||||
renderer::{CastFrom, Renderer},
|
||||
view::{
|
||||
strings::StrState, InfallibleRender, Mountable, Position,
|
||||
PositionState, Render, ToTemplate,
|
||||
strings::StrState, Mountable, NeverError, Position, PositionState,
|
||||
Render, ToTemplate,
|
||||
},
|
||||
};
|
||||
use reactive_graph::signal::guards::ReadGuard;
|
||||
|
@ -56,6 +56,8 @@ macro_rules! render_primitive {
|
|||
where G: Deref<Target = $child_type>
|
||||
{
|
||||
type State = [<ReadGuard $child_type:camel State>]<R>;
|
||||
type FallibleState = Self::State;
|
||||
type Error = NeverError;
|
||||
|
||||
fn build(self) -> Self::State {
|
||||
let node = R::create_text_node(&self.to_string());
|
||||
|
@ -69,9 +71,15 @@ macro_rules! render_primitive {
|
|||
*this = *self;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, G> InfallibleRender for ReadGuard<$child_type, G> {}
|
||||
fn try_build(self) -> Result<Self::FallibleState, Self::Error> {
|
||||
Ok(self.build())
|
||||
}
|
||||
|
||||
fn try_rebuild(self, state: &mut Self::FallibleState) -> Result<(), Self::Error> {
|
||||
Ok(self.rebuild(state))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, G, R> RenderHtml<R> for ReadGuard<$child_type, G>
|
||||
where
|
||||
|
@ -191,6 +199,8 @@ where
|
|||
G: Deref<Target = String>,
|
||||
{
|
||||
type State = ReadGuardStringState<R>;
|
||||
type FallibleState = Self::State;
|
||||
type Error = NeverError;
|
||||
|
||||
fn build(self) -> Self::State {
|
||||
let node = R::create_text_node(&self);
|
||||
|
@ -208,9 +218,18 @@ where
|
|||
str.push_str(&self);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<G> InfallibleRender for ReadGuard<String, G> {}
|
||||
fn try_build(self) -> Result<Self::FallibleState, Self::Error> {
|
||||
Ok(self.build())
|
||||
}
|
||||
|
||||
fn try_rebuild(
|
||||
self,
|
||||
state: &mut Self::FallibleState,
|
||||
) -> Result<(), Self::Error> {
|
||||
Ok(self.rebuild(state))
|
||||
}
|
||||
}
|
||||
|
||||
impl<G, R> RenderHtml<R> for ReadGuard<String, G>
|
||||
where
|
||||
|
|
|
@ -5,11 +5,15 @@ use crate::{
|
|||
renderer::{DomRenderer, Renderer},
|
||||
ssr::StreamBuilder,
|
||||
view::{
|
||||
InfallibleRender, Mountable, Position, PositionState, Render,
|
||||
RenderHtml, ToTemplate,
|
||||
Mountable, Position, PositionState, Render, RenderHtml, ToTemplate,
|
||||
},
|
||||
};
|
||||
use reactive_graph::{computed::ScopedFuture, effect::RenderEffect};
|
||||
use reactive_graph::{
|
||||
computed::ScopedFuture,
|
||||
effect::RenderEffect,
|
||||
graph::{Observer, ReactiveNode},
|
||||
untrack,
|
||||
};
|
||||
|
||||
mod class;
|
||||
mod guards;
|
||||
|
@ -42,9 +46,14 @@ where
|
|||
F: FnMut() -> V + 'static,
|
||||
V: Render<R>,
|
||||
V::State: 'static,
|
||||
V::FallibleState: 'static,
|
||||
V::Error: 'static,
|
||||
R: Renderer,
|
||||
{
|
||||
type State = RenderEffectState<V::State>;
|
||||
type FallibleState =
|
||||
RenderEffectState<Result<V::FallibleState, Option<V::Error>>>;
|
||||
type Error = V::Error;
|
||||
|
||||
#[track_caller]
|
||||
fn build(mut self) -> Self::State {
|
||||
|
@ -60,6 +69,45 @@ where
|
|||
.into()
|
||||
}
|
||||
|
||||
fn try_build(mut self) -> Result<Self::FallibleState, Self::Error> {
|
||||
let initial = untrack(|| self().try_build())?;
|
||||
let parent = Observer::get();
|
||||
let effect = RenderEffect::new_with_value(
|
||||
{
|
||||
move |prev| {
|
||||
let value = self();
|
||||
if let Some(mut state) = prev {
|
||||
match state {
|
||||
Ok(ref mut state) => {
|
||||
if let Err(e) = value.try_rebuild(state) {
|
||||
if let Some(parent) = &parent {
|
||||
crate::log(
|
||||
"telling parent to check itself",
|
||||
);
|
||||
parent.mark_check();
|
||||
}
|
||||
return Err(Some(e));
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
//if let Some(parent) = parent {
|
||||
crate::log("need to tell parent to rerender");
|
||||
//}
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
state
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
},
|
||||
Some(Ok(initial)),
|
||||
);
|
||||
|
||||
Ok(effect.into())
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn rebuild(mut self, state: &mut Self::State) {
|
||||
// TODO
|
||||
|
@ -80,6 +128,22 @@ where
|
|||
)
|
||||
.into(); */
|
||||
}
|
||||
|
||||
fn try_rebuild(
|
||||
self,
|
||||
state: &mut Self::FallibleState,
|
||||
) -> Result<(), Self::Error> {
|
||||
if let Some(inner) = &mut state.0 {
|
||||
inner
|
||||
.with_value_mut(|value| match value {
|
||||
Err(e) if e.is_some() => Err(e.take().unwrap()),
|
||||
_ => Ok(()),
|
||||
})
|
||||
.unwrap_or(Ok(()))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RenderEffectState<T: 'static>(Option<RenderEffect<T>>);
|
||||
|
@ -120,35 +184,51 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<F, V> InfallibleRender for F where F: FnMut() -> V + 'static {}
|
||||
|
||||
/* impl<F, V, R> FallibleRender<R> for F
|
||||
pub struct RenderEffectFallibleState<T, E>
|
||||
where
|
||||
F: FnMut() -> V + 'static,
|
||||
V: FallibleRender<R>,
|
||||
V::State: 'static,
|
||||
T: 'static,
|
||||
E: 'static,
|
||||
{
|
||||
effect: Option<RenderEffect<Result<T, E>>>,
|
||||
}
|
||||
|
||||
impl<T, E, R> Mountable<R> for RenderEffectFallibleState<T, E>
|
||||
where
|
||||
T: Mountable<R>,
|
||||
R: Renderer,
|
||||
{
|
||||
type FallibleState = V::FallibleState;
|
||||
type Error = V::Error;
|
||||
|
||||
fn try_build(self) -> Result<Self::FallibleState, Self::Error> {
|
||||
todo!()
|
||||
fn unmount(&mut self) {
|
||||
if let Some(ref mut inner) = self.effect {
|
||||
inner.unmount();
|
||||
}
|
||||
}
|
||||
|
||||
fn try_rebuild(
|
||||
self,
|
||||
state: &mut Self::FallibleState,
|
||||
) -> Result<(), Self::Error> {
|
||||
todo!()
|
||||
fn mount(&mut self, parent: &R::Element, marker: Option<&R::Node>) {
|
||||
if let Some(ref mut inner) = self.effect {
|
||||
inner.mount(parent, marker);
|
||||
}
|
||||
}
|
||||
} */
|
||||
|
||||
fn insert_before_this(
|
||||
&self,
|
||||
parent: &R::Element,
|
||||
child: &mut dyn Mountable<R>,
|
||||
) -> bool {
|
||||
if let Some(inner) = &self.effect {
|
||||
inner.insert_before_this(parent, child)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<F, V, R> RenderHtml<R> for F
|
||||
where
|
||||
F: FnMut() -> V + 'static,
|
||||
V: RenderHtml<R>,
|
||||
V::State: 'static,
|
||||
V::FallibleState: 'static,
|
||||
V::Error: 'static,
|
||||
R: Renderer + 'static,
|
||||
R::Node: Clone,
|
||||
R::Element: Clone,
|
||||
|
@ -220,6 +300,40 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<M, E, R> Mountable<R> for Result<M, E>
|
||||
where
|
||||
M: Mountable<R>,
|
||||
R: Renderer,
|
||||
{
|
||||
fn unmount(&mut self) {
|
||||
if let Ok(ref mut inner) = self {
|
||||
inner.unmount();
|
||||
}
|
||||
}
|
||||
|
||||
fn mount(
|
||||
&mut self,
|
||||
parent: &<R as Renderer>::Element,
|
||||
marker: Option<&<R as Renderer>::Node>,
|
||||
) {
|
||||
if let Ok(ref mut inner) = self {
|
||||
inner.mount(parent, marker);
|
||||
}
|
||||
}
|
||||
|
||||
fn insert_before_this(
|
||||
&self,
|
||||
parent: &<R as Renderer>::Element,
|
||||
child: &mut dyn Mountable<R>,
|
||||
) -> bool {
|
||||
if let Ok(inner) = &self {
|
||||
inner.insert_before_this(parent, child)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Extends to track suspense
|
||||
impl<const TRANSITION: bool, Fal, Fut> Suspend<TRANSITION, Fal, Fut> {
|
||||
pub fn track(self) -> Suspend<TRANSITION, Fal, ScopedFuture<Fut>> {
|
||||
|
|
|
@ -3,7 +3,7 @@ use crate::{
|
|||
prelude::Mountable,
|
||||
renderer::Renderer,
|
||||
ssr::StreamBuilder,
|
||||
view::{Position, PositionState, Render, RenderHtml},
|
||||
view::{NeverError, Position, PositionState, Render, RenderHtml},
|
||||
};
|
||||
use reactive_graph::owner::Owner;
|
||||
use std::marker::PhantomData;
|
||||
|
@ -39,21 +39,21 @@ impl<T, R> OwnedView<T, R> {
|
|||
#[derive(Debug, Clone)]
|
||||
pub struct OwnedViewState<T, R>
|
||||
where
|
||||
T: Render<R>,
|
||||
T: Mountable<R>,
|
||||
R: Renderer,
|
||||
{
|
||||
owner: Owner,
|
||||
state: T::State,
|
||||
state: T,
|
||||
rndr: PhantomData<R>,
|
||||
}
|
||||
|
||||
impl<T, R> OwnedViewState<T, R>
|
||||
where
|
||||
T: Render<R>,
|
||||
T: Mountable<R>,
|
||||
R: Renderer,
|
||||
{
|
||||
/// Wraps a state with the given owner.
|
||||
fn new(state: T::State, owner: Owner) -> Self {
|
||||
fn new(state: T, owner: Owner) -> Self {
|
||||
Self {
|
||||
owner,
|
||||
state,
|
||||
|
@ -67,7 +67,9 @@ where
|
|||
T: Render<R>,
|
||||
R: Renderer,
|
||||
{
|
||||
type State = OwnedViewState<T, R>;
|
||||
type State = OwnedViewState<T::State, R>;
|
||||
type FallibleState = OwnedViewState<T::FallibleState, R>;
|
||||
type Error = T::Error;
|
||||
|
||||
fn build(self) -> Self::State {
|
||||
let state = self.owner.with(|| self.view.build());
|
||||
|
@ -79,6 +81,17 @@ where
|
|||
owner.with(|| view.rebuild(&mut state.state));
|
||||
state.owner = owner;
|
||||
}
|
||||
|
||||
fn try_build(self) -> Result<Self::FallibleState, Self::Error> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn try_rebuild(
|
||||
self,
|
||||
state: &mut Self::FallibleState,
|
||||
) -> Result<(), Self::Error> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, R> RenderHtml<R> for OwnedView<T, R>
|
||||
|
@ -126,7 +139,7 @@ where
|
|||
|
||||
impl<T, R> Mountable<R> for OwnedViewState<T, R>
|
||||
where
|
||||
T: Render<R>,
|
||||
T: Mountable<R>,
|
||||
R: Renderer,
|
||||
{
|
||||
fn unmount(&mut self) {
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
use super::{Mountable, Position, PositionState, Render, RenderHtml};
|
||||
use super::{
|
||||
Mountable, NeverError, Position, PositionState, Render, RenderHtml,
|
||||
};
|
||||
use crate::{
|
||||
hydration::Cursor,
|
||||
renderer::{CastFrom, Renderer},
|
||||
|
@ -198,6 +200,8 @@ where
|
|||
R: Renderer + 'static,
|
||||
{
|
||||
type State = AnyViewState<R>;
|
||||
type FallibleState = Self::State;
|
||||
type Error = NeverError;
|
||||
|
||||
fn build(self) -> Self::State {
|
||||
(self.build)(self.value)
|
||||
|
@ -206,6 +210,17 @@ where
|
|||
fn rebuild(self, state: &mut Self::State) {
|
||||
(self.rebuild)(self.type_id, self.value, state)
|
||||
}
|
||||
|
||||
fn try_build(self) -> Result<Self::FallibleState, Self::Error> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn try_rebuild(
|
||||
self,
|
||||
state: &mut Self::FallibleState,
|
||||
) -> Result<(), Self::Error> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl<R> RenderHtml<R> for AnyView<R>
|
||||
|
|
|
@ -1,22 +1,49 @@
|
|||
use super::{Mountable, Position, PositionState, Render, RenderHtml};
|
||||
use super::{
|
||||
Mountable, NeverError, Position, PositionState, Render, RenderHtml,
|
||||
};
|
||||
use crate::{
|
||||
hydration::Cursor,
|
||||
renderer::{CastFrom, Renderer},
|
||||
ssr::StreamBuilder,
|
||||
};
|
||||
use std::{
|
||||
error::Error,
|
||||
fmt::{Debug, Display},
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Either<A, B> {
|
||||
Left(A),
|
||||
Right(B),
|
||||
}
|
||||
|
||||
impl<A, B> Display for Either<A, B>
|
||||
where
|
||||
A: Error,
|
||||
B: Error,
|
||||
{
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Either::Left(v) => std::fmt::Display::fmt(&v, f),
|
||||
Either::Right(v) => std::fmt::Display::fmt(&v, f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, B> Error for Either<A, B>
|
||||
where
|
||||
A: Error,
|
||||
B: Error,
|
||||
{
|
||||
}
|
||||
|
||||
pub struct EitherState<A, B, Rndr>
|
||||
where
|
||||
A: Render<Rndr>,
|
||||
B: Render<Rndr>,
|
||||
A: Mountable<Rndr>,
|
||||
B: Mountable<Rndr>,
|
||||
Rndr: Renderer,
|
||||
{
|
||||
state: Either<A::State, B::State>,
|
||||
state: Either<A, B>,
|
||||
marker: Rndr::Placeholder,
|
||||
}
|
||||
|
||||
|
@ -26,7 +53,9 @@ where
|
|||
B: Render<Rndr>,
|
||||
Rndr: Renderer,
|
||||
{
|
||||
type State = EitherState<A, B, Rndr>;
|
||||
type State = EitherState<A::State, B::State, Rndr>;
|
||||
type FallibleState = EitherState<A::FallibleState, B::FallibleState, Rndr>;
|
||||
type Error = Either<A::Error, B::Error>;
|
||||
|
||||
fn build(self) -> Self::State {
|
||||
let marker = Rndr::create_placeholder();
|
||||
|
@ -42,7 +71,6 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
// TODO hold onto old states to avoid rerendering them?
|
||||
fn rebuild(self, state: &mut Self::State) {
|
||||
let marker = state.marker.as_ref();
|
||||
match (self, &mut state.state) {
|
||||
|
@ -62,12 +90,23 @@ where
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn try_build(self) -> Result<Self::FallibleState, Self::Error> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn try_rebuild(
|
||||
self,
|
||||
state: &mut Self::FallibleState,
|
||||
) -> Result<(), Self::Error> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, B, Rndr> Mountable<Rndr> for EitherState<A, B, Rndr>
|
||||
where
|
||||
A: Render<Rndr>,
|
||||
B: Render<Rndr>,
|
||||
A: Mountable<Rndr>,
|
||||
B: Mountable<Rndr>,
|
||||
Rndr: Renderer,
|
||||
{
|
||||
fn unmount(&mut self) {
|
||||
|
@ -181,10 +220,28 @@ const fn min_usize(vals: &[usize]) -> usize {
|
|||
macro_rules! tuples {
|
||||
($num:literal => $($ty:ident),*) => {
|
||||
paste::paste! {
|
||||
#[derive(Debug)]
|
||||
pub enum [<EitherOf $num>]<$($ty,)*> {
|
||||
$($ty ($ty),)*
|
||||
}
|
||||
|
||||
impl<$($ty,)*> Display for [<EitherOf $num>]<$($ty,)*>
|
||||
where
|
||||
$($ty: Display,)*
|
||||
{
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
$([<EitherOf $num>]::$ty(this) => this.fmt(f),)*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<$($ty,)*> Error for [<EitherOf $num>]<$($ty,)*>
|
||||
where
|
||||
$($ty: Error,)*
|
||||
{
|
||||
}
|
||||
|
||||
pub struct [<EitherOf $num State>]<$($ty,)* Rndr>
|
||||
where
|
||||
$($ty: Render<Rndr>,)*
|
||||
|
@ -234,6 +291,8 @@ macro_rules! tuples {
|
|||
Rndr: Renderer
|
||||
{
|
||||
type State = [<EitherOf $num State>]<$($ty,)* Rndr>;
|
||||
type FallibleState = [<EitherOf $num State>]<$($ty,)* Rndr>;
|
||||
type Error = Box<dyn Error>;
|
||||
|
||||
fn build(self) -> Self::State {
|
||||
let marker = Rndr::create_placeholder();
|
||||
|
@ -264,6 +323,17 @@ macro_rules! tuples {
|
|||
// and store the new state
|
||||
state.state = new_state;
|
||||
}
|
||||
|
||||
fn try_build(self) -> Result<Self::FallibleState, Self::Error> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn try_rebuild(
|
||||
self,
|
||||
state: &mut Self::FallibleState,
|
||||
) -> Result<(), Self::Error> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl<Rndr, $($ty,)*> RenderHtml<Rndr> for [<EitherOf $num>]<$($ty,)*>
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
use super::{either::Either, RenderHtml};
|
||||
use crate::view::{FallibleRender, Mountable, Render, Renderer};
|
||||
use super::{either::Either, NeverError, Position, PositionState, RenderHtml};
|
||||
use crate::{
|
||||
hydration::Cursor,
|
||||
ssr::StreamBuilder,
|
||||
view::{Mountable, Render, Renderer},
|
||||
};
|
||||
use std::marker::PhantomData;
|
||||
|
||||
impl<R, T, E> Render<R> for Result<T, E>
|
||||
|
@ -8,6 +12,8 @@ where
|
|||
R: Renderer,
|
||||
{
|
||||
type State = <Option<T> as Render<R>>::State;
|
||||
type FallibleState = T::State;
|
||||
type Error = E;
|
||||
|
||||
fn build(self) -> Self::State {
|
||||
self.ok().build()
|
||||
|
@ -16,15 +22,6 @@ where
|
|||
fn rebuild(self, state: &mut Self::State) {
|
||||
self.ok().rebuild(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl<R, T, E> FallibleRender<R> for Result<T, E>
|
||||
where
|
||||
T: Render<R>,
|
||||
R: Renderer,
|
||||
{
|
||||
type FallibleState = T::State;
|
||||
type Error = E;
|
||||
|
||||
fn try_build(self) -> Result<Self::FallibleState, Self::Error> {
|
||||
let inner = self?;
|
||||
|
@ -42,9 +39,49 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<R, T, E> RenderHtml<R> for Result<T, E>
|
||||
where
|
||||
T: RenderHtml<R>,
|
||||
R: Renderer,
|
||||
R::Element: Clone,
|
||||
R::Node: Clone,
|
||||
{
|
||||
const MIN_LENGTH: usize = T::MIN_LENGTH;
|
||||
|
||||
fn to_html_with_buf(
|
||||
self,
|
||||
buf: &mut String,
|
||||
position: &mut super::Position,
|
||||
) {
|
||||
if let Ok(inner) = self {
|
||||
inner.to_html_with_buf(buf, position);
|
||||
}
|
||||
}
|
||||
|
||||
fn to_html_async_with_buf<const OUT_OF_ORDER: bool>(
|
||||
self,
|
||||
buf: &mut StreamBuilder,
|
||||
position: &mut Position,
|
||||
) where
|
||||
Self: Sized,
|
||||
{
|
||||
if let Ok(inner) = self {
|
||||
inner.to_html_async_with_buf::<OUT_OF_ORDER>(buf, position);
|
||||
}
|
||||
}
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
cursor: &Cursor<R>,
|
||||
position: &PositionState,
|
||||
) -> Self::State {
|
||||
self.ok().hydrate::<FROM_SERVER>(cursor, position)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait TryCatchBoundary<Fal, FalFn, Rndr>
|
||||
where
|
||||
Self: Sized + FallibleRender<Rndr>,
|
||||
Self: Sized + Render<Rndr>,
|
||||
Fal: Render<Rndr>,
|
||||
FalFn: FnMut(Self::Error) -> Fal,
|
||||
Rndr: Renderer,
|
||||
|
@ -54,7 +91,7 @@ where
|
|||
|
||||
impl<T, Fal, FalFn, Rndr> TryCatchBoundary<Fal, FalFn, Rndr> for T
|
||||
where
|
||||
T: Sized + FallibleRender<Rndr>,
|
||||
T: Sized + Render<Rndr>,
|
||||
Fal: Render<Rndr>,
|
||||
FalFn: FnMut(Self::Error) -> Fal,
|
||||
Rndr: Renderer,
|
||||
|
@ -66,7 +103,7 @@ where
|
|||
|
||||
pub struct Try<T, Fal, FalFn, Rndr>
|
||||
where
|
||||
T: FallibleRender<Rndr>,
|
||||
T: Render<Rndr>,
|
||||
Fal: Render<Rndr>,
|
||||
FalFn: FnMut(T::Error) -> Fal,
|
||||
Rndr: Renderer,
|
||||
|
@ -78,7 +115,7 @@ where
|
|||
|
||||
impl<T, Fal, FalFn, Rndr> Try<T, Fal, FalFn, Rndr>
|
||||
where
|
||||
T: FallibleRender<Rndr>,
|
||||
T: Render<Rndr>,
|
||||
Fal: Render<Rndr>,
|
||||
FalFn: FnMut(T::Error) -> Fal,
|
||||
Rndr: Renderer,
|
||||
|
@ -94,12 +131,14 @@ where
|
|||
|
||||
impl<T, Fal, FalFn, Rndr> Render<Rndr> for Try<T, Fal, FalFn, Rndr>
|
||||
where
|
||||
T: FallibleRender<Rndr>,
|
||||
T: Render<Rndr>,
|
||||
Fal: Render<Rndr>,
|
||||
FalFn: FnMut(T::Error) -> Fal,
|
||||
Rndr: Renderer,
|
||||
{
|
||||
type State = TryState<T, Fal, Rndr>;
|
||||
type FallibleState = Self::State;
|
||||
type Error = NeverError;
|
||||
|
||||
fn build(mut self) -> Self::State {
|
||||
let state = match self.child.try_build() {
|
||||
|
@ -133,12 +172,23 @@ where
|
|||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn try_build(self) -> Result<Self::FallibleState, Self::Error> {
|
||||
Ok(self.build())
|
||||
}
|
||||
|
||||
fn try_rebuild(
|
||||
self,
|
||||
state: &mut Self::FallibleState,
|
||||
) -> Result<(), Self::Error> {
|
||||
Ok(self.rebuild(state))
|
||||
}
|
||||
}
|
||||
|
||||
// TODO RenderHtml implementation for ErrorBoundary
|
||||
impl<T, Fal, FalFn, Rndr> RenderHtml<Rndr> for Try<T, Fal, FalFn, Rndr>
|
||||
where
|
||||
T: FallibleRender<Rndr>,
|
||||
T: Render<Rndr>,
|
||||
Fal: RenderHtml<Rndr>,
|
||||
FalFn: FnMut(T::Error) -> Fal,
|
||||
Rndr: Renderer,
|
||||
|
@ -176,7 +226,7 @@ where
|
|||
|
||||
pub struct TryState<T, Fal, Rndr>
|
||||
where
|
||||
T: FallibleRender<Rndr>,
|
||||
T: Render<Rndr>,
|
||||
Fal: Render<Rndr>,
|
||||
Rndr: Renderer,
|
||||
{
|
||||
|
@ -186,7 +236,7 @@ where
|
|||
|
||||
impl<T, Fal, Rndr> Mountable<Rndr> for TryState<T, Fal, Rndr>
|
||||
where
|
||||
T: FallibleRender<Rndr>,
|
||||
T: Render<Rndr>,
|
||||
Fal: Render<Rndr>,
|
||||
Rndr: Renderer,
|
||||
{
|
||||
|
|
|
@ -1,17 +1,22 @@
|
|||
use super::{Mountable, Position, PositionState, Render, RenderHtml};
|
||||
use super::{
|
||||
Mountable, NeverError, Position, PositionState, Render, RenderHtml,
|
||||
};
|
||||
use crate::{
|
||||
hydration::Cursor,
|
||||
renderer::{CastFrom, Renderer},
|
||||
ssr::StreamBuilder,
|
||||
};
|
||||
use itertools::Itertools;
|
||||
use std::error::Error;
|
||||
|
||||
impl<T, R> Render<R> for Option<T>
|
||||
where
|
||||
T: Render<R>,
|
||||
R: Renderer,
|
||||
{
|
||||
type State = OptionState<T, R>;
|
||||
type State = OptionState<T::State, R>;
|
||||
type FallibleState = OptionState<T::FallibleState, R>;
|
||||
type Error = T::Error;
|
||||
|
||||
fn build(self) -> Self::State {
|
||||
let placeholder = R::create_placeholder();
|
||||
|
@ -41,6 +46,35 @@ where
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn try_build(self) -> Result<Self::FallibleState, Self::Error> {
|
||||
match self {
|
||||
None => {
|
||||
let placeholder = R::create_placeholder();
|
||||
Ok(OptionState {
|
||||
placeholder,
|
||||
state: None,
|
||||
})
|
||||
}
|
||||
Some(inner) => match inner.try_build() {
|
||||
Err(e) => return Err(e),
|
||||
Ok(inner) => {
|
||||
let placeholder = R::create_placeholder();
|
||||
Ok(OptionState {
|
||||
placeholder,
|
||||
state: Some(inner),
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn try_rebuild(
|
||||
self,
|
||||
state: &mut Self::FallibleState,
|
||||
) -> Result<(), Self::Error> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, R> RenderHtml<R> for Option<T>
|
||||
|
@ -101,18 +135,18 @@ where
|
|||
/// View state for an optional view.
|
||||
pub struct OptionState<T, R>
|
||||
where
|
||||
T: Render<R>,
|
||||
T: Mountable<R>,
|
||||
R: Renderer,
|
||||
{
|
||||
/// Marks the location of this view.
|
||||
placeholder: R::Placeholder,
|
||||
/// The view state.
|
||||
state: Option<T::State>,
|
||||
state: Option<T>,
|
||||
}
|
||||
|
||||
impl<T, R> Mountable<R> for OptionState<T, R>
|
||||
where
|
||||
T: Render<R>,
|
||||
T: Mountable<R>,
|
||||
R: Renderer,
|
||||
{
|
||||
fn unmount(&mut self) {
|
||||
|
@ -154,7 +188,9 @@ where
|
|||
R::Element: Clone,
|
||||
R::Node: Clone,
|
||||
{
|
||||
type State = VecState<T, R>;
|
||||
type State = VecState<T::State, R>;
|
||||
type FallibleState = VecState<T::FallibleState, R>;
|
||||
type Error = T::Error;
|
||||
|
||||
fn build(self) -> Self::State {
|
||||
VecState {
|
||||
|
@ -211,21 +247,40 @@ where
|
|||
old.append(&mut adds);
|
||||
}
|
||||
}
|
||||
|
||||
fn try_build(self) -> Result<Self::FallibleState, Self::Error> {
|
||||
let states = self
|
||||
.into_iter()
|
||||
.map(T::try_build)
|
||||
.collect::<Result<_, _>>()?;
|
||||
Ok(VecState {
|
||||
states,
|
||||
parent: None,
|
||||
marker: None,
|
||||
})
|
||||
}
|
||||
|
||||
fn try_rebuild(
|
||||
self,
|
||||
state: &mut Self::FallibleState,
|
||||
) -> Result<(), Self::Error> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct VecState<T, R>
|
||||
where
|
||||
T: Render<R>,
|
||||
T: Mountable<R>,
|
||||
R: Renderer,
|
||||
{
|
||||
states: Vec<T::State>,
|
||||
states: Vec<T>,
|
||||
parent: Option<R::Element>,
|
||||
marker: Option<R::Node>,
|
||||
}
|
||||
|
||||
impl<T, R> Mountable<R> for VecState<T, R>
|
||||
where
|
||||
T: Render<R>,
|
||||
T: Mountable<R>,
|
||||
R: Renderer,
|
||||
R::Element: Clone,
|
||||
R::Node: Clone,
|
||||
|
|
|
@ -3,6 +3,7 @@ use crate::{
|
|||
hydration::Cursor,
|
||||
renderer::{CastFrom, Renderer},
|
||||
ssr::StreamBuilder,
|
||||
view::NeverError,
|
||||
};
|
||||
use drain_filter_polyfill::VecExt as VecDrainFilterExt;
|
||||
use indexmap::IndexSet;
|
||||
|
@ -73,6 +74,9 @@ where
|
|||
Rndr::Element: Clone,
|
||||
{
|
||||
type State = KeyedState<K, V, Rndr>;
|
||||
// TODO fallible state and try_build()/try_rebuild() here
|
||||
type FallibleState = Self::State;
|
||||
type Error = NeverError;
|
||||
|
||||
fn build(self) -> Self::State {
|
||||
let items = self.items.into_iter();
|
||||
|
@ -126,6 +130,17 @@ where
|
|||
|
||||
*hashed_items = new_hashed_items;
|
||||
}
|
||||
|
||||
fn try_build(self) -> Result<Self::FallibleState, Self::Error> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn try_rebuild(
|
||||
self,
|
||||
state: &mut Self::FallibleState,
|
||||
) -> Result<(), Self::Error> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, I, K, KF, VF, V, Rndr> RenderHtml<Rndr>
|
||||
|
|
|
@ -18,33 +18,23 @@ pub mod tuples;
|
|||
///
|
||||
/// It is generic over the renderer itself, as long as that implements the [`Renderer`]
|
||||
/// trait.
|
||||
pub trait Render<R: Renderer> {
|
||||
pub trait Render<R: Renderer>: Sized {
|
||||
/// The “view state” for this type, which can be retained between updates.
|
||||
///
|
||||
/// For example, for a text node, `State` might be the actual DOM text node
|
||||
/// and the previous string, to allow for diffing between updates.
|
||||
type State: Mountable<R>;
|
||||
type FallibleState: Mountable<R>;
|
||||
type Error;
|
||||
|
||||
/// Creates the view for the first time, without hydrating from existing HTML.
|
||||
fn build(self) -> Self::State;
|
||||
|
||||
/// Updates the view with new data.
|
||||
fn rebuild(self, state: &mut Self::State);
|
||||
}
|
||||
|
||||
pub trait InfallibleRender {}
|
||||
|
||||
pub trait FallibleRender<R>: Sized + Render<R>
|
||||
where
|
||||
R: Renderer,
|
||||
{
|
||||
type FallibleState: Mountable<R>;
|
||||
type Error;
|
||||
|
||||
/// Creates the view fallibly, handling any [`Result`] by propagating its `Err`.
|
||||
fn try_build(self) -> Result<Self::FallibleState, Self::Error>;
|
||||
|
||||
/// Updates the view with new data fallibly, handling any [`Result`] by propagating its `Err`.
|
||||
fn try_rebuild(
|
||||
self,
|
||||
state: &mut Self::FallibleState,
|
||||
|
@ -62,27 +52,6 @@ impl core::fmt::Display for NeverError {
|
|||
|
||||
impl std::error::Error for NeverError {}
|
||||
|
||||
impl<T, R> FallibleRender<R> for T
|
||||
where
|
||||
T: Render<R> + InfallibleRender,
|
||||
R: Renderer,
|
||||
{
|
||||
type FallibleState = Self::State;
|
||||
type Error = NeverError;
|
||||
|
||||
fn try_build(self) -> Result<Self::FallibleState, Self::Error> {
|
||||
Ok(self.build())
|
||||
}
|
||||
|
||||
fn try_rebuild(
|
||||
self,
|
||||
state: &mut Self::FallibleState,
|
||||
) -> Result<(), Self::Error> {
|
||||
self.rebuild(state);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// The `RenderHtml` trait allows rendering something to HTML, and transforming
|
||||
/// that HTML into an interactive interface.
|
||||
///
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
use super::{
|
||||
InfallibleRender, Mountable, Position, PositionState, Render, RenderHtml,
|
||||
};
|
||||
use super::{Mountable, Position, PositionState, Render, RenderHtml};
|
||||
use crate::{
|
||||
hydration::Cursor,
|
||||
renderer::{CastFrom, Renderer},
|
||||
view::ToTemplate,
|
||||
view::{NeverError, ToTemplate},
|
||||
};
|
||||
use std::{
|
||||
fmt::Write,
|
||||
|
@ -48,6 +46,8 @@ macro_rules! render_primitive {
|
|||
|
||||
impl<'a, R: Renderer> Render<R> for $child_type {
|
||||
type State = [<$child_type:camel State>]<R>;
|
||||
type FallibleState = Self::State;
|
||||
type Error = NeverError;
|
||||
|
||||
fn build(self) -> Self::State {
|
||||
let node = R::create_text_node(&self.to_string());
|
||||
|
@ -61,9 +61,15 @@ macro_rules! render_primitive {
|
|||
*this = self;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> InfallibleRender for $child_type {}
|
||||
fn try_build(self) -> Result<Self::FallibleState, Self::Error> {
|
||||
Ok(self.build())
|
||||
}
|
||||
|
||||
fn try_rebuild(self, state: &mut Self::FallibleState) -> Result<(), Self::Error> {
|
||||
Ok(self.rebuild(state))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, R> RenderHtml<R> for $child_type
|
||||
where
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use super::{
|
||||
InfallibleRender, Mountable, Position, PositionState, Render, RenderHtml,
|
||||
Mountable, NeverError, Position, PositionState, Render, RenderHtml,
|
||||
ToTemplate,
|
||||
};
|
||||
use crate::{
|
||||
|
@ -114,6 +114,8 @@ where
|
|||
R::Text: Mountable<R>,
|
||||
{
|
||||
type State = Option<R::Text>;
|
||||
type FallibleState = Self::State;
|
||||
type Error = NeverError;
|
||||
|
||||
fn build(self) -> Self::State {
|
||||
// a view state has to be returned so it can be mounted
|
||||
|
@ -122,9 +124,18 @@ where
|
|||
|
||||
// This type is specified as static, so no rebuilding is done.
|
||||
fn rebuild(self, _state: &mut Self::State) {}
|
||||
}
|
||||
|
||||
impl<const V: &'static str> InfallibleRender for Static<V> {}
|
||||
fn try_build(self) -> Result<Self::FallibleState, Self::Error> {
|
||||
Ok(Render::<R>::build(self))
|
||||
}
|
||||
|
||||
fn try_rebuild(
|
||||
self,
|
||||
state: &mut Self::FallibleState,
|
||||
) -> Result<(), Self::Error> {
|
||||
Ok(Render::<R>::rebuild(self, state))
|
||||
}
|
||||
}
|
||||
|
||||
impl<const V: &'static str, R> RenderHtml<R> for Static<V>
|
||||
where
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use super::{
|
||||
InfallibleRender, Mountable, Position, PositionState, Render, RenderHtml,
|
||||
Mountable, NeverError, Position, PositionState, Render, RenderHtml,
|
||||
ToTemplate,
|
||||
};
|
||||
use crate::{
|
||||
|
@ -15,6 +15,8 @@ pub struct StrState<'a, R: Renderer> {
|
|||
|
||||
impl<'a, R: Renderer> Render<R> for &'a str {
|
||||
type State = StrState<'a, R>;
|
||||
type FallibleState = Self::State;
|
||||
type Error = NeverError;
|
||||
|
||||
fn build(self) -> Self::State {
|
||||
let node = R::create_text_node(self);
|
||||
|
@ -28,9 +30,18 @@ impl<'a, R: Renderer> Render<R> for &'a str {
|
|||
*str = self;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> InfallibleRender for &'a str {}
|
||||
fn try_build(self) -> Result<Self::FallibleState, Self::Error> {
|
||||
Ok(self.build())
|
||||
}
|
||||
|
||||
fn try_rebuild(
|
||||
self,
|
||||
state: &mut Self::FallibleState,
|
||||
) -> Result<(), Self::Error> {
|
||||
Ok(self.rebuild(state))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, R> RenderHtml<R> for &'a str
|
||||
where
|
||||
|
@ -129,6 +140,8 @@ pub struct StringState<R: Renderer> {
|
|||
|
||||
impl<R: Renderer> Render<R> for String {
|
||||
type State = StringState<R>;
|
||||
type FallibleState = Self::State;
|
||||
type Error = NeverError;
|
||||
|
||||
fn build(self) -> Self::State {
|
||||
let node = R::create_text_node(&self);
|
||||
|
@ -142,9 +155,18 @@ impl<R: Renderer> Render<R> for String {
|
|||
*str = self;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl InfallibleRender for String {}
|
||||
fn try_build(self) -> Result<Self::FallibleState, Self::Error> {
|
||||
Ok(self.build())
|
||||
}
|
||||
|
||||
fn try_rebuild(
|
||||
self,
|
||||
state: &mut Self::FallibleState,
|
||||
) -> Result<(), Self::Error> {
|
||||
Ok(self.rebuild(state))
|
||||
}
|
||||
}
|
||||
|
||||
impl<R> RenderHtml<R> for String
|
||||
where
|
||||
|
@ -215,6 +237,8 @@ pub struct RcStrState<R: Renderer> {
|
|||
|
||||
impl<R: Renderer> Render<R> for Rc<str> {
|
||||
type State = RcStrState<R>;
|
||||
type FallibleState = Self::State;
|
||||
type Error = NeverError;
|
||||
|
||||
fn build(self) -> Self::State {
|
||||
let node = R::create_text_node(&self);
|
||||
|
@ -228,9 +252,18 @@ impl<R: Renderer> Render<R> for Rc<str> {
|
|||
*str = self;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl InfallibleRender for Rc<str> {}
|
||||
fn try_build(self) -> Result<Self::FallibleState, Self::Error> {
|
||||
Ok(self.build())
|
||||
}
|
||||
|
||||
fn try_rebuild(
|
||||
self,
|
||||
state: &mut Self::FallibleState,
|
||||
) -> Result<(), Self::Error> {
|
||||
Ok(self.rebuild(state))
|
||||
}
|
||||
}
|
||||
|
||||
impl<R> RenderHtml<R> for Rc<str>
|
||||
where
|
||||
|
@ -302,6 +335,8 @@ pub struct ArcStrState<R: Renderer> {
|
|||
|
||||
impl<R: Renderer> Render<R> for Arc<str> {
|
||||
type State = ArcStrState<R>;
|
||||
type FallibleState = Self::State;
|
||||
type Error = NeverError;
|
||||
|
||||
fn build(self) -> Self::State {
|
||||
let node = R::create_text_node(&self);
|
||||
|
@ -315,9 +350,18 @@ impl<R: Renderer> Render<R> for Arc<str> {
|
|||
*str = self;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl InfallibleRender for Arc<str> {}
|
||||
fn try_build(self) -> Result<Self::FallibleState, Self::Error> {
|
||||
Ok(self.build())
|
||||
}
|
||||
|
||||
fn try_rebuild(
|
||||
self,
|
||||
state: &mut Self::FallibleState,
|
||||
) -> Result<(), Self::Error> {
|
||||
Ok(self.rebuild(state))
|
||||
}
|
||||
}
|
||||
|
||||
impl<R> RenderHtml<R> for Arc<str>
|
||||
where
|
||||
|
@ -389,6 +433,8 @@ pub struct CowStrState<'a, R: Renderer> {
|
|||
|
||||
impl<'a, R: Renderer> Render<R> for Cow<'a, str> {
|
||||
type State = CowStrState<'a, R>;
|
||||
type FallibleState = Self::State;
|
||||
type Error = NeverError;
|
||||
|
||||
fn build(self) -> Self::State {
|
||||
let node = R::create_text_node(&self);
|
||||
|
@ -402,9 +448,18 @@ impl<'a, R: Renderer> Render<R> for Cow<'a, str> {
|
|||
*str = self;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> InfallibleRender for Cow<'a, str> {}
|
||||
fn try_build(self) -> Result<Self::FallibleState, Self::Error> {
|
||||
Ok(self.build())
|
||||
}
|
||||
|
||||
fn try_rebuild(
|
||||
self,
|
||||
state: &mut Self::FallibleState,
|
||||
) -> Result<(), Self::Error> {
|
||||
Ok(self.rebuild(state))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, R> RenderHtml<R> for Cow<'a, str>
|
||||
where
|
||||
|
|
|
@ -61,6 +61,9 @@ where
|
|||
V::State: Mountable<Dom>,
|
||||
{
|
||||
type State = V::State;
|
||||
type FallibleState = V::FallibleState;
|
||||
type Error = V::Error;
|
||||
// TODO try_build/try_rebuild()
|
||||
|
||||
fn build(self) -> Self::State {
|
||||
let tpl = Self::to_template();
|
||||
|
@ -74,6 +77,17 @@ where
|
|||
fn rebuild(self, state: &mut Self::State) {
|
||||
self.view.rebuild(state)
|
||||
}
|
||||
|
||||
fn try_build(self) -> Result<Self::FallibleState, Self::Error> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn try_rebuild(
|
||||
self,
|
||||
state: &mut Self::FallibleState,
|
||||
) -> Result<(), Self::Error> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl<V> RenderHtml<Dom> for ViewTemplate<V>
|
||||
|
|
|
@ -1,11 +1,8 @@
|
|||
use super::{
|
||||
Mountable, Position, PositionState, Render, RenderHtml, Renderer,
|
||||
ToTemplate,
|
||||
};
|
||||
use crate::{
|
||||
hydration::Cursor,
|
||||
view::{FallibleRender, InfallibleRender, StreamBuilder},
|
||||
Mountable, NeverError, Position, PositionState, Render, RenderHtml,
|
||||
Renderer, ToTemplate,
|
||||
};
|
||||
use crate::{error::AnyError, hydration::Cursor, view::StreamBuilder};
|
||||
use const_str_slice_concat::{
|
||||
const_concat, const_concat_with_separator, str_from_buffer,
|
||||
};
|
||||
|
@ -13,13 +10,24 @@ use std::error::Error;
|
|||
|
||||
impl<R: Renderer> Render<R> for () {
|
||||
type State = ();
|
||||
type FallibleState = Self::State;
|
||||
type Error = NeverError;
|
||||
|
||||
fn build(self) -> Self::State {}
|
||||
|
||||
fn rebuild(self, _state: &mut Self::State) {}
|
||||
}
|
||||
|
||||
impl InfallibleRender for () {}
|
||||
fn try_build(self) -> Result<Self::FallibleState, Self::Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn try_rebuild(
|
||||
self,
|
||||
state: &mut Self::FallibleState,
|
||||
) -> Result<(), Self::Error> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<R> RenderHtml<R> for ()
|
||||
where
|
||||
|
@ -68,6 +76,8 @@ impl ToTemplate for () {
|
|||
|
||||
impl<A: Render<R>, R: Renderer> Render<R> for (A,) {
|
||||
type State = A::State;
|
||||
type FallibleState = A::FallibleState;
|
||||
type Error = A::Error;
|
||||
|
||||
fn build(self) -> Self::State {
|
||||
self.0.build()
|
||||
|
@ -76,11 +86,6 @@ impl<A: Render<R>, R: Renderer> Render<R> for (A,) {
|
|||
fn rebuild(self, state: &mut Self::State) {
|
||||
self.0.rebuild(state)
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: FallibleRender<R>, R: Renderer> FallibleRender<R> for (A,) {
|
||||
type Error = A::Error;
|
||||
type FallibleState = A::FallibleState;
|
||||
|
||||
fn try_build(self) -> Result<Self::FallibleState, Self::Error> {
|
||||
self.0.try_build()
|
||||
|
@ -148,18 +153,20 @@ macro_rules! impl_view_for_tuples {
|
|||
where
|
||||
$first: Render<Rndr>,
|
||||
$($ty: Render<Rndr>),*,
|
||||
$first::Error: Error + Sized + 'static,
|
||||
$($ty::Error: Error + Sized + 'static),*,
|
||||
Rndr: Renderer
|
||||
{
|
||||
type State = ($first::State, $($ty::State,)*);
|
||||
type Error = AnyError;
|
||||
type FallibleState = ($first::FallibleState, $($ty::FallibleState,)*);
|
||||
|
||||
fn build(self) -> Self::State {
|
||||
paste::paste! {
|
||||
let ([<$first:lower>], $([<$ty:lower>],)*) = self;
|
||||
(
|
||||
[<$first:lower>].build(),
|
||||
$([<$ty:lower>].build()),*
|
||||
)
|
||||
}
|
||||
let ($first, $($ty,)*) = self;
|
||||
(
|
||||
$first.build(),
|
||||
$($ty.build()),*
|
||||
)
|
||||
}
|
||||
|
||||
fn rebuild(self, state: &mut Self::State) {
|
||||
|
@ -170,25 +177,13 @@ macro_rules! impl_view_for_tuples {
|
|||
$([<$ty:lower>].rebuild([<view_ $ty:lower>]));*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<$first, $($ty),*, Rndr> FallibleRender<Rndr> for ($first, $($ty,)*)
|
||||
where
|
||||
$first: FallibleRender<Rndr>,
|
||||
$($ty: FallibleRender<Rndr>),*,
|
||||
$first::Error: Error + 'static,
|
||||
$($ty::Error: Error + 'static),*,
|
||||
Rndr: Renderer
|
||||
{
|
||||
type Error = (); /* Box<dyn Error>; */
|
||||
type FallibleState = ($first::FallibleState, $($ty::FallibleState,)*);
|
||||
|
||||
fn try_build(self) -> Result<Self::FallibleState, Self::Error> {
|
||||
paste::paste! {
|
||||
let ([<$first:lower>], $([<$ty:lower>],)*) = self;
|
||||
let ($first, $($ty,)*) = self;
|
||||
Ok((
|
||||
[<$first:lower>].try_build().map_err(|_| ())?,
|
||||
$([<$ty:lower>].try_build().map_err(|_| ())?),*
|
||||
$first.try_build().map_err(AnyError::new)?,
|
||||
$($ty.try_build().map_err(AnyError::new)?),*
|
||||
))
|
||||
}
|
||||
}
|
||||
|
@ -197,8 +192,8 @@ macro_rules! impl_view_for_tuples {
|
|||
paste::paste! {
|
||||
let ([<$first:lower>], $([<$ty:lower>],)*) = self;
|
||||
let ([<view_ $first:lower>], $([<view_ $ty:lower>],)*) = state;
|
||||
[<$first:lower>].try_rebuild([<view_ $first:lower>]).map_err(|_| ())?;
|
||||
$([<$ty:lower>].try_rebuild([<view_ $ty:lower>]).map_err(|_| ())?);*
|
||||
[<$first:lower>].try_rebuild([<view_ $first:lower>]).map_err(AnyError::new)?;
|
||||
$([<$ty:lower>].try_rebuild([<view_ $ty:lower>]).map_err(AnyError::new)?);*
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
@ -208,6 +203,8 @@ macro_rules! impl_view_for_tuples {
|
|||
where
|
||||
$first: RenderHtml<Rndr>,
|
||||
$($ty: RenderHtml<Rndr>),*,
|
||||
$first::Error: Error + Sized + 'static,
|
||||
$($ty::Error: Error + Sized + 'static),*,
|
||||
Rndr: Renderer,
|
||||
Rndr::Node: Clone,
|
||||
Rndr::Element: Clone
|
||||
|
|
Loading…
Reference in a new issue