ErrorBoundary component

This commit is contained in:
Greg Johnston 2024-03-27 21:41:49 -04:00
parent 88b93f40f9
commit 100ed7d926
41 changed files with 238 additions and 269 deletions

View file

@ -37,6 +37,7 @@ members = [
"routing",
"is_server",
"routing_macro",
"any_error",
]
exclude = ["benchmarks", "examples"]
@ -45,6 +46,7 @@ version = "0.6.13"
rust-version = "1.75"
[workspace.dependencies]
any_error = { path = "./any_error/" }
any_spawner = { path = "./any_spawner/" }
const_str_slice_concat = { path = "./const_str_slice_concat" }
either_of = { path = "./either_of/" }

6
any_error/Cargo.toml Normal file
View file

@ -0,0 +1,6 @@
[package]
name = "any_error"
edition = "2021"
version.workspace = true
[dependencies]

41
any_error/src/lib.rs Normal file
View file

@ -0,0 +1,41 @@
use std::{error, fmt, ops, sync::Arc};
/// This is a result type into which any error can be converted.
///
/// Results are stored as [`Error`].
pub type Result<T, E = Error> = core::result::Result<T, E>;
/// A generic wrapper for any error.
#[derive(Debug, Clone)]
#[repr(transparent)]
pub struct Error(Arc<dyn error::Error + Send + Sync>);
impl Error {
/// Converts the wrapper into the inner reference-counted error.
pub fn into_inner(self) -> Arc<dyn error::Error + Send + Sync> {
Arc::clone(&self.0)
}
}
impl ops::Deref for Error {
type Target = Arc<dyn error::Error + Send + Sync>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl<T> From<T> for Error
where
T: error::Error + Send + Sync + 'static,
{
fn from(value: T) -> Self {
Error(Arc::new(value))
}
}

View file

@ -5,7 +5,7 @@ use leptos::{
computed::AsyncDerived,
signal::{signal, RwSignal},
},
view, IntoView, Transition,
view, ErrorBoundary, IntoView, Transition,
};
use serde::{Deserialize, Serialize};
use thiserror::Error;
@ -46,7 +46,7 @@ async fn fetch_cats(count: CatCount) -> Result<Vec<String>> {
}
pub fn fetch_example() -> impl IntoView {
let (cat_count, set_cat_count) = signal::<CatCount>(3);
let (cat_count, set_cat_count) = signal::<CatCount>(0);
// we use new_unsync here because the reqwasm request type isn't Send
// if we were doing SSR, then
@ -73,6 +73,18 @@ pub fn fetch_example() -> impl IntoView {
}
};*/
let cats_view = move || async move {
view! {
<ErrorBoundary fallback=|e| view! { <p class="error">{e.to_string()}</p> }>
{async move { cats.await.map(|cats| {
cats.into_iter()
.map(|s| view! { <p><img src={s}/></p> })
.collect::<Vec<_>>()
})}}
</ErrorBoundary>
}
};
view! {
<div>
<label>
@ -86,17 +98,10 @@ pub fn fetch_example() -> impl IntoView {
}
/>
</label>
<Transition fallback=|| view! { <div>"Loading..."</div> }>
{async move {
cats.await
.map(|cats| {
cats.into_iter()
.map(|s| view! { <p><img src={s}/></p> })
.collect::<Vec<_>>()
})
.unwrap_or_default()
}}
</Transition>
<Transition fallback=|| view! { <div>"Loading..."</div> }>
{cats_view()}
</Transition>
</ErrorBoundary>
</div>
}
}

View file

@ -10,6 +10,7 @@ readme = "../README.md"
rust-version.workspace = true
[dependencies]
any_error = { workspace = true }
any_spawner = { workspace = true, features = ["wasm-bindgen"] }
base64 = { version = "0.22", optional = true }
cfg-if = "1"

View file

@ -100,21 +100,10 @@ pub trait ToChildren<F> {
fn to_children(f: F) -> Self;
}
impl<F, C> ToChildren<F> for TypedChildren<C>
where
F: FnOnce() -> C + 'static,
C: IntoView,
{
#[inline]
fn to_children(f: F) -> Self {
TypedChildren(Box::new(move || f().into_view()))
}
}
impl<F, C> ToChildren<F> for Children
where
F: FnOnce() -> C + 'static,
C: RenderHtml<Dom> + 'static,
F: FnOnce() -> C + Send + 'static,
C: RenderHtml<Dom> + Send + 'static,
{
#[inline]
fn to_children(f: F) -> Self {
@ -124,8 +113,8 @@ where
impl<F, C> ToChildren<F> for ChildrenFn
where
F: Fn() -> C + 'static,
C: RenderHtml<Dom> + 'static,
F: Fn() -> C + Send + 'static,
C: RenderHtml<Dom> + Send + 'static,
{
#[inline]
fn to_children(f: F) -> Self {
@ -135,8 +124,8 @@ where
impl<F, C> ToChildren<F> for ChildrenFnMut
where
F: Fn() -> C + 'static,
C: RenderHtml<Dom> + 'static,
F: Fn() -> C + Send + 'static,
C: RenderHtml<Dom> + Send + 'static,
{
#[inline]
fn to_children(f: F) -> Self {
@ -147,7 +136,7 @@ where
impl<F, C> ToChildren<F> for BoxedChildrenFn
where
F: Fn() -> C + 'static,
C: RenderHtml<Dom> + 'static,
C: RenderHtml<Dom> + Send + 'static,
{
#[inline]
fn to_children(f: F) -> Self {
@ -169,7 +158,7 @@ impl Default for ViewFn {
impl<F, C> From<F> for ViewFn
where
F: Fn() -> C + Send + Sync + 'static,
C: RenderHtml<Dom> + 'static,
C: RenderHtml<Dom> + Send + 'static,
{
fn from(value: F) -> Self {
Self(Arc::new(move || value().into_any()))
@ -185,10 +174,42 @@ impl ViewFn {
/// A typed equivalent to [`Children`], which takes a generic but preserves type information to
/// allow the compiler to optimize the view more effectively.
pub struct TypedChildren<T>(Box<dyn FnOnce() -> View<T>>);
pub struct TypedChildren<T>(Box<dyn FnOnce() -> View<T> + Send>);
impl<T> TypedChildren<T> {
pub fn into_inner(self) -> impl FnOnce() -> View<T> {
pub fn into_inner(self) -> impl FnOnce() -> View<T> + Send {
self.0
}
}
impl<F, C> ToChildren<F> for TypedChildren<C>
where
F: FnOnce() -> C + Send + 'static,
C: IntoView,
{
#[inline]
fn to_children(f: F) -> Self {
TypedChildren(Box::new(move || f().into_view()))
}
}
/// A typed equivalent to [`ChildrenMut`], which takes a generic but preserves type information to
/// allow the compiler to optimize the view more effectively.
pub struct TypedChildrenMut<T>(Box<dyn FnMut() -> View<T> + Send>);
impl<T> TypedChildrenMut<T> {
pub fn into_inner(self) -> impl FnMut() -> View<T> + Send {
self.0
}
}
impl<F, C> ToChildren<F> for TypedChildrenMut<C>
where
F: FnMut() -> C + Send + 'static,
C: IntoView,
{
#[inline]
fn to_children(mut f: F) -> Self {
TypedChildrenMut(Box::new(move || f().into_view()))
}
}

View file

@ -1,7 +1,7 @@
use crate::Children;
use leptos_dom::{Errors, HydrationCtx, IntoView};
use leptos_macro::{component, view};
use leptos_reactive::{provide_context, run_as_child, signal_prelude::*};
use crate::{children::TypedChildrenMut, IntoView};
use any_error::Error;
use leptos_macro::component;
use tachys::view::error_boundary::TryCatchBoundary;
///
/// ## Beginner's Tip: ErrorBoundary Requires Your Error To Implement std::error::Error.
@ -26,57 +26,18 @@ use leptos_reactive::{provide_context, run_as_child, signal_prelude::*};
/// For more information about how to easily implement `Error` see
/// [thiserror](https://docs.rs/thiserror/latest/thiserror/)
#[component]
pub fn ErrorBoundary<F, IV>(
pub fn ErrorBoundary<FalFn, Fal, Chil>(
/// The elements that will be rendered, which may include one or more `Result<_>` types.
children: Children,
children: TypedChildrenMut<Chil>,
/// A fallback that will be shown if an error occurs.
fallback: F,
fallback: FalFn,
) -> impl IntoView
where
F: Fn(Error) -> IV + 'static,
IV: IntoView,
FalFn: FnMut(Error) -> Fal + Clone + Send + 'static,
Fal: IntoView + 'static,
Chil: IntoView + 'static,
{
run_as_child(move || {
let before_children = HydrationCtx::next_error();
let errors: RwSignal<Errors> = create_rw_signal(Errors::default());
provide_context(errors);
// Run children so that they render and execute resources
_ = HydrationCtx::next_error();
let children = children();
HydrationCtx::continue_from(before_children);
#[cfg(all(debug_assertions, feature = "hydrate"))]
{
use leptos_dom::View;
if children.nodes.iter().any(|child| {
matches!(child, View::Suspense(_, _))
|| matches!(child, View::Component(repr) if repr.name() == "Transition")
}) {
leptos_dom::logging::console_warn("You are using a <Suspense/> or \
<Transition/> as the direct child of an <ErrorBoundary/>. To ensure correct \
hydration, these should be reorganized so that the <ErrorBoundary/> is a child \
of the <Suspense/> or <Transition/> instead: \n\
\nview! {{ \
\n <Suspense fallback=todo!()>\n <ErrorBoundary fallback=todo!()>\n {{move || {{ /* etc. */")
}
}
let children = children.into_view();
let errors_empty = create_memo(move |_| errors.with(Errors::is_empty));
move || {
if errors_empty.get() {
children.clone().into_view()
} else {
view! {
{fallback(errors)}
<leptos-error-boundary style="display: none">{children.clone()}</leptos-error-boundary>
}
.into_view()
}
}
})
let mut children = children.into_inner();
// TODO dev-mode warning about Suspense/ErrorBoundary ordering
move || children().catch(fallback.clone())
}

View file

@ -14,14 +14,15 @@ pub struct View<T>(T)
where
T: Sized;
pub trait IntoView: Sized + Render<Dom> + RenderHtml<Dom> //+ AddAnyAttr<Dom>
pub trait IntoView: Sized + Render<Dom> + RenderHtml<Dom> + Send
//+ AddAnyAttr<Dom>
{
fn into_view(self) -> View<Self>;
}
impl<T> IntoView for T
where
T: Sized + Render<Dom> + RenderHtml<Dom>, //+ AddAnyAttr<Dom>,
T: Sized + Render<Dom> + RenderHtml<Dom> + Send, //+ AddAnyAttr<Dom>,
{
fn into_view(self) -> View<Self> {
View(self)
@ -40,14 +41,14 @@ impl<T: Render<Dom>> Render<Dom> for View<T> {
self.0.rebuild(state)
}
fn try_build(self) -> tachys::error::Result<Self::FallibleState> {
fn try_build(self) -> any_error::Result<Self::FallibleState> {
self.0.try_build()
}
fn try_rebuild(
self,
state: &mut Self::FallibleState,
) -> tachys::error::Result<()> {
) -> any_error::Result<()> {
self.0.try_rebuild(state)
}
}

View file

@ -1,4 +1,4 @@
//#![deny(missing_docs)] // TODO restore
//#!rdeny(missing_docs)] // TODO restore
#![forbid(unsafe_code)]
//! # About Leptos
//!
@ -156,6 +156,8 @@ pub use action_form::*;
pub mod callback;
pub mod children;
pub mod component;
mod error_boundary;
pub use error_boundary::*;
mod for_loop;
mod hydration_scripts;
#[cfg(feature = "nonce")]
@ -164,6 +166,7 @@ mod show;
mod suspense_component;
pub mod text_prop;
mod transition;
pub use any_error as error;
pub use for_loop::*;
pub use hydration_scripts::*;
pub use leptos_macro::*;
@ -171,7 +174,7 @@ pub use reactive_graph::{
self,
signal::{arc_signal, create_signal, signal},
};
pub use server_fn::{self, error};
pub use server_fn;
pub use show::*;
pub use suspense_component::*;
pub use transition::*;

View file

@ -1,8 +1,10 @@
use crate::{children::ToChildren};
use tachys::prelude::FutureViewExt;
use crate::{children::ViewFn, IntoView};
use crate::{
children::{ToChildren, ViewFn},
IntoView,
};
use leptos_macro::component;
use std::{future::Future, sync::Arc};
use tachys::prelude::FutureViewExt;
/// An async, typed equivalent to [`Children`], which takes a generic but preserves
/// type information to allow the compiler to optimize the view more effectively.
@ -39,16 +41,15 @@ pub fn Suspense<Chil, ChilFn, ChilFut>(
) -> impl IntoView
where
Chil: IntoView + 'static,
ChilFn: Fn() -> ChilFut + Clone + 'static,
ChilFut: Future<Output = Chil> + Send + Sync + 'static,
ChilFn: Fn() -> ChilFut + Send + Clone + 'static,
ChilFut: Future<Output = Chil> + Send + 'static,
{
let children = Arc::new(children.into_inner());
let children = children.into_inner();
// TODO check this against islands
move || {
children()
(children.clone())()
.suspend()
.with_fallback(fallback.run())
.track()
}
}

View file

@ -11,8 +11,8 @@ pub fn Transition<Chil, ChilFn, ChilFut>(
) -> impl IntoView
where
Chil: IntoView + 'static,
ChilFn: Fn() -> ChilFut + Clone + 'static,
ChilFut: Future<Output = Chil> + Send + Sync + 'static,
ChilFn: Fn() -> ChilFut + Clone + Send + 'static,
ChilFut: Future<Output = Chil> + Send + 'static,
{
let children = children.into_inner();
move || {

View file

@ -10,6 +10,7 @@ readme = "../README.md"
rust-version.workspace = true
[dependencies]
any_error = { workspace = true }
server_fn_macro_default = { workspace = true }
# used for hashing paths in #[server] macro
const_format = "0.2"

View file

@ -1,3 +1,4 @@
use any_error::Error;
use serde::{Deserialize, Serialize};
use std::{
error, fmt,
@ -12,50 +13,9 @@ use url::Url;
/// A custom header that can be used to indicate a server function returned an error.
pub const SERVER_FN_ERROR_HEADER: &str = "serverfnerror";
/// This is a result type into which any error can be converted,
/// and which can be used directly in your `view`.
///
/// All errors will be stored as [`struct@Error`].
pub type Result<T, E = Error> = core::result::Result<T, E>;
/// A generic wrapper for any error.
#[derive(Debug, Clone)]
#[repr(transparent)]
pub struct Error(Arc<dyn error::Error + Send + Sync>);
impl Error {
/// Converts the wrapper into the inner reference-counted error.
pub fn into_inner(self) -> Arc<dyn error::Error + Send + Sync> {
Arc::clone(&self.0)
}
}
impl ops::Deref for Error {
type Target = Arc<dyn error::Error + Send + Sync>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl<T> From<T> for Error
where
T: std::error::Error + Send + Sync + 'static,
{
fn from(value: T) -> Self {
Error(Arc::new(value))
}
}
impl From<ServerFnError> for Error {
fn from(e: ServerFnError) -> Self {
Error(Arc::new(ServerFnErrorErr::from(e)))
Error::from(ServerFnErrorErr::from(e))
}
}

View file

@ -4,6 +4,7 @@ version = "0.1.0"
edition = "2021"
[dependencies]
any_error = { workspace = true }
any_spawner = { workspace = true }
const_str_slice_concat = { workspace = true }
either_of = { workspace = true }

View file

@ -124,14 +124,14 @@ where
});
}
fn try_build(self) -> crate::error::Result<Self::FallibleState> {
fn try_build(self) -> any_error::Result<Self::FallibleState> {
todo!()
}
fn try_rebuild(
self,
_state: &mut Self::FallibleState,
) -> crate::error::Result<()> {
) -> any_error::Result<()> {
todo!()
}
}
@ -140,7 +140,7 @@ impl<const TRANSITION: bool, Fal, Fut, Rndr> RenderHtml<Rndr>
for Suspend<TRANSITION, Fal, Fut>
where
Fal: RenderHtml<Rndr> + 'static,
Fut: Future + Send + Sync + 'static,
Fut: Future + Send + 'static,
Fut::Output: RenderHtml<Rndr>,
Rndr: Renderer + 'static,
{

View file

@ -1,38 +0,0 @@
use std::{error, fmt, ops, result};
/// This is a result type into which any error can be converted,
/// and which can be used directly in your `view`.
///
/// All errors will be stored as [`struct@AnyError`].
pub type Result<T> = result::Result<T, AnyError>;
/// 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()
}
}

View file

@ -91,7 +91,7 @@ pub trait OnAttribute<E, F, Rndr> {
impl<T, E, F, Rndr> OnAttribute<E, F, Rndr> for T
where
T: AddAnyAttr<Rndr>,
E: EventDescriptor + 'static,
E: EventDescriptor + Send + 'static,
E::EventType: 'static,
E::EventType: From<Rndr::Event>,
F: FnMut(E::EventType) + 'static,
@ -114,7 +114,7 @@ impl<T, E, F, Rndr> OnTargetAttribute<E, F, Self, Rndr> for T
where
Self: ElementType,
T: AddAnyAttr<Rndr>,
E: EventDescriptor + 'static,
E: EventDescriptor + Send + 'static,
E::EventType: 'static,
E::EventType: From<Rndr::Event>,
F: FnMut(Targeted<E::EventType, <Self as ElementType>::Output, Rndr>)

View file

@ -189,7 +189,7 @@ where
}
}
fn try_build(self) -> crate::error::Result<Self::FallibleState> {
fn try_build(self) -> any_error::Result<Self::FallibleState> {
let el = Rndr::create_element(self.tag);
let attrs = self.attributes.build(&el);
let mut children = self.children.try_build()?;
@ -205,7 +205,7 @@ where
fn try_rebuild(
self,
state: &mut Self::FallibleState,
) -> crate::error::Result<()> {
) -> any_error::Result<()> {
let ElementState {
attrs, children, ..
} = state;

View file

@ -58,20 +58,21 @@ impl<E, T, R> From<E> for Targeted<E, T, R> {
}
}
pub fn on<E, R>(event: E, mut cb: impl FnMut(E::EventType) + 'static) -> On<R>
pub fn on<E, R>(event: E, cb: impl FnMut(E::EventType) + 'static) -> On<R>
where
E: EventDescriptor + 'static,
E: EventDescriptor + Send + 'static,
E::EventType: 'static,
R: DomRenderer,
E::EventType: From<R::Event>,
{
let mut cb = send_wrapper::SendWrapper::new(cb);
On {
name: event.name(),
setup: Box::new(move |el| {
let cb = Box::new(move |ev: R::Event| {
let ev = E::EventType::from(ev);
cb(ev);
}) as Box<dyn FnMut(R::Event)>;
}) as Box<dyn FnMut(R::Event) + Send>;
if E::BUBBLES && cfg!(feature = "delegation") {
R::add_event_listener_delegated(
@ -93,7 +94,7 @@ pub fn on_target<E, T, R>(
mut cb: impl FnMut(Targeted<E::EventType, T, R>) + 'static,
) -> On<R>
where
E: EventDescriptor + 'static,
E: EventDescriptor + Send + 'static,
E::EventType: 'static,
R: DomRenderer,
E::EventType: From<R::Event>,
@ -104,7 +105,7 @@ where
pub struct On<R: DomRenderer> {
name: Cow<'static, str>,
#[allow(clippy::type_complexity)]
setup: Box<dyn FnOnce(&R::Element) -> Box<dyn FnOnce(&R::Element)>>,
setup: Box<dyn FnOnce(&R::Element) -> Box<dyn FnOnce(&R::Element)> + Send>,
ty: PhantomData<R>,
}

View file

@ -58,14 +58,14 @@ where
self.view.rebuild(state);
}
fn try_build(self) -> crate::error::Result<Self::FallibleState> {
fn try_build(self) -> any_error::Result<Self::FallibleState> {
self.view.try_build()
}
fn try_rebuild(
self,
state: &mut Self::FallibleState,
) -> crate::error::Result<()> {
) -> any_error::Result<()> {
self.view.try_rebuild(state)
}
}
@ -157,14 +157,14 @@ where
fn rebuild(self, _state: &mut Self::State) {}
fn try_build(self) -> crate::error::Result<Self::FallibleState> {
fn try_build(self) -> any_error::Result<Self::FallibleState> {
Ok(())
}
fn try_rebuild(
self,
_state: &mut Self::FallibleState,
) -> crate::error::Result<()> {
) -> any_error::Result<()> {
Ok(())
}
}

View file

@ -33,14 +33,14 @@ impl<R: Renderer> Render<R> for Doctype<R> {
fn rebuild(self, _state: &mut Self::State) {}
fn try_build(self) -> crate::error::Result<Self::FallibleState> {
fn try_build(self) -> any_error::Result<Self::FallibleState> {
Ok(())
}
fn try_rebuild(
self,
_state: &mut Self::FallibleState,
) -> crate::error::Result<()> {
) -> any_error::Result<()> {
Ok(())
}
}

View file

@ -28,7 +28,6 @@ use web_sys::Node;
pub mod async_views;
pub mod dom;
pub mod error;
pub mod html;
pub mod hydration;
pub mod mathml;

View file

@ -29,14 +29,14 @@ impl<R: Renderer> Render<R> for Oco<'static, str> {
}
}
fn try_build(self) -> crate::error::Result<Self::FallibleState> {
fn try_build(self) -> any_error::Result<Self::FallibleState> {
Ok(<Self as Render<R>>::build(self))
}
fn try_rebuild(
self,
state: &mut Self::FallibleState,
) -> crate::error::Result<()> {
) -> any_error::Result<()> {
<Self as Render<R>>::rebuild(self, state);
Ok(())
}

View file

@ -72,11 +72,11 @@ macro_rules! render_primitive {
}
}
fn try_build(self) -> crate::error::Result<Self::FallibleState> {
fn try_build(self) -> any_error::Result<Self::FallibleState> {
Ok(self.build())
}
fn try_rebuild(self, state: &mut Self::FallibleState) -> crate::error::Result<()> {
fn try_rebuild(self, state: &mut Self::FallibleState) -> any_error::Result<()> {
self.rebuild(state);
Ok(())
}
@ -224,14 +224,14 @@ where
}
}
fn try_build(self) -> crate::error::Result<Self::FallibleState> {
fn try_build(self) -> any_error::Result<Self::FallibleState> {
Ok(self.build())
}
fn try_rebuild(
self,
state: &mut Self::FallibleState,
) -> crate::error::Result<()> {
) -> any_error::Result<()> {
self.rebuild(state);
Ok(())
}

View file

@ -1,6 +1,5 @@
use crate::{
async_views::Suspend,
error::AnyError,
html::{
attribute::{Attribute, AttributeValue},
element::InnerHtmlValue,
@ -14,6 +13,7 @@ use crate::{
RenderHtml, ToTemplate,
},
};
use any_error::Error as AnyError;
use reactive_graph::{
computed::ScopedFuture,
effect::RenderEffect,
@ -72,7 +72,7 @@ where
.into()
}
fn try_build(mut self) -> crate::error::Result<Self::FallibleState> {
fn try_build(mut self) -> any_error::Result<Self::FallibleState> {
let parent = Observer::get();
let effect = RenderEffect::new({
move |prev| {
@ -135,7 +135,7 @@ where
fn try_rebuild(
self,
state: &mut Self::FallibleState,
) -> crate::error::Result<()> {
) -> any_error::Result<()> {
crate::log("RenderEffect::try_rebuild");
if let Some(inner) = &mut state.0 {
inner

View file

@ -81,14 +81,14 @@ where
state.owner = owner;
}
fn try_build(self) -> crate::error::Result<Self::FallibleState> {
fn try_build(self) -> any_error::Result<Self::FallibleState> {
todo!()
}
fn try_rebuild(
self,
_state: &mut Self::FallibleState,
) -> crate::error::Result<()> {
) -> any_error::Result<()> {
todo!()
}
}

View file

@ -117,7 +117,7 @@ impl DomRenderer for Dom {
el: &Self::Element,
name: &str,
cb: Box<dyn FnMut(Self::Event)>,
) -> Box<dyn FnOnce(&Self::Element)> {
) -> Box<dyn FnOnce(&Self::Element) + Send> {
let cb = wasm_bindgen::closure::Closure::wrap(cb);
let name = intern(name);
or_debug!(
@ -132,6 +132,7 @@ impl DomRenderer for Dom {
// return the remover
Box::new({
let name = name.to_owned();
let cb = send_wrapper::SendWrapper::new(cb);
move |el| {
or_debug!(
el.remove_event_listener_with_callback(
@ -162,7 +163,7 @@ impl DomRenderer for Dom {
name: Cow<'static, str>,
delegation_key: Cow<'static, str>,
cb: Box<dyn FnMut(Self::Event)>,
) -> Box<dyn FnOnce(&Self::Element)> {
) -> Box<dyn FnOnce(&Self::Element) + Send> {
let cb = Closure::wrap(cb);
let key = intern(&delegation_key);
or_debug!(
@ -242,8 +243,9 @@ impl DomRenderer for Dom {
// return the remover
Box::new({
let key = key.to_owned();
let cb = send_wrapper::SendWrapper::new(cb);
move |el| {
drop(cb);
drop(cb.take());
or_debug!(
js_sys::Reflect::delete_property(
el,

View file

@ -225,7 +225,7 @@ impl DomRenderer for MockDom {
el: &Self::Element,
name: &str,
cb: Box<dyn FnMut(Self::Event)>,
) -> Box<dyn FnOnce(&Self::Element)> {
) -> Box<dyn FnOnce(&Self::Element) + Send> {
todo!()
}
@ -234,7 +234,7 @@ impl DomRenderer for MockDom {
name: Cow<'static, str>,
delegation_key: Cow<'static, str>,
cb: Box<dyn FnMut(Self::Event)>,
) -> Box<dyn FnOnce(&Self::Element)> {
) -> Box<dyn FnOnce(&Self::Element) + Send> {
todo!()
}

View file

@ -108,7 +108,7 @@ pub trait Renderer: Sized + Debug {
}
/// A function that can be called to remove an event handler from an element after it has been added.
pub type RemoveEventHandler<T> = Box<dyn FnOnce(&T)>;
pub type RemoveEventHandler<T> = Box<dyn FnOnce(&T) + Send>;
/// Additional rendering behavior that applies only to DOM nodes.
pub trait DomRenderer: Renderer {
/// Generic event type, from which any specific event can be converted.

View file

@ -21,7 +21,7 @@ pub struct StreamBuilder {
id: Option<Vec<u16>>,
}
type PinnedFuture<T> = Pin<Box<dyn Future<Output = T> + Send + Sync>>;
type PinnedFuture<T> = Pin<Box<dyn Future<Output = T> + Send>>;
type ChunkFuture = PinnedFuture<VecDeque<StreamChunk>>;
impl StreamBuilder {
@ -48,7 +48,7 @@ impl StreamBuilder {
pub fn push_async(
&mut self,
should_block: bool,
fut: impl Future<Output = VecDeque<StreamChunk>> + Send + Sync + 'static,
fut: impl Future<Output = VecDeque<StreamChunk>> + Send + 'static,
) {
// flush sync chunk
let sync = mem::take(&mut self.sync_buf);
@ -141,7 +141,7 @@ impl StreamBuilder {
pub fn push_async_out_of_order<View, Rndr>(
&mut self,
should_block: bool,
view: impl Future<Output = View> + Send + Sync + 'static,
view: impl Future<Output = View> + Send + 'static,
position: &mut Position,
) where
View: RenderHtml<Rndr>,

View file

@ -11,7 +11,7 @@ where
R: Renderer,
{
type_id: TypeId,
value: Box<dyn Any>,
value: Box<dyn Any + Send>,
// The fields below are cfg-gated so they will not be included in WASM bundles if not needed.
// Ordinarily, the compiler can simply omit this dead code because the methods are not called.
@ -118,6 +118,7 @@ where
impl<T, R> IntoAny<R> for T
where
T: Send,
T: RenderHtml<R> + 'static,
T::State: 'static,
R: Renderer + 'static,
@ -129,7 +130,7 @@ where
#[cfg(feature = "ssr")]
let html_len = self.html_len();
let value = Box::new(self) as Box<dyn Any>;
let value = Box::new(self) as Box<dyn Any + Send>;
#[cfg(feature = "ssr")]
let to_html =
@ -269,14 +270,14 @@ where
(self.rebuild)(self.type_id, self.value, state)
}
fn try_build(self) -> crate::error::Result<Self::FallibleState> {
fn try_build(self) -> any_error::Result<Self::FallibleState> {
todo!()
}
fn try_rebuild(
self,
_state: &mut Self::FallibleState,
) -> crate::error::Result<()> {
) -> any_error::Result<()> {
todo!()
}
}

View file

@ -63,14 +63,14 @@ where
}
}
fn try_build(self) -> crate::error::Result<Self::FallibleState> {
fn try_build(self) -> any_error::Result<Self::FallibleState> {
todo!()
}
fn try_rebuild(
self,
_state: &mut Self::FallibleState,
) -> crate::error::Result<()> {
) -> any_error::Result<()> {
todo!()
}
}
@ -280,14 +280,14 @@ macro_rules! tuples {
state.state = new_state;
}
fn try_build(self) -> crate::error::Result<Self::FallibleState> {
fn try_build(self) -> any_error::Result<Self::FallibleState> {
todo!()
}
fn try_rebuild(
self,
_state: &mut Self::FallibleState,
) -> crate::error::Result<()> {
) -> any_error::Result<()> {
todo!()
}
}

View file

@ -1,17 +1,17 @@
use super::{Position, PositionState, RenderHtml};
use crate::{
error::AnyError,
hydration::Cursor,
ssr::StreamBuilder,
view::{Mountable, Render, Renderer},
};
use std::{error::Error, marker::PhantomData};
use any_error::Error as AnyError;
use std::marker::PhantomData;
impl<R, T, E> Render<R> for Result<T, E>
where
T: Render<R>,
R: Renderer,
E: Error + 'static,
E: Into<AnyError> + 'static,
{
type State = <Option<T> as Render<R>>::State;
type FallibleState = T::State;
@ -24,8 +24,8 @@ where
self.ok().rebuild(state);
}
fn try_build(self) -> crate::error::Result<Self::FallibleState> {
let inner = self.map_err(AnyError::new)?;
fn try_build(self) -> any_error::Result<Self::FallibleState> {
let inner = self.map_err(Into::into)?;
let state = inner.build();
Ok(state)
}
@ -33,8 +33,8 @@ where
fn try_rebuild(
self,
state: &mut Self::FallibleState,
) -> crate::error::Result<()> {
let inner = self.map_err(AnyError::new)?;
) -> any_error::Result<()> {
let inner = self.map_err(Into::into)?;
inner.rebuild(state);
Ok(())
}
@ -44,7 +44,7 @@ impl<R, T, E> RenderHtml<R> for Result<T, E>
where
T: RenderHtml<R>,
R: Renderer,
E: Error + 'static,
E: Into<AnyError> + 'static,
{
const MIN_LENGTH: usize = T::MIN_LENGTH;
@ -209,14 +209,14 @@ where
}
}
fn try_build(self) -> crate::error::Result<Self::FallibleState> {
fn try_build(self) -> any_error::Result<Self::FallibleState> {
Ok(self.build())
}
fn try_rebuild(
self,
state: &mut Self::FallibleState,
) -> crate::error::Result<()> {
) -> any_error::Result<()> {
self.rebuild(state);
Ok(())
}

View file

@ -43,7 +43,7 @@ where
}
}
fn try_build(self) -> crate::error::Result<Self::FallibleState> {
fn try_build(self) -> any_error::Result<Self::FallibleState> {
match self {
None => {
let placeholder = R::create_placeholder();
@ -68,7 +68,7 @@ where
fn try_rebuild(
self,
_state: &mut Self::FallibleState,
) -> crate::error::Result<()> {
) -> any_error::Result<()> {
todo!()
}
}
@ -246,7 +246,7 @@ where
}
}
fn try_build(self) -> crate::error::Result<Self::FallibleState> {
fn try_build(self) -> any_error::Result<Self::FallibleState> {
let states = self
.into_iter()
.map(T::try_build)
@ -261,7 +261,7 @@ where
fn try_rebuild(
self,
_state: &mut Self::FallibleState,
) -> crate::error::Result<()> {
) -> any_error::Result<()> {
todo!()
}
}

View file

@ -128,14 +128,14 @@ where
*hashed_items = new_hashed_items;
}
fn try_build(self) -> crate::error::Result<Self::FallibleState> {
fn try_build(self) -> any_error::Result<Self::FallibleState> {
todo!()
}
fn try_rebuild(
self,
_state: &mut Self::FallibleState,
) -> crate::error::Result<()> {
) -> any_error::Result<()> {
todo!()
}
}

View file

@ -33,12 +33,12 @@ pub trait Render<R: Renderer>: Sized {
/// Updates the view with new data.
fn rebuild(self, state: &mut Self::State);
fn try_build(self) -> crate::error::Result<Self::FallibleState>;
fn try_build(self) -> any_error::Result<Self::FallibleState>;
fn try_rebuild(
self,
state: &mut Self::FallibleState,
) -> crate::error::Result<()>;
) -> any_error::Result<()>;
}
#[derive(Debug, Clone, Copy)]

View file

@ -62,11 +62,11 @@ macro_rules! render_primitive {
}
}
fn try_build(self) -> crate::error::Result<Self::FallibleState> {
fn try_build(self) -> any_error::Result<Self::FallibleState> {
Ok(self.build())
}
fn try_rebuild(self, state: &mut Self::FallibleState) -> crate::error::Result<()> {
fn try_rebuild(self, state: &mut Self::FallibleState) -> any_error::Result<()> {
self.rebuild(state);
Ok(())
}

View file

@ -139,14 +139,14 @@ where
// This type is specified as static, so no rebuilding is done.
fn rebuild(self, _state: &mut Self::State) {}
fn try_build(self) -> crate::error::Result<Self::FallibleState> {
fn try_build(self) -> any_error::Result<Self::FallibleState> {
Ok(Render::<R>::build(self))
}
fn try_rebuild(
self,
state: &mut Self::FallibleState,
) -> crate::error::Result<()> {
) -> any_error::Result<()> {
Render::<R>::rebuild(self, state);
Ok(())
}

View file

@ -29,14 +29,14 @@ impl<'a, R: Renderer> Render<R> for &'a str {
}
}
fn try_build(self) -> crate::error::Result<Self::FallibleState> {
fn try_build(self) -> any_error::Result<Self::FallibleState> {
Ok(self.build())
}
fn try_rebuild(
self,
state: &mut Self::FallibleState,
) -> crate::error::Result<()> {
) -> any_error::Result<()> {
self.rebuild(state);
Ok(())
}
@ -156,14 +156,14 @@ impl<R: Renderer> Render<R> for String {
}
}
fn try_build(self) -> crate::error::Result<Self::FallibleState> {
fn try_build(self) -> any_error::Result<Self::FallibleState> {
Ok(self.build())
}
fn try_rebuild(
self,
state: &mut Self::FallibleState,
) -> crate::error::Result<()> {
) -> any_error::Result<()> {
self.rebuild(state);
Ok(())
}
@ -255,14 +255,14 @@ impl<R: Renderer> Render<R> for Rc<str> {
}
}
fn try_build(self) -> crate::error::Result<Self::FallibleState> {
fn try_build(self) -> any_error::Result<Self::FallibleState> {
Ok(self.build())
}
fn try_rebuild(
self,
state: &mut Self::FallibleState,
) -> crate::error::Result<()> {
) -> any_error::Result<()> {
self.rebuild(state);
Ok(())
}
@ -355,14 +355,14 @@ impl<R: Renderer> Render<R> for Arc<str> {
}
}
fn try_build(self) -> crate::error::Result<Self::FallibleState> {
fn try_build(self) -> any_error::Result<Self::FallibleState> {
Ok(self.build())
}
fn try_rebuild(
self,
state: &mut Self::FallibleState,
) -> crate::error::Result<()> {
) -> any_error::Result<()> {
self.rebuild(state);
Ok(())
}
@ -455,14 +455,14 @@ impl<'a, R: Renderer> Render<R> for Cow<'a, str> {
}
}
fn try_build(self) -> crate::error::Result<Self::FallibleState> {
fn try_build(self) -> any_error::Result<Self::FallibleState> {
Ok(self.build())
}
fn try_rebuild(
self,
state: &mut Self::FallibleState,
) -> crate::error::Result<()> {
) -> any_error::Result<()> {
self.rebuild(state);
Ok(())
}

View file

@ -60,14 +60,14 @@ where
self.view.rebuild(state)
}
fn try_build(self) -> crate::error::Result<Self::FallibleState> {
fn try_build(self) -> any_error::Result<Self::FallibleState> {
todo!()
}
fn try_rebuild(
self,
_state: &mut Self::FallibleState,
) -> crate::error::Result<()> {
) -> any_error::Result<()> {
todo!()
}
}

View file

@ -19,14 +19,14 @@ impl<R: Renderer> Render<R> for () {
fn rebuild(self, _state: &mut Self::State) {}
fn try_build(self) -> crate::error::Result<Self::FallibleState> {
fn try_build(self) -> any_error::Result<Self::FallibleState> {
Ok(())
}
fn try_rebuild(
self,
_state: &mut Self::FallibleState,
) -> crate::error::Result<()> {
) -> any_error::Result<()> {
Ok(())
}
}
@ -111,14 +111,14 @@ impl<A: Render<R>, R: Renderer> Render<R> for (A,) {
self.0.rebuild(state)
}
fn try_build(self) -> crate::error::Result<Self::FallibleState> {
fn try_build(self) -> any_error::Result<Self::FallibleState> {
self.0.try_build()
}
fn try_rebuild(
self,
state: &mut Self::FallibleState,
) -> crate::error::Result<()> {
) -> any_error::Result<()> {
self.0.try_rebuild(state)
}
}
@ -231,7 +231,7 @@ macro_rules! impl_view_for_tuples {
}
}
fn try_build(self) -> crate::error::Result<Self::FallibleState> {
fn try_build(self) -> any_error::Result<Self::FallibleState> {
#[allow(non_snake_case)]
let ($first, $($ty,)*) = self;
Ok((
@ -240,7 +240,7 @@ macro_rules! impl_view_for_tuples {
))
}
fn try_rebuild(self, state: &mut Self::FallibleState) -> crate::error::Result<()> {
fn try_rebuild(self, state: &mut Self::FallibleState) -> any_error::Result<()> {
paste::paste! {
let ([<$first:lower>], $([<$ty:lower>],)*) = self;
let ([<view_ $first:lower>], $([<view_ $ty:lower>],)*) = state;