implement AddAnyAttr for AnyView (#3562)

This commit is contained in:
Greg Johnston
2025-02-09 20:46:41 -05:00
committed by GitHub
parent 287fc47163
commit 299acd25f3
38 changed files with 1421 additions and 2180 deletions

493
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -19,7 +19,7 @@ async fn clear() {
// note that we start at the initial value of 10
let _dispose = mount_to(
test_wrapper.clone().unchecked_into(),
|| view! { <SimpleCounter initial_value=10 step=1 /> },
|| view! { <SimpleCounter initial_value=10 step=1/> },
);
// now we extract the buttons by iterating over the DOM
@@ -59,9 +59,9 @@ async fn clear() {
// .into_view() here is just a convenient way of specifying "use the regular DOM renderer"
.into_view()
// views are lazy -- they describe a DOM tree but don't create it yet
// calling .build(None) will actually build the DOM elements
.build(None)
// .build(None) returned an ElementState, which is a smart pointer for
// calling .build() will actually build the DOM elements
.build()
// .build() returned an ElementState, which is a smart pointer for
// a DOM element. So we can still just call .outer_html(), which access the outerHTML on
// the actual DOM element
.outer_html()
@@ -87,7 +87,7 @@ async fn inc() {
let _dispose = mount_to(
test_wrapper.clone().unchecked_into(),
|| view! { <SimpleCounter initial_value=0 step=1 /> },
|| view! { <SimpleCounter initial_value=0 step=1/> },
);
// You can do testing with vanilla DOM operations
@@ -150,7 +150,7 @@ async fn inc() {
}
}
.into_view()
.build(None)
.build()
.outer_html()
);
@@ -173,7 +173,7 @@ async fn inc() {
}
}
.into_view()
.build(None)
.build()
.outer_html()
);
}

View File

@@ -3,7 +3,6 @@ use crate::attr::{
Attribute, NextAttribute,
};
use leptos::prelude::*;
use tachys::view::any_view::ExtraAttrsMut;
/// Function stored to build/rebuild the wrapped children when attributes are added.
type ChildBuilder<T> = dyn Fn(AnyAttribute) -> T + Send + Sync + 'static;
@@ -44,7 +43,7 @@ pub fn AttributeInterceptor<Chil, T>(
) -> impl IntoView
where
Chil: Fn(AnyAttribute) -> T + Send + Sync + 'static,
T: IntoView + 'static,
T: IntoView,
{
AttributeInterceptorInner::new(children)
}
@@ -78,20 +77,16 @@ impl<T: IntoView> AttributeInterceptorInner<T, ()> {
impl<T: IntoView, A: Attribute> Render for AttributeInterceptorInner<T, A> {
type State = <T as Render>::State;
fn build(self, extra_attrs: Option<Vec<AnyAttribute>>) -> Self::State {
self.children.build(extra_attrs)
fn build(self) -> Self::State {
self.children.build()
}
fn rebuild(
self,
state: &mut Self::State,
extra_attrs: Option<Vec<AnyAttribute>>,
) {
self.children.rebuild(state, extra_attrs);
fn rebuild(self, state: &mut Self::State) {
self.children.rebuild(state);
}
}
impl<T: IntoView + 'static, A> AddAnyAttr for AttributeInterceptorInner<T, A>
impl<T: IntoView, A> AddAnyAttr for AttributeInterceptorInner<T, A>
where
A: Attribute,
{
@@ -119,23 +114,19 @@ where
}
}
impl<T: IntoView + 'static, A: Attribute> RenderHtml
for AttributeInterceptorInner<T, A>
{
impl<T: IntoView, A: Attribute> RenderHtml for AttributeInterceptorInner<T, A> {
type AsyncOutput = T::AsyncOutput;
type Owned = AttributeInterceptorInner<T, A::CloneableOwned>;
const MIN_LENGTH: usize = T::MIN_LENGTH;
fn dry_resolve(&mut self, extra_attrs: ExtraAttrsMut<'_>) {
self.children.dry_resolve(extra_attrs)
fn dry_resolve(&mut self) {
self.children.dry_resolve()
}
fn resolve(
self,
extra_attrs: ExtraAttrsMut<'_>,
) -> impl std::future::Future<Output = Self::AsyncOutput> + Send {
self.children.resolve(extra_attrs)
self.children.resolve()
}
fn to_html_with_buf(
@@ -144,14 +135,14 @@ impl<T: IntoView + 'static, A: Attribute> RenderHtml
position: &mut leptos::tachys::view::Position,
escape: bool,
mark_branches: bool,
extra_attrs: Option<Vec<AnyAttribute>>,
_extra_attrs: Vec<AnyAttribute>,
) {
self.children.to_html_with_buf(
buf,
position,
escape,
mark_branches,
extra_attrs,
vec![],
)
}
@@ -159,17 +150,7 @@ impl<T: IntoView + 'static, A: Attribute> RenderHtml
self,
cursor: &leptos::tachys::hydration::Cursor,
position: &leptos::tachys::view::PositionState,
extra_attrs: Option<Vec<AnyAttribute>>,
) -> Self::State {
self.children
.hydrate::<FROM_SERVER>(cursor, position, extra_attrs)
}
fn into_owned(self) -> Self::Owned {
AttributeInterceptorInner {
children_builder: self.children_builder,
children: self.children,
attributes: self.attributes.into_cloneable_owned(),
}
self.children.hydrate::<FROM_SERVER>(cursor, position)
}
}

View File

@@ -16,8 +16,8 @@ use tachys::{
reactive_graph::OwnedView,
ssr::StreamBuilder,
view::{
add_attr::AddAnyAttr, any_view::ExtraAttrsMut, Mountable, Position,
PositionState, Render, RenderHtml,
add_attr::AddAnyAttr, Mountable, Position, PositionState, Render,
RenderHtml,
},
};
use throw_error::{Error, ErrorHook, ErrorId};
@@ -163,6 +163,14 @@ where
self.children.insert_before_this(child)
}
}
fn elements(&self) -> Vec<tachys::renderer::types::Element> {
if let Some(fallback) = &self.fallback {
fallback.elements()
} else {
self.children.elements()
}
}
}
impl<Chil, FalFn, Fal> Render for ErrorBoundaryView<Chil, FalFn>
@@ -173,10 +181,10 @@ where
{
type State = RenderEffect<ErrorBoundaryViewState<Chil::State, Fal::State>>;
fn build(mut self, extra_attrs: Option<Vec<AnyAttribute>>) -> Self::State {
fn build(mut self) -> Self::State {
let hook = Arc::clone(&self.hook);
let _hook = throw_error::set_error_hook(Arc::clone(&hook));
let mut children = Some(self.children.build(extra_attrs.clone()));
let mut children = Some(self.children.build());
RenderEffect::new(
move |prev: Option<
ErrorBoundaryViewState<Chil::State, Fal::State>,
@@ -193,8 +201,7 @@ where
// yes errors, and was showing children
(false, None) => {
state.fallback = Some(
(self.fallback)(self.errors.clone())
.build(extra_attrs.clone()),
(self.fallback)(self.errors.clone()).build(),
);
state
.children
@@ -208,10 +215,8 @@ where
}
state
} else {
let fallback = (!self.errors_empty.get()).then(|| {
(self.fallback)(self.errors.clone())
.build(extra_attrs.clone())
});
let fallback = (!self.errors_empty.get())
.then(|| (self.fallback)(self.errors.clone()).build());
ErrorBoundaryViewState {
children: children.take().unwrap(),
fallback,
@@ -221,12 +226,8 @@ where
)
}
fn rebuild(
self,
state: &mut Self::State,
extra_attrs: Option<Vec<AnyAttribute>>,
) {
let new = self.build(extra_attrs);
fn rebuild(self, state: &mut Self::State) {
let new = self.build();
let mut old = std::mem::replace(state, new);
old.insert_before_this(state);
old.unmount();
@@ -275,18 +276,14 @@ where
Fal: RenderHtml + Send + 'static,
{
type AsyncOutput = ErrorBoundaryView<Chil::AsyncOutput, FalFn>;
type Owned = Self;
const MIN_LENGTH: usize = Chil::MIN_LENGTH;
fn dry_resolve(&mut self, extra_attrs: ExtraAttrsMut<'_>) {
self.children.dry_resolve(extra_attrs);
fn dry_resolve(&mut self) {
self.children.dry_resolve();
}
async fn resolve(
self,
extra_attrs: ExtraAttrsMut<'_>,
) -> Self::AsyncOutput {
async fn resolve(self) -> Self::AsyncOutput {
let ErrorBoundaryView {
hook,
boundary_id,
@@ -300,7 +297,7 @@ where
hook,
boundary_id,
errors_empty,
children: children.resolve(extra_attrs).await,
children: children.resolve().await,
fallback,
errors,
}
@@ -312,7 +309,7 @@ where
position: &mut Position,
escape: bool,
mark_branches: bool,
extra_attrs: Option<Vec<AnyAttribute>>,
extra_attrs: Vec<AnyAttribute>,
) {
// first, attempt to serialize the children to HTML, then check for errors
let _hook = throw_error::set_error_hook(self.hook);
@@ -347,7 +344,7 @@ where
position: &mut Position,
escape: bool,
mark_branches: bool,
extra_attrs: Option<Vec<AnyAttribute>>,
extra_attrs: Vec<AnyAttribute>,
) where
Self: Sized,
{
@@ -384,7 +381,6 @@ where
mut self,
cursor: &Cursor,
position: &PositionState,
extra_attrs: Option<Vec<AnyAttribute>>,
) -> Self::State {
let mut children = Some(self.children);
let hook = Arc::clone(&self.hook);
@@ -406,8 +402,7 @@ where
// yes errors, and was showing children
(false, None) => {
state.fallback = Some(
(self.fallback)(self.errors.clone())
.build(extra_attrs.clone()),
(self.fallback)(self.errors.clone()).build(),
);
state
.children
@@ -424,23 +419,15 @@ where
let children = children.take().unwrap();
let (children, fallback) = if self.errors_empty.get() {
(
children.hydrate::<FROM_SERVER>(
&cursor,
&position,
extra_attrs.clone(),
),
children.hydrate::<FROM_SERVER>(&cursor, &position),
None,
)
} else {
(
children.build(extra_attrs.clone()),
children.build(),
Some(
(self.fallback)(self.errors.clone())
.hydrate::<FROM_SERVER>(
&cursor,
&position,
extra_attrs.clone(),
),
.hydrate::<FROM_SERVER>(&cursor, &position),
),
)
};
@@ -450,10 +437,6 @@ where
},
)
}
fn into_owned(self) -> Self::Owned {
self
}
}
#[derive(Debug)]

View File

@@ -4,8 +4,8 @@ use tachys::{
hydration::Cursor,
ssr::StreamBuilder,
view::{
add_attr::AddAnyAttr, any_view::ExtraAttrsMut, Position, PositionState,
Render, RenderHtml, ToTemplate,
add_attr::AddAnyAttr, Position, PositionState, Render, RenderHtml,
ToTemplate,
},
};
@@ -76,34 +76,26 @@ where
impl<T: Render> Render for View<T> {
type State = T::State;
fn build(self, extra_attrs: Option<Vec<AnyAttribute>>) -> Self::State {
self.inner.build(extra_attrs)
fn build(self) -> Self::State {
self.inner.build()
}
fn rebuild(
self,
state: &mut Self::State,
extra_attrs: Option<Vec<AnyAttribute>>,
) {
self.inner.rebuild(state, extra_attrs)
fn rebuild(self, state: &mut Self::State) {
self.inner.rebuild(state)
}
}
impl<T: RenderHtml> RenderHtml for View<T> {
type AsyncOutput = T::AsyncOutput;
type Owned = View<T::Owned>;
const MIN_LENGTH: usize = <T as RenderHtml>::MIN_LENGTH;
async fn resolve(
self,
extra_attrs: ExtraAttrsMut<'_>,
) -> Self::AsyncOutput {
self.inner.resolve(extra_attrs).await
async fn resolve(self) -> Self::AsyncOutput {
self.inner.resolve().await
}
fn dry_resolve(&mut self, extra_attrs: ExtraAttrsMut<'_>) {
self.inner.dry_resolve(extra_attrs);
fn dry_resolve(&mut self) {
self.inner.dry_resolve();
}
fn to_html_with_buf(
@@ -112,7 +104,7 @@ impl<T: RenderHtml> RenderHtml for View<T> {
position: &mut Position,
escape: bool,
mark_branches: bool,
extra_attrs: Option<Vec<AnyAttribute>>,
extra_attrs: Vec<AnyAttribute>,
) {
#[cfg(debug_assertions)]
let vm = self.view_marker.to_owned();
@@ -141,7 +133,7 @@ impl<T: RenderHtml> RenderHtml for View<T> {
position: &mut Position,
escape: bool,
mark_branches: bool,
extra_attrs: Option<Vec<AnyAttribute>>,
extra_attrs: Vec<AnyAttribute>,
) where
Self: Sized,
{
@@ -170,18 +162,8 @@ impl<T: RenderHtml> RenderHtml for View<T> {
self,
cursor: &Cursor,
position: &PositionState,
extra_attrs: Option<Vec<AnyAttribute>>,
) -> Self::State {
self.inner
.hydrate::<FROM_SERVER>(cursor, position, extra_attrs)
}
fn into_owned(self) -> Self::Owned {
View {
inner: self.inner.into_owned(),
#[cfg(debug_assertions)]
view_marker: self.view_marker,
}
self.inner.hydrate::<FROM_SERVER>(cursor, position)
}
}

View File

@@ -71,7 +71,6 @@ where
view.hydrate::<true>(
&Cursor::new(parent.unchecked_into()),
&PositionState::default(),
None,
)
});
@@ -125,7 +124,7 @@ where
let owner = Owner::new();
let mountable = owner.with(move || {
let view = f().into_view();
let mut mountable = view.build(None);
let mut mountable = view.build();
mountable.mount(&parent, None);
mountable
});
@@ -153,7 +152,7 @@ where
let owner = Owner::new();
let mountable = owner.with(move || {
let view = f();
let mut mountable = view.build(None);
let mut mountable = view.build();
mountable.mount(parent, None);
mountable
});

View File

@@ -25,7 +25,6 @@ use tachys::{
ssr::StreamBuilder,
view::{
add_attr::AddAnyAttr,
any_view::ExtraAttrsMut,
either::{EitherKeepAlive, EitherKeepAliveState},
Mountable, Position, PositionState, Render, RenderHtml,
},
@@ -163,7 +162,7 @@ where
OwnedViewState<EitherKeepAliveState<Chil::State, Fal::State>>,
>;
fn build(self, extra_attrs: Option<Vec<AnyAttribute>>) -> Self::State {
fn build(self) -> Self::State {
let mut children = Some(self.children);
let mut fallback = Some(self.fallback);
let none_pending = self.none_pending;
@@ -188,20 +187,16 @@ where
);
if let Some(mut state) = prev {
this.rebuild(&mut state, extra_attrs.clone());
this.rebuild(&mut state);
state
} else {
this.build(extra_attrs.clone())
this.build()
}
})
}
fn rebuild(
self,
state: &mut Self::State,
extra_attrs: Option<Vec<AnyAttribute>>,
) {
let new = self.build(extra_attrs);
fn rebuild(self, state: &mut Self::State) {
let new = self.build();
let mut old = std::mem::replace(state, new);
old.insert_before_this(state);
old.unmount();
@@ -252,16 +247,12 @@ where
// i.e., if this is the child of another Suspense during SSR, don't wait for it: it will handle
// itself
type AsyncOutput = Self;
type Owned = Self;
const MIN_LENGTH: usize = Chil::MIN_LENGTH;
fn dry_resolve(&mut self, _extra_attrs: ExtraAttrsMut<'_>) {}
fn dry_resolve(&mut self) {}
async fn resolve(
self,
_extra_attrs: ExtraAttrsMut<'_>,
) -> Self::AsyncOutput {
async fn resolve(self) -> Self::AsyncOutput {
self
}
@@ -271,7 +262,7 @@ where
position: &mut Position,
escape: bool,
mark_branches: bool,
extra_attrs: Option<Vec<AnyAttribute>>,
extra_attrs: Vec<AnyAttribute>,
) {
self.fallback.to_html_with_buf(
buf,
@@ -288,7 +279,7 @@ where
position: &mut Position,
escape: bool,
mark_branches: bool,
mut extra_attrs: Option<Vec<AnyAttribute>>,
extra_attrs: Vec<AnyAttribute>,
) where
Self: Sized,
{
@@ -313,8 +304,7 @@ where
provide_context(LocalResourceNotifier::from(local_tx));
// walk over the tree of children once to make sure that all resource loads are registered
self.children
.dry_resolve(ExtraAttrsMut::from_owned(&mut extra_attrs));
self.children.dry_resolve();
// check the set of tasks to see if it is empty, now or later
let eff = reactive_graph::effect::Effect::new_isomorphic({
@@ -330,8 +320,7 @@ where
}
});
let mut fut = Box::pin(ScopedFuture::new(ErrorHookFuture::new({
let mut extra_attrs = extra_attrs.clone();
let mut fut = Box::pin(ScopedFuture::new(ErrorHookFuture::new(
async move {
// race the local resource notifier against the set of tasks
//
@@ -358,7 +347,7 @@ where
// but in situations like a <For each=|| some_resource.snapshot()/> we actually
// want to be able to 1) synchronously read a resource's value, but still 2) wait
// for it to load before we render everything
let mut children = Box::pin(self.children.resolve(ExtraAttrsMut::from_owned(&mut extra_attrs)).fuse());
let mut children = Box::pin(self.children.resolve().fuse());
// we continue racing the children against the "do we have any local
// resources?" Future
@@ -377,8 +366,8 @@ where
}
}
}
}
})));
},
)));
match fut.as_mut().now_or_never() {
Some(Some(resolved)) => {
Either::<Fal, _>::Right(resolved)
@@ -449,7 +438,6 @@ where
self,
cursor: &Cursor,
position: &PositionState,
extra_attrs: Option<Vec<AnyAttribute>>,
) -> Self::State {
let cursor = cursor.to_owned();
let position = position.to_owned();
@@ -478,21 +466,13 @@ where
);
if let Some(mut state) = prev {
this.rebuild(&mut state, extra_attrs.clone());
this.rebuild(&mut state);
state
} else {
this.hydrate::<FROM_SERVER>(
&cursor,
&position,
extra_attrs.clone(),
)
this.hydrate::<FROM_SERVER>(&cursor, &position)
}
})
}
fn into_owned(self) -> Self::Owned {
self
}
}
/// A wrapper that prevents [`Suspense`] from waiting for any resource reads that happen inside
@@ -512,16 +492,12 @@ where
{
type State = T::State;
fn build(self, extra_attrs: Option<Vec<AnyAttribute>>) -> Self::State {
(self.0)().build(extra_attrs)
fn build(self) -> Self::State {
(self.0)().build()
}
fn rebuild(
self,
state: &mut Self::State,
extra_attrs: Option<Vec<AnyAttribute>>,
) {
(self.0)().rebuild(state, extra_attrs);
fn rebuild(self, state: &mut Self::State) {
(self.0)().rebuild(state);
}
}
@@ -549,16 +525,12 @@ where
T: RenderHtml + 'static,
{
type AsyncOutput = Self;
type Owned = Self;
const MIN_LENGTH: usize = T::MIN_LENGTH;
fn dry_resolve(&mut self, _extra_attrs: ExtraAttrsMut<'_>) {}
fn dry_resolve(&mut self) {}
async fn resolve(
self,
_extra_attrs: ExtraAttrsMut<'_>,
) -> Self::AsyncOutput {
async fn resolve(self) -> Self::AsyncOutput {
self
}
@@ -568,7 +540,7 @@ where
position: &mut Position,
escape: bool,
mark_branches: bool,
extra_attrs: Option<Vec<AnyAttribute>>,
extra_attrs: Vec<AnyAttribute>,
) {
(self.0)().to_html_with_buf(
buf,
@@ -585,7 +557,7 @@ where
position: &mut Position,
escape: bool,
mark_branches: bool,
extra_attrs: Option<Vec<AnyAttribute>>,
extra_attrs: Vec<AnyAttribute>,
) where
Self: Sized,
{
@@ -602,12 +574,7 @@ where
self,
cursor: &Cursor,
position: &PositionState,
extra_attrs: Option<Vec<AnyAttribute>>,
) -> Self::State {
(self.0)().hydrate::<FROM_SERVER>(cursor, position, extra_attrs)
}
fn into_owned(self) -> Self::Owned {
self
(self.0)().hydrate::<FROM_SERVER>(cursor, position)
}
}

View File

@@ -84,8 +84,7 @@ mod view_implementations {
reactive_graph::{RenderEffectState, Suspend, SuspendState},
ssr::StreamBuilder,
view::{
add_attr::AddAnyAttr, any_view::ExtraAttrsMut, Position,
PositionState, Render, RenderHtml,
add_attr::AddAnyAttr, Position, PositionState, Render, RenderHtml,
},
};
@@ -96,17 +95,12 @@ mod view_implementations {
{
type State = RenderEffectState<SuspendState<T>>;
fn build(self, extra_attrs: Option<Vec<AnyAttribute>>) -> Self::State {
(move || Suspend::new(async move { self.await })).build(extra_attrs)
fn build(self) -> Self::State {
(move || Suspend::new(async move { self.await })).build()
}
fn rebuild(
self,
state: &mut Self::State,
extra_attrs: Option<Vec<AnyAttribute>>,
) {
(move || Suspend::new(async move { self.await }))
.rebuild(state, extra_attrs)
fn rebuild(self, state: &mut Self::State) {
(move || Suspend::new(async move { self.await })).rebuild(state)
}
}
@@ -141,20 +135,15 @@ mod view_implementations {
Ser: Send + 'static,
{
type AsyncOutput = Option<T>;
type Owned = Self;
const MIN_LENGTH: usize = 0;
fn dry_resolve(&mut self, _extra_attrs: ExtraAttrsMut<'_>) {
fn dry_resolve(&mut self) {
self.read();
}
fn resolve(
self,
extra_attrs: ExtraAttrsMut<'_>,
) -> impl Future<Output = Self::AsyncOutput> + Send {
(move || Suspend::new(async move { self.await }))
.resolve(extra_attrs)
fn resolve(self) -> impl Future<Output = Self::AsyncOutput> + Send {
(move || Suspend::new(async move { self.await })).resolve()
}
fn to_html_with_buf(
@@ -163,7 +152,7 @@ mod view_implementations {
position: &mut Position,
escape: bool,
mark_branches: bool,
extra_attrs: Option<Vec<AnyAttribute>>,
extra_attrs: Vec<AnyAttribute>,
) {
(move || Suspend::new(async move { self.await })).to_html_with_buf(
buf,
@@ -180,7 +169,7 @@ mod view_implementations {
position: &mut Position,
escape: bool,
mark_branches: bool,
extra_attrs: Option<Vec<AnyAttribute>>,
extra_attrs: Vec<AnyAttribute>,
) where
Self: Sized,
{
@@ -198,14 +187,9 @@ mod view_implementations {
self,
cursor: &Cursor,
position: &PositionState,
extra_attrs: Option<Vec<AnyAttribute>>,
) -> Self::State {
(move || Suspend::new(async move { self.await }))
.hydrate::<FROM_SERVER>(cursor, position, extra_attrs)
}
fn into_owned(self) -> Self::Owned {
self
.hydrate::<FROM_SERVER>(cursor, position)
}
}
}

View File

@@ -1,9 +1,6 @@
use crate::ServerMetaContext;
use leptos::{
attr::{
any_attribute::{AnyAttribute, AnyAttributeState},
NextAttribute,
},
attr::{any_attribute::AnyAttribute, NextAttribute},
component, html,
reactive::owner::use_context,
tachys::{
@@ -11,8 +8,8 @@ use leptos::{
html::attribute::Attribute,
hydration::Cursor,
view::{
add_attr::AddAnyAttr, any_view::ExtraAttrsMut, Mountable, Position,
PositionState, Render, RenderHtml,
add_attr::AddAnyAttr, Mountable, Position, PositionState, Render,
RenderHtml,
},
},
IntoView,
@@ -61,7 +58,6 @@ where
At: Attribute,
{
attributes: At::State,
extra_attrs: Option<Vec<AnyAttributeState>>,
}
impl<At> Render for BodyView<At>
@@ -70,27 +66,15 @@ where
{
type State = BodyViewState<At>;
fn build(self, extra_attrs: Option<Vec<AnyAttribute>>) -> Self::State {
fn build(self) -> Self::State {
let el = document().body().expect("there to be a <body> element");
let attributes = self.attributes.build(&el);
let extra_attrs = extra_attrs.map(|attrs| attrs.build(&el));
BodyViewState {
attributes,
extra_attrs,
}
BodyViewState { attributes }
}
fn rebuild(
self,
state: &mut Self::State,
extra_attrs: Option<Vec<AnyAttribute>>,
) {
fn rebuild(self, state: &mut Self::State) {
self.attributes.rebuild(&mut state.attributes);
if let (Some(extra_attrs), Some(extra_attr_states)) =
(extra_attrs, &mut state.extra_attrs)
{
extra_attrs.rebuild(extra_attr_states);
}
}
}
@@ -119,24 +103,17 @@ where
At: Attribute,
{
type AsyncOutput = BodyView<At::AsyncOutput>;
type Owned = BodyView<At::CloneableOwned>;
const MIN_LENGTH: usize = At::MIN_LENGTH;
fn dry_resolve(&mut self, mut extra_attrs: ExtraAttrsMut<'_>) {
fn dry_resolve(&mut self) {
self.attributes.dry_resolve();
extra_attrs.iter_mut().for_each(Attribute::dry_resolve);
}
async fn resolve(
self,
extra_attrs: ExtraAttrsMut<'_>,
) -> Self::AsyncOutput {
let (attributes, _) = futures::join!(
self.attributes.resolve(),
ExtraAttrsMut::resolve(extra_attrs)
);
BodyView { attributes }
async fn resolve(self) -> Self::AsyncOutput {
BodyView {
attributes: self.attributes.resolve().await,
}
}
fn to_html_with_buf(
@@ -145,13 +122,12 @@ where
_position: &mut Position,
_escape: bool,
_mark_branches: bool,
extra_attrs: Option<Vec<AnyAttribute>>,
extra_attrs: Vec<AnyAttribute>,
) {
if let Some(meta) = use_context::<ServerMetaContext>() {
let mut buf = String::new();
_ = html::attributes_to_html(
self.attributes,
extra_attrs,
(self.attributes, extra_attrs),
&mut buf,
);
if !buf.is_empty() {
@@ -164,23 +140,11 @@ where
self,
_cursor: &Cursor,
_position: &PositionState,
extra_attrs: Option<Vec<AnyAttribute>>,
) -> Self::State {
let el = document().body().expect("there to be a <body> element");
let attributes = self.attributes.hydrate::<FROM_SERVER>(&el);
let extra_attrs =
extra_attrs.map(|attrs| attrs.hydrate::<FROM_SERVER>(&el));
BodyViewState {
attributes,
extra_attrs,
}
}
fn into_owned(self) -> Self::Owned {
BodyView {
attributes: self.attributes.into_cloneable_owned(),
}
BodyViewState { attributes }
}
}
@@ -200,4 +164,11 @@ where
fn insert_before_this(&self, _child: &mut dyn Mountable) -> bool {
false
}
fn elements(&self) -> Vec<leptos::tachys::renderer::types::Element> {
vec![document()
.body()
.expect("there to be a <body> element")
.into()]
}
}

View File

@@ -1,9 +1,6 @@
use crate::ServerMetaContext;
use leptos::{
attr::{
any_attribute::{AnyAttribute, AnyAttributeState},
NextAttribute,
},
attr::{any_attribute::AnyAttribute, NextAttribute},
component, html,
reactive::owner::use_context,
tachys::{
@@ -11,8 +8,8 @@ use leptos::{
html::attribute::Attribute,
hydration::Cursor,
view::{
add_attr::AddAnyAttr, any_view::ExtraAttrsMut, Mountable, Position,
PositionState, Render, RenderHtml,
add_attr::AddAnyAttr, Mountable, Position, PositionState, Render,
RenderHtml,
},
},
IntoView,
@@ -58,7 +55,6 @@ where
At: Attribute,
{
attributes: At::State,
extra_attrs: Option<Vec<AnyAttributeState>>,
}
impl<At> Render for HtmlView<At>
@@ -67,33 +63,18 @@ where
{
type State = HtmlViewState<At>;
fn build(self, extra_attrs: Option<Vec<AnyAttribute>>) -> Self::State {
fn build(self) -> Self::State {
let el = document()
.document_element()
.expect("there to be a <html> element");
let attributes = self.attributes.build(&el);
let extra_attrs = extra_attrs.map(|attrs| {
attrs.into_iter().map(|attr| attr.build(&el)).collect()
});
HtmlViewState {
attributes,
extra_attrs,
}
HtmlViewState { attributes }
}
fn rebuild(
self,
state: &mut Self::State,
extra_attrs: Option<Vec<AnyAttribute>>,
) {
fn rebuild(self, state: &mut Self::State) {
self.attributes.rebuild(&mut state.attributes);
if let (Some(extra_attrs), Some(extra_attr_states)) =
(extra_attrs, &mut state.extra_attrs)
{
extra_attrs.rebuild(extra_attr_states);
}
}
}
@@ -122,24 +103,17 @@ where
At: Attribute,
{
type AsyncOutput = HtmlView<At::AsyncOutput>;
type Owned = HtmlView<At::CloneableOwned>;
const MIN_LENGTH: usize = At::MIN_LENGTH;
fn dry_resolve(&mut self, mut extra_attrs: ExtraAttrsMut<'_>) {
fn dry_resolve(&mut self) {
self.attributes.dry_resolve();
extra_attrs.iter_mut().for_each(Attribute::dry_resolve);
}
async fn resolve(
self,
extra_attrs: ExtraAttrsMut<'_>,
) -> Self::AsyncOutput {
let (attributes, _) = futures::join!(
self.attributes.resolve(),
ExtraAttrsMut::resolve(extra_attrs)
);
HtmlView { attributes }
async fn resolve(self) -> Self::AsyncOutput {
HtmlView {
attributes: self.attributes.resolve().await,
}
}
fn to_html_with_buf(
@@ -148,13 +122,12 @@ where
_position: &mut Position,
_escape: bool,
_mark_branches: bool,
extra_attrs: Option<Vec<AnyAttribute>>,
extra_attrs: Vec<AnyAttribute>,
) {
if let Some(meta) = use_context::<ServerMetaContext>() {
let mut buf = String::new();
_ = html::attributes_to_html(
self.attributes,
extra_attrs,
(self.attributes, extra_attrs),
&mut buf,
);
if !buf.is_empty() {
@@ -167,30 +140,14 @@ where
self,
_cursor: &Cursor,
_position: &PositionState,
extra_attrs: Option<Vec<AnyAttribute>>,
) -> Self::State {
let el = document()
.document_element()
.expect("there to be a <html> element");
let attributes = self.attributes.hydrate::<FROM_SERVER>(&el);
let extra_attrs = extra_attrs.map(|attrs| {
attrs
.into_iter()
.map(|attr| attr.hydrate::<FROM_SERVER>(&el))
.collect()
});
HtmlViewState {
attributes,
extra_attrs,
}
}
fn into_owned(self) -> Self::Owned {
HtmlView {
attributes: self.attributes.into_cloneable_owned(),
}
HtmlViewState { attributes }
}
}
@@ -212,4 +169,10 @@ where
fn insert_before_this(&self, _child: &mut dyn Mountable) -> bool {
false
}
fn elements(&self) -> Vec<leptos::tachys::renderer::types::Element> {
vec![document()
.document_element()
.expect("there to be a <html> element")]
}
}

View File

@@ -57,8 +57,8 @@ use leptos::{
},
hydration::Cursor,
view::{
add_attr::AddAnyAttr, any_view::ExtraAttrsMut, Mountable, Position,
PositionState, Render, RenderHtml,
add_attr::AddAnyAttr, Mountable, Position, PositionState, Render,
RenderHtml,
},
},
IntoView,
@@ -334,7 +334,7 @@ where
&mut Position::NextChild,
false,
false,
None,
vec![],
);
_ = cx.elements.send(buf); // fails only if the receiver is already dropped
} else {
@@ -391,17 +391,13 @@ where
{
type State = RegisteredMetaTagState<E, At, Ch>;
fn build(self, extra_attrs: Option<Vec<AnyAttribute>>) -> Self::State {
let state = self.el.unwrap().build(extra_attrs);
fn build(self) -> Self::State {
let state = self.el.unwrap().build();
RegisteredMetaTagState { state }
}
fn rebuild(
self,
state: &mut Self::State,
extra_attrs: Option<Vec<AnyAttribute>>,
) {
self.el.unwrap().rebuild(&mut state.state, extra_attrs);
fn rebuild(self, state: &mut Self::State) {
self.el.unwrap().rebuild(&mut state.state);
}
}
@@ -434,18 +430,14 @@ where
Ch: RenderHtml + Send,
{
type AsyncOutput = Self;
type Owned = RegisteredMetaTag<E, At::CloneableOwned, Ch::Owned>;
const MIN_LENGTH: usize = 0;
fn dry_resolve(&mut self, extra_attrs: ExtraAttrsMut<'_>) {
self.el.dry_resolve(extra_attrs)
fn dry_resolve(&mut self) {
self.el.dry_resolve()
}
async fn resolve(
self,
_extra_attrs: ExtraAttrsMut<'_>,
) -> Self::AsyncOutput {
async fn resolve(self) -> Self::AsyncOutput {
self // TODO?
}
@@ -455,7 +447,7 @@ where
_position: &mut Position,
_escape: bool,
_mark_branches: bool,
_extra_attrs: Option<Vec<AnyAttribute>>,
_extra_attrs: Vec<AnyAttribute>,
) {
// meta tags are rendered into the buffer stored into the context
// the value has already been taken out, when we're on the server
@@ -465,7 +457,6 @@ where
self,
_cursor: &Cursor,
_position: &PositionState,
extra_attrs: Option<Vec<AnyAttribute>>,
) -> Self::State {
let cursor = use_context::<MetaContext>()
.expect(
@@ -476,16 +467,9 @@ where
let state = self.el.unwrap().hydrate::<FROM_SERVER>(
&cursor,
&PositionState::new(Position::NextChild),
extra_attrs,
);
RegisteredMetaTagState { state }
}
fn into_owned(self) -> Self::Owned {
RegisteredMetaTag {
el: self.el.map(|inner| inner.into_owned()),
}
}
}
impl<E, At, Ch> Mountable for RegisteredMetaTagState<E, At, Ch>
@@ -518,6 +502,10 @@ where
// we intended!
false
}
fn elements(&self) -> Vec<leptos::tachys::renderer::types::Element> {
self.state.elements()
}
}
/// During server rendering, inserts the meta tags that have been generated by the other components
@@ -538,14 +526,9 @@ struct MetaTagsView;
impl Render for MetaTagsView {
type State = ();
fn build(self, _extra_attrs: Option<Vec<AnyAttribute>>) -> Self::State {}
fn build(self) -> Self::State {}
fn rebuild(
self,
_state: &mut Self::State,
_extra_attrs: Option<Vec<AnyAttribute>>,
) {
}
fn rebuild(self, _state: &mut Self::State) {}
}
impl AddAnyAttr for MetaTagsView {
@@ -564,16 +547,12 @@ impl AddAnyAttr for MetaTagsView {
impl RenderHtml for MetaTagsView {
type AsyncOutput = Self;
type Owned = Self;
const MIN_LENGTH: usize = 0;
fn dry_resolve(&mut self, _extra_attrs: ExtraAttrsMut<'_>) {}
fn dry_resolve(&mut self) {}
async fn resolve(
self,
_extra_attrs: ExtraAttrsMut<'_>,
) -> Self::AsyncOutput {
async fn resolve(self) -> Self::AsyncOutput {
self
}
@@ -583,7 +562,7 @@ impl RenderHtml for MetaTagsView {
_position: &mut Position,
_escape: bool,
_mark_branches: bool,
_extra_attrs: Option<Vec<AnyAttribute>>,
_extra_attrs: Vec<AnyAttribute>,
) {
buf.push_str("<!--HEAD-->");
}
@@ -592,13 +571,8 @@ impl RenderHtml for MetaTagsView {
self,
_cursor: &Cursor,
_position: &PositionState,
_extra_attrs: Option<Vec<AnyAttribute>>,
) -> Self::State {
}
fn into_owned(self) -> Self::Owned {
self
}
}
pub(crate) trait OrDefaultNonce {

View File

@@ -11,8 +11,8 @@ use leptos::{
dom::document,
hydration::Cursor,
view::{
add_attr::AddAnyAttr, any_view::ExtraAttrsMut, Mountable, Position,
PositionState, Render, RenderHtml,
add_attr::AddAnyAttr, Mountable, Position, PositionState, Render,
RenderHtml,
},
},
text_prop::TextProp,
@@ -189,7 +189,7 @@ struct TitleViewState {
impl Render for TitleView {
type State = TitleViewState;
fn build(mut self, _extra_attrs: Option<Vec<AnyAttribute>>) -> Self::State {
fn build(mut self) -> Self::State {
let el = self.el();
let meta = self.meta;
if let Some(formatter) = self.formatter.take() {
@@ -213,12 +213,8 @@ impl Render for TitleView {
TitleViewState { effect }
}
fn rebuild(
self,
state: &mut Self::State,
extra_attrs: Option<Vec<AnyAttribute>>,
) {
*state = self.build(extra_attrs);
fn rebuild(self, state: &mut Self::State) {
*state = self.build();
}
}
@@ -238,16 +234,12 @@ impl AddAnyAttr for TitleView {
impl RenderHtml for TitleView {
type AsyncOutput = Self;
type Owned = Self;
const MIN_LENGTH: usize = 0;
fn dry_resolve(&mut self, _extra_attrs: ExtraAttrsMut<'_>) {}
fn dry_resolve(&mut self) {}
async fn resolve(
self,
_extra_attrs: ExtraAttrsMut<'_>,
) -> Self::AsyncOutput {
async fn resolve(self) -> Self::AsyncOutput {
self
}
@@ -257,7 +249,7 @@ impl RenderHtml for TitleView {
_position: &mut Position,
_escape: bool,
_mark_branches: bool,
_extra_attrs: Option<Vec<AnyAttribute>>,
_extra_attrs: Vec<AnyAttribute>,
) {
// meta tags are rendered into the buffer stored into the context
// the value has already been taken out, when we're on the server
@@ -267,7 +259,6 @@ impl RenderHtml for TitleView {
mut self,
_cursor: &Cursor,
_position: &PositionState,
_extra_attrs: Option<Vec<AnyAttribute>>,
) -> Self::State {
let el = self.el();
let meta = self.meta;
@@ -292,10 +283,6 @@ impl RenderHtml for TitleView {
});
TitleViewState { effect }
}
fn into_owned(self) -> Self::Owned {
self
}
}
impl Mountable for TitleViewState {
@@ -313,4 +300,8 @@ impl Mountable for TitleViewState {
fn insert_before_this(&self, _child: &mut dyn Mountable) -> bool {
false
}
fn elements(&self) -> Vec<leptos::tachys::renderer::types::Element> {
vec![]
}
}

View File

@@ -26,7 +26,7 @@ use tachys::{
ssr::StreamBuilder,
view::{
add_attr::AddAnyAttr,
any_view::{AnyView, AnyViewState, ExtraAttrsMut, IntoAny},
any_view::{AnyView, AnyViewState, IntoAny},
Mountable, Position, PositionState, Render, RenderHtml,
},
};
@@ -69,6 +69,10 @@ impl Mountable for FlatRoutesViewState {
fn insert_before_this(&self, child: &mut dyn Mountable) -> bool {
self.view.insert_before_this(child)
}
fn elements(&self) -> Vec<tachys::renderer::types::Element> {
self.view.elements()
}
}
impl<Loc, Defs, FalFn, Fal> Render for FlatRoutesView<Loc, Defs, FalFn>
@@ -80,7 +84,7 @@ where
{
type State = Rc<RefCell<FlatRoutesViewState>>;
fn build(self, extra_attrs: Option<Vec<AnyAttribute>>) -> Self::State {
fn build(self) -> Self::State {
let FlatRoutesView {
current_url,
routes,
@@ -118,7 +122,7 @@ where
match new_match {
None => Rc::new(RefCell::new(FlatRoutesViewState {
view: fallback().into_any().build(extra_attrs),
view: fallback().into_any().build(),
id,
owner,
params,
@@ -151,7 +155,7 @@ where
match view.as_mut().now_or_never() {
Some(view) => Rc::new(RefCell::new(FlatRoutesViewState {
view: view.into_any().build(extra_attrs),
view: view.into_any().build(),
id,
owner,
params,
@@ -162,7 +166,7 @@ where
None => {
let state =
Rc::new(RefCell::new(FlatRoutesViewState {
view: ().into_any().build(extra_attrs.clone()),
view: ().into_any().build(),
id,
owner,
params,
@@ -175,10 +179,8 @@ where
let state = Rc::clone(&state);
async move {
let view = view.await;
view.into_any().rebuild(
&mut state.borrow_mut().view,
extra_attrs,
);
view.into_any()
.rebuild(&mut state.borrow_mut().view);
}
});
@@ -189,11 +191,7 @@ where
}
}
fn rebuild(
self,
state: &mut Self::State,
extra_attrs: Option<Vec<AnyAttribute>>,
) {
fn rebuild(self, state: &mut Self::State) {
let FlatRoutesView {
current_url,
location,
@@ -271,9 +269,7 @@ where
provide_context(url);
provide_context(params_memo);
provide_context(Matched(ArcMemo::from(new_matched)));
fallback()
.into_any()
.rebuild(&mut state.borrow_mut().view, extra_attrs)
fallback().into_any().rebuild(&mut state.borrow_mut().view)
});
if let Some(location) = location {
location.ready_to_complete();
@@ -323,10 +319,8 @@ where
== spawned_path
{
let rebuild = move || {
view.into_any().rebuild(
&mut state.borrow_mut().view,
extra_attrs,
);
view.into_any()
.rebuild(&mut state.borrow_mut().view);
};
if transition {
start_view_transition(0, is_back, rebuild);
@@ -354,7 +348,7 @@ impl<Loc, Defs, FalFn, Fal> AddAnyAttr for FlatRoutesView<Loc, Defs, FalFn>
where
Loc: LocationProvider + Send,
Defs: MatchNestedRoutes + Send + 'static,
FalFn: FnOnce() -> Fal + Send + 'static,
FalFn: FnOnce() -> Fal + Send,
Fal: RenderHtml + 'static,
{
type Output<SomeNewAttr: leptos::attr::Attribute> =
@@ -427,20 +421,16 @@ impl<Loc, Defs, FalFn, Fal> RenderHtml for FlatRoutesView<Loc, Defs, FalFn>
where
Loc: LocationProvider + Send,
Defs: MatchNestedRoutes + Send + 'static,
FalFn: FnOnce() -> Fal + Send + 'static,
FalFn: FnOnce() -> Fal + Send,
Fal: RenderHtml + 'static,
{
type AsyncOutput = Self;
type Owned = Self;
const MIN_LENGTH: usize = <Either<Fal, AnyView> as RenderHtml>::MIN_LENGTH;
fn dry_resolve(&mut self, _extra_attrs: ExtraAttrsMut<'_>) {}
fn dry_resolve(&mut self) {}
async fn resolve(
self,
_extra_attrs: ExtraAttrsMut<'_>,
) -> Self::AsyncOutput {
async fn resolve(self) -> Self::AsyncOutput {
self
}
@@ -450,7 +440,7 @@ where
position: &mut Position,
escape: bool,
mark_branches: bool,
extra_attrs: Option<Vec<AnyAttribute>>,
extra_attrs: Vec<AnyAttribute>,
) {
// if this is being run on the server for the first time, generating all possible routes
if RouteList::is_generating() {
@@ -513,7 +503,7 @@ where
position: &mut Position,
escape: bool,
mark_branches: bool,
extra_attrs: Option<Vec<AnyAttribute>>,
extra_attrs: Vec<AnyAttribute>,
) where
Self: Sized,
{
@@ -531,7 +521,6 @@ where
self,
cursor: &Cursor,
position: &PositionState,
extra_attrs: Option<Vec<AnyAttribute>>,
) -> Self::State {
// this can be mostly the same as the build() implementation, but with hydrate()
//
@@ -576,11 +565,9 @@ where
match new_match {
None => Rc::new(RefCell::new(FlatRoutesViewState {
view: fallback().into_any().hydrate::<FROM_SERVER>(
cursor,
position,
extra_attrs,
),
view: fallback()
.into_any()
.hydrate::<FROM_SERVER>(cursor, position),
id,
owner,
params,
@@ -613,11 +600,9 @@ where
match view.as_mut().now_or_never() {
Some(view) => Rc::new(RefCell::new(FlatRoutesViewState {
view: view.into_any().hydrate::<FROM_SERVER>(
cursor,
position,
extra_attrs,
),
view: view
.into_any()
.hydrate::<FROM_SERVER>(cursor, position),
id,
owner,
params,
@@ -633,8 +618,4 @@ where
}
}
}
fn into_owned(self) -> Self::Owned {
self
}
}

View File

@@ -36,7 +36,7 @@ use tachys::{
ssr::StreamBuilder,
view::{
add_attr::AddAnyAttr,
any_view::{AnyView, ExtraAttrsMut, IntoAny},
any_view::{AnyView, IntoAny},
either::EitherOf3State,
Mountable, Position, PositionState, Render, RenderHtml,
},
@@ -76,7 +76,7 @@ where
// TODO support fallback while loading
type State = NestedRouteViewState<Fal>;
fn build(self, extra_attrs: Option<Vec<AnyAttribute>>) -> Self::State {
fn build(self) -> Self::State {
let NestedRoutesView {
routes,
outer_owner,
@@ -95,7 +95,7 @@ where
let new_match = routes.match_route(url.path());
// start with an empty view because we'll be loading routes async
let view = EitherOf3::A(()).build(extra_attrs.clone());
let view = EitherOf3::A(()).build();
let view = Rc::new(RefCell::new(view));
let matched_view = match new_match {
None => EitherOf3::B(fallback()),
@@ -120,7 +120,7 @@ where
for trigger in triggers {
trigger.notify();
}
matched_view.rebuild(&mut *view.borrow_mut(), extra_attrs);
matched_view.rebuild(&mut *view.borrow_mut());
})
});
@@ -132,11 +132,7 @@ where
}
}
fn rebuild(
self,
state: &mut Self::State,
extra_attrs: Option<Vec<AnyAttribute>>,
) {
fn rebuild(self, state: &mut Self::State) {
let url_snapshot = self.current_url.get_untracked();
// if the path is the same, we do not need to re-route
@@ -158,7 +154,7 @@ where
match new_match {
None => {
EitherOf3::<(), Fal, AnyView>::B((self.fallback)())
.rebuild(&mut state.view.borrow_mut(), extra_attrs);
.rebuild(&mut state.view.borrow_mut());
state.outlets.clear();
if let Some(loc) = self.location {
loc.ready_to_complete();
@@ -217,10 +213,7 @@ where
if matches!(state.view.borrow().state, EitherOf3::B(_)) {
self.outer_owner.with(|| {
EitherOf3::<(), Fal, AnyView>::C(Outlet().into_any())
.rebuild(
&mut *state.view.borrow_mut(),
extra_attrs,
);
.rebuild(&mut *state.view.borrow_mut());
})
}
}
@@ -235,8 +228,8 @@ where
impl<Loc, Defs, Fal, FalFn> AddAnyAttr for NestedRoutesView<Loc, Defs, FalFn>
where
Loc: LocationProvider + Send,
Defs: MatchNestedRoutes + Send + 'static,
FalFn: FnOnce() -> Fal + Send + 'static,
Defs: MatchNestedRoutes + Send,
FalFn: FnOnce() -> Fal + Send,
Fal: RenderHtml + 'static,
{
type Output<SomeNewAttr: leptos::attr::Attribute> =
@@ -256,21 +249,17 @@ where
impl<Loc, Defs, FalFn, Fal> RenderHtml for NestedRoutesView<Loc, Defs, FalFn>
where
Loc: LocationProvider + Send,
Defs: MatchNestedRoutes + Send + 'static,
FalFn: FnOnce() -> Fal + Send + 'static,
Defs: MatchNestedRoutes + Send,
FalFn: FnOnce() -> Fal + Send,
Fal: RenderHtml + 'static,
{
type AsyncOutput = Self;
type Owned = Self;
const MIN_LENGTH: usize = 0; // TODO
fn dry_resolve(&mut self, _extra_attrs: ExtraAttrsMut<'_>) {}
fn dry_resolve(&mut self) {}
async fn resolve(
self,
_extra_attrs: ExtraAttrsMut<'_>,
) -> Self::AsyncOutput {
async fn resolve(self) -> Self::AsyncOutput {
self
}
@@ -280,7 +269,7 @@ where
position: &mut Position,
escape: bool,
mark_branches: bool,
extra_attrs: Option<Vec<AnyAttribute>>,
extra_attrs: Vec<AnyAttribute>,
) {
// if this is being run on the server for the first time, generating all possible routes
if RouteList::is_generating() {
@@ -376,7 +365,7 @@ where
position: &mut Position,
escape: bool,
mark_branches: bool,
extra_attrs: Option<Vec<AnyAttribute>>,
extra_attrs: Vec<AnyAttribute>,
) where
Self: Sized,
{
@@ -427,7 +416,6 @@ where
self,
cursor: &Cursor,
position: &PositionState,
extra_attrs: Option<Vec<AnyAttribute>>,
) -> Self::State {
let NestedRoutesView {
routes,
@@ -467,7 +455,7 @@ where
outer_owner.with(|| EitherOf3::C(Outlet().into_any()))
}
}
.hydrate::<FROM_SERVER>(cursor, position, extra_attrs),
.hydrate::<FROM_SERVER>(cursor, position),
));
NestedRouteViewState {
@@ -477,10 +465,6 @@ where
view,
}
}
fn into_owned(self) -> Self::Owned {
self
}
}
type OutletViewFn = Box<dyn Fn() -> Suspend<AnyView> + Send>;
@@ -897,6 +881,10 @@ where
fn insert_before_this(&self, child: &mut dyn Mountable) -> bool {
self.view.insert_before_this(child)
}
fn elements(&self) -> Vec<tachys::renderer::types::Element> {
self.view.elements()
}
}
/// Displays the child route nested in a parent route, allowing you to control exactly where

View File

@@ -89,7 +89,7 @@ where
type State =
ReactiveRouterInnerState<Rndr, Loc, Defs, FallbackFn, Fallback>;
fn build(self, _extra_attrs: Option<Vec<AnyAttribute>>) -> Self::State {
fn build(self) -> Self::State {
let (prev_id, inner) = self.inner.fallback_or_view();
let owner = self.owner.with(Owner::new);
ReactiveRouterInnerState {
@@ -100,11 +100,7 @@ where
}
}
fn rebuild(
self,
state: &mut Self::State,
_extra_attrs: Option<Vec<AnyAttribute>>,
) {
fn rebuild(self, state: &mut Self::State) {
let (new_id, view) = self.inner.fallback_or_view();
if new_id != state.prev_id {
state.owner = self.owner.with(Owner::new)
@@ -133,9 +129,7 @@ where
buf: &mut String,
position: &mut Position,
escape: bool,
mark_branches: bool,
_extra_attrs: Option<Vec<AnyAttribute>>,
) {
mark_branches: bool, extra_attrs: Vec<AnyAttribute>) {
// if this is being run on the server for the first time, generating all possible routes
if RouteList::is_generating() {
let mut routes = RouteList::new();
@@ -160,9 +154,7 @@ where
buf: &mut StreamBuilder,
position: &mut Position,
escape: bool,
mark_branches: bool,
_extra_attrs: Option<Vec<AnyAttribute>>,
) where
mark_branches: bool, extra_attrs: Vec<AnyAttribute>) where
Self: Sized,
{
self.inner
@@ -175,7 +167,6 @@ where
self,
cursor: &Cursor,
position: &PositionState,
_extra_attrs: Option<Vec<AnyAttribute>>,
) -> Self::State {
let (prev_id, inner) = self.inner.fallback_or_view();
let owner = self.owner.with(Owner::new);
@@ -287,7 +278,7 @@ where
{
type State = ReactiveRouteState<View::State>;
fn build(self, extra_attrs: Option<Vec<AnyAttribute>>) -> Self::State {
fn build(self) -> Self::State {
let MatchedRoute {
search_params,
params,
@@ -298,19 +289,14 @@ where
params: ArcRwSignal::new(params),
matched: ArcRwSignal::new(matched),
};
let view_state =
untrack(|| (self.view_fn)(&matched).build(extra_attrs.clone()));
let view_state = untrack(|| (self.view_fn)(&matched).build());
ReactiveRouteState {
matched,
view_state,
}
}
fn rebuild(
mut self,
state: &mut Self::State,
_extra_attrs: Option<Vec<AnyAttribute>>,
) {
fn rebuild(mut self, state: &mut Self::State) {
let ReactiveRouteState { matched, .. } = state;
matched
.search_params
@@ -335,9 +321,7 @@ where
buf: &mut String,
position: &mut Position,
escape: bool,
mark_branches: bool,
extra_attrs: Option<Vec<AnyAttribute>>,
) {
mark_branches: bool, extra_attrs: Vec<AnyAttribute>) {
let MatchedRoute {
search_params,
params,
@@ -349,12 +333,7 @@ where
matched: ArcRwSignal::new(matched),
};
untrack(|| {
(self.view_fn)(&matched).to_html_with_buf(
buf,
position,
escape,
extra_attrs.clone(),
)
(self.view_fn)(&matched).to_html_with_buf(buf, position, escape)
});
}
@@ -363,9 +342,7 @@ where
buf: &mut StreamBuilder,
position: &mut Position,
escape: bool,
mark_branches: bool,
extra_attrs: Option<Vec<AnyAttribute>>,
) where
mark_branches: bool, extra_attrs: Vec<AnyAttribute>) where
Self: Sized,
{
let MatchedRoute {
@@ -379,12 +356,8 @@ where
matched: ArcRwSignal::new(matched),
};
untrack(|| {
(self.view_fn)(&matched).to_html_async_with_buf::<OUT_OF_ORDER>(
buf,
position,
escape,
extra_attrs.clone(),
)
(self.view_fn)(&matched)
.to_html_async_with_buf::<OUT_OF_ORDER>(buf, position, escape)
});
}
@@ -392,7 +365,6 @@ where
self,
cursor: &Cursor,
position: &PositionState,
extra_attrs: Option<Vec<AnyAttribute>>,
) -> Self::State {
let MatchedRoute {
search_params,
@@ -405,11 +377,7 @@ where
matched: ArcRwSignal::new(matched),
};
let view_state = untrack(|| {
(self.view_fn)(&matched).hydrate::<FROM_SERVER>(
cursor,
position,
extra_attrs.clone(),
)
(self.view_fn)(&matched).hydrate::<FROM_SERVER>(cursor, position)
});
ReactiveRouteState {
matched,

View File

@@ -1,4 +1,3 @@
#![allow(unused_mut)]
use super::{Attribute, NextAttribute};
use dyn_clone::DynClone;
use std::{
@@ -36,10 +35,6 @@ pub struct AnyAttribute {
type_id: TypeId,
html_len: usize,
value: Box<dyn DynAttr>,
/// Temporary attribute set during the resolving cycle, to resolve only once.
pub(crate) resolved: bool,
#[cfg(feature = "ssr")]
to_html: fn(
Box<dyn DynAttr>,
@@ -202,7 +197,6 @@ where
type_id: TypeId::of::<T::CloneableOwned>(),
html_len: value.html_len(),
value,
resolved: false,
#[cfg(feature = "ssr")]
to_html,
build,
@@ -309,13 +303,10 @@ impl Attribute for AnyAttribute {
);
}
async fn resolve(mut self) -> Self::AsyncOutput {
async fn resolve(self) -> Self::AsyncOutput {
#[cfg(feature = "ssr")]
{
let res = (self.resolve)(self.value).await;
// Used by batch_resolve_items_with_extra_attrs() for optimisations.
self.resolved = true;
res
(self.resolve)(self.value).await
}
#[cfg(not(feature = "ssr"))]
panic!(

View File

@@ -24,7 +24,7 @@ use web_sys::Element;
/// let view = element.on(ev::click, move |_| /* ... */);
///
/// // `element` now contains the actual element
/// let element = element.build(None);
/// let element = element.build();
/// let remove = element.on(ev::blur, move |_| /* ... */);
/// ```
pub trait ElementExt {

View File

@@ -3,17 +3,18 @@ use crate::hydration::set_currently_hydrating;
use crate::{
html::attribute::Attribute,
hydration::{failed_to_cast_element, Cursor},
prelude::IntoAttribute,
renderer::{CastFrom, Rndr},
ssr::StreamBuilder,
view::{
add_attr::AddAnyAttr, any_view::ExtraAttrsMut, IntoRender, Mountable,
Position, PositionState, Render, RenderHtml, ToTemplate,
add_attr::AddAnyAttr, IntoRender, Mountable, Position, PositionState,
Render, RenderHtml, ToTemplate,
},
};
use const_str_slice_concat::{
const_concat, const_concat_with_prefix, str_from_buffer,
};
use futures::future::join3;
use futures::future::join;
use next_tuple::NextTuple;
use std::ops::Deref;
@@ -22,8 +23,7 @@ mod element_ext;
mod elements;
mod inner_html;
use super::attribute::{
any_attribute::{AnyAttribute, AnyAttributeState},
escape_attr, NextAttribute,
any_attribute::AnyAttribute, escape_attr, NextAttribute,
};
pub use custom::*;
pub use element_ext::*;
@@ -186,42 +186,31 @@ where
{
type State = ElementState<At::State, Ch::State>;
fn rebuild(
self,
state: &mut Self::State,
extra_attrs: Option<Vec<AnyAttribute>>,
) {
fn rebuild(self, state: &mut Self::State) {
let ElementState {
attrs, children, ..
} = state;
self.attributes.rebuild(attrs);
if let (Some(extra_attrs), Some(extra_attr_states)) =
(extra_attrs, &mut state.extra_attrs)
{
extra_attrs.rebuild(extra_attr_states);
}
if let Some(children) = children {
self.children.rebuild(children, None);
self.children.rebuild(children);
}
}
fn build(self, extra_attrs: Option<Vec<AnyAttribute>>) -> Self::State {
fn build(self) -> Self::State {
let el = Rndr::create_element(self.tag.tag(), E::NAMESPACE);
let attrs = self.attributes.build(&el);
let extra_attrs = extra_attrs.map(|attrs| attrs.build(&el));
let children = if E::SELF_CLOSING {
None
} else {
let mut children = self.children.build(None);
let mut children = self.children.build();
children.mount(&el, None);
Some(children)
};
ElementState {
el,
children,
attrs,
extra_attrs,
children,
}
}
}
@@ -233,7 +222,6 @@ where
Ch: RenderHtml + Send,
{
type AsyncOutput = HtmlElement<E, At::AsyncOutput, Ch::AsyncOutput>;
type Owned = HtmlElement<E, At::CloneableOwned, Ch::Owned>;
const MIN_LENGTH: usize = if E::SELF_CLOSING {
3 // < ... />
@@ -248,22 +236,14 @@ where
+ E::TAG.len()
};
fn dry_resolve(&mut self, mut extra_attrs: ExtraAttrsMut<'_>) {
fn dry_resolve(&mut self) {
self.attributes.dry_resolve();
extra_attrs.iter_mut().for_each(Attribute::dry_resolve);
self.children.dry_resolve(ExtraAttrsMut::default());
self.children.dry_resolve();
}
async fn resolve(
self,
extra_attrs: ExtraAttrsMut<'_>,
) -> Self::AsyncOutput {
let (attributes, _, children) = join3(
self.attributes.resolve(),
ExtraAttrsMut::resolve(extra_attrs),
self.children.resolve(ExtraAttrsMut::default()),
)
.await;
async fn resolve(self) -> Self::AsyncOutput {
let (attributes, children) =
join(self.attributes.resolve(), self.children.resolve()).await;
HtmlElement {
#[cfg(any(debug_assertions, leptos_debuginfo))]
defined_at: self.defined_at,
@@ -273,7 +253,7 @@ where
}
}
fn html_len(&self, extra_attrs: Option<Vec<&AnyAttribute>>) -> usize {
fn html_len(&self) -> usize {
if E::SELF_CLOSING {
3 // < ... />
+ E::TAG.len()
@@ -282,10 +262,7 @@ where
2 // < ... >
+ E::TAG.len()
+ self.attributes.html_len()
+ extra_attrs.map(|attrs| {
attrs.into_iter().map(Attribute::html_len).sum::<usize>()
}).unwrap_or(0)
+ self.children.html_len(None)
+ self.children.html_len()
+ 3 // </ ... >
+ E::TAG.len()
}
@@ -297,13 +274,14 @@ where
position: &mut Position,
_escape: bool,
mark_branches: bool,
extra_attrs: Option<Vec<AnyAttribute>>,
extra_attributes: Vec<AnyAttribute>,
) {
// opening tag
buf.push('<');
buf.push_str(self.tag.tag());
let inner_html = attributes_to_html(self.attributes, extra_attrs, buf);
let inner_html =
attributes_to_html((self.attributes, extra_attributes), buf);
buf.push('>');
@@ -318,7 +296,7 @@ where
position,
E::ESCAPE_CHILDREN,
mark_branches,
None,
vec![],
);
}
@@ -336,7 +314,7 @@ where
position: &mut Position,
_escape: bool,
mark_branches: bool,
extra_attrs: Option<Vec<AnyAttribute>>,
extra_attributes: Vec<AnyAttribute>,
) where
Self: Sized,
{
@@ -346,7 +324,7 @@ where
buf.push_str(self.tag.tag());
let inner_html =
attributes_to_html(self.attributes, extra_attrs, &mut buf);
attributes_to_html((self.attributes, extra_attributes), &mut buf);
buf.push('>');
buffer.push_sync(&buf);
@@ -362,7 +340,7 @@ where
position,
E::ESCAPE_CHILDREN,
mark_branches,
None,
vec![],
);
}
@@ -380,7 +358,6 @@ where
self,
cursor: &Cursor,
position: &PositionState,
extra_attrs: Option<Vec<AnyAttribute>>,
) -> Self::State {
#[cfg(any(debug_assertions, leptos_debuginfo))]
{
@@ -405,15 +382,13 @@ where
});
let attrs = self.attributes.hydrate::<FROM_SERVER>(&el);
let extra_attrs = extra_attrs
.map(|attrs| Attribute::hydrate::<FROM_SERVER>(attrs, &el));
// hydrate children
let children = if !Ch::EXISTS || !E::ESCAPE_CHILDREN {
None
} else {
position.set(Position::FirstChild);
Some(self.children.hydrate::<FROM_SERVER>(cursor, position, None))
Some(self.children.hydrate::<FROM_SERVER>(cursor, position))
};
// go to next sibling
@@ -427,32 +402,19 @@ where
ElementState {
el,
children,
attrs,
extra_attrs,
}
}
fn into_owned(self) -> Self::Owned {
HtmlElement {
#[cfg(any(debug_assertions, leptos_debuginfo))]
defined_at: self.defined_at,
tag: self.tag,
attributes: self.attributes.into_cloneable_owned(),
children: self.children.into_owned(),
children,
}
}
}
/// Renders an [`Attribute`] (which can be one or more HTML attributes) into an HTML buffer.
pub fn attributes_to_html<At>(
attr: At,
extra_attrs: Option<Vec<AnyAttribute>>,
buf: &mut String,
) -> String
pub fn attributes_to_html<At>(attr: At, buf: &mut String) -> String
where
At: Attribute,
At: IntoAttribute,
{
let attr = attr.into_attr();
// `class` and `style` are created first, and pushed later
// this is because they can be filled by a mixture of values that include
// either the whole value (`class="..."` or `style="..."`) and individual
@@ -468,11 +430,6 @@ where
// inject regular attributes, and fill class and style
attr.to_html(buf, &mut class, &mut style, &mut inner_html);
if let Some(extra_attrs) = extra_attrs {
for attr in extra_attrs {
attr.to_html(buf, &mut class, &mut style, &mut inner_html);
}
}
if !class.is_empty() {
buf.push(' ');
@@ -493,10 +450,8 @@ where
/// The retained view state for an HTML element.
pub struct ElementState<At, Ch> {
pub(crate) el: crate::renderer::types::Element,
pub(crate) attrs: At,
pub(crate) children: Option<Ch>,
attrs: At,
extra_attrs: Option<Vec<AnyAttributeState>>,
}
impl<At, Ch> Deref for ElementState<At, Ch> {
@@ -531,6 +486,10 @@ impl<At, Ch> Mountable for ElementState<At, Ch> {
}
false
}
fn elements(&self) -> Vec<crate::renderer::types::Element> {
vec![self.el.clone()]
}
}
impl<E, At, Ch> ToTemplate for HtmlElement<E, At, Ch>
@@ -639,7 +598,7 @@ mod tests {
fn mock_dom_creates_element() {
let el: HtmlElement<Main, _, _, MockDom> =
main().child(p().id("test").lang("en").child("Hello, world!"));
let el = el.build(None);
let el = el.build();
assert_eq!(
el.el.to_debug_html(),
"<main><p id=\"test\" lang=\"en\">Hello, world!</p></main>"
@@ -653,7 +612,7 @@ mod tests {
em().child("beautiful"),
" world!",
)));
let el = el.build(None);
let el = el.build();
assert_eq!(
el.el.to_debug_html(),
"<main><p>Hello, <em>beautiful</em> world!</p></main>"

View File

@@ -3,9 +3,7 @@ use crate::{
hydration::Cursor,
prelude::{Render, RenderHtml},
ssr::StreamBuilder,
view::{
add_attr::AddAnyAttr, any_view::ExtraAttrsMut, Position, PositionState,
},
view::{add_attr::AddAnyAttr, Position, PositionState},
};
/// An island of interactivity in an otherwise-inert HTML document.
@@ -61,16 +59,12 @@ where
{
type State = View::State;
fn build(self, extra_attrs: Option<Vec<AnyAttribute>>) -> Self::State {
self.view.build(extra_attrs)
fn build(self) -> Self::State {
self.view.build()
}
fn rebuild(
self,
state: &mut Self::State,
extra_attrs: Option<Vec<AnyAttribute>>,
) {
self.view.rebuild(state, extra_attrs);
fn rebuild(self, state: &mut Self::State) {
self.view.rebuild(state);
}
}
@@ -106,7 +100,6 @@ where
View: RenderHtml,
{
type AsyncOutput = Island<View::AsyncOutput>;
type Owned = Island<View::Owned>;
const MIN_LENGTH: usize = ISLAND_TAG.len() * 2
+ "<>".len()
@@ -114,14 +107,11 @@ where
+ "data-component".len()
+ View::MIN_LENGTH;
fn dry_resolve(&mut self, extra_attrs: ExtraAttrsMut<'_>) {
self.view.dry_resolve(extra_attrs)
fn dry_resolve(&mut self) {
self.view.dry_resolve()
}
async fn resolve(
self,
extra_attrs: ExtraAttrsMut<'_>,
) -> Self::AsyncOutput {
async fn resolve(self) -> Self::AsyncOutput {
let Island {
component,
props_json,
@@ -130,7 +120,7 @@ where
Island {
component,
props_json,
view: view.resolve(extra_attrs).await,
view: view.resolve().await,
}
}
@@ -140,7 +130,7 @@ where
position: &mut Position,
escape: bool,
mark_branches: bool,
extra_attrs: Option<Vec<AnyAttribute>>,
extra_attrs: Vec<AnyAttribute>,
) {
Self::open_tag(self.component, &self.props_json, buf);
self.view.to_html_with_buf(
@@ -159,7 +149,7 @@ where
position: &mut Position,
escape: bool,
mark_branches: bool,
extra_attrs: Option<Vec<AnyAttribute>>,
extra_attrs: Vec<AnyAttribute>,
) where
Self: Sized,
{
@@ -187,7 +177,6 @@ where
self,
cursor: &Cursor,
position: &PositionState,
extra_attrs: Option<Vec<AnyAttribute>>,
) -> Self::State {
if position.get() == Position::FirstChild {
cursor.child();
@@ -196,16 +185,7 @@ where
}
position.set(Position::FirstChild);
self.view
.hydrate::<FROM_SERVER>(cursor, position, extra_attrs)
}
fn into_owned(self) -> Self::Owned {
Island {
component: self.component,
props_json: self.props_json,
view: self.view.into_owned(),
}
self.view.hydrate::<FROM_SERVER>(cursor, position)
}
}
@@ -255,14 +235,9 @@ where
{
type State = ();
fn build(self, _extra_attrs: Option<Vec<AnyAttribute>>) -> Self::State {}
fn build(self) -> Self::State {}
fn rebuild(
self,
_state: &mut Self::State,
_extra_attrs: Option<Vec<AnyAttribute>>,
) {
}
fn rebuild(self, _state: &mut Self::State) {}
}
impl<View> AddAnyAttr for IslandChildren<View>
@@ -292,24 +267,20 @@ where
View: RenderHtml,
{
type AsyncOutput = IslandChildren<View::AsyncOutput>;
type Owned = IslandChildren<View::Owned>;
const MIN_LENGTH: usize = ISLAND_CHILDREN_TAG.len() * 2
+ "<>".len()
+ "</>".len()
+ View::MIN_LENGTH;
fn dry_resolve(&mut self, extra_attrs: ExtraAttrsMut<'_>) {
self.view.dry_resolve(extra_attrs)
fn dry_resolve(&mut self) {
self.view.dry_resolve()
}
async fn resolve(
self,
extra_attrs: ExtraAttrsMut<'_>,
) -> Self::AsyncOutput {
async fn resolve(self) -> Self::AsyncOutput {
let IslandChildren { view, on_hydrate } = self;
IslandChildren {
view: view.resolve(extra_attrs).await,
view: view.resolve().await,
on_hydrate,
}
}
@@ -320,7 +291,7 @@ where
position: &mut Position,
escape: bool,
mark_branches: bool,
extra_attrs: Option<Vec<AnyAttribute>>,
extra_attrs: Vec<AnyAttribute>,
) {
Self::open_tag(buf);
self.view.to_html_with_buf(
@@ -339,7 +310,7 @@ where
position: &mut Position,
escape: bool,
mark_branches: bool,
extra_attrs: Option<Vec<AnyAttribute>>,
extra_attrs: Vec<AnyAttribute>,
) where
Self: Sized,
{
@@ -367,7 +338,6 @@ where
self,
cursor: &Cursor,
position: &PositionState,
_extra_attrs: Option<Vec<AnyAttribute>>,
) -> Self::State {
// island children aren't hydrated
// we update the walk to pass over them
@@ -402,11 +372,4 @@ where
);
}
}
fn into_owned(self) -> Self::Owned {
IslandChildren {
view: self.view.into_owned(),
on_hydrate: self.on_hydrate,
}
}
}

View File

@@ -7,9 +7,7 @@ use crate::{
dom::{Element, Node},
CastFrom, Rndr,
},
view::{
any_view::ExtraAttrsMut, Position, PositionState, Render, RenderHtml,
},
view::{Position, PositionState, Render, RenderHtml},
};
use attribute::any_attribute::AnyAttribute;
use std::borrow::Cow;
@@ -46,30 +44,21 @@ pub fn doctype(value: &'static str) -> Doctype {
impl Render for Doctype {
type State = ();
fn build(self, _extra_attrs: Option<Vec<AnyAttribute>>) -> Self::State {}
fn build(self) -> Self::State {}
fn rebuild(
self,
_state: &mut Self::State,
_extra_attrs: Option<Vec<AnyAttribute>>,
) {
}
fn rebuild(self, _state: &mut Self::State) {}
}
no_attrs!(Doctype);
impl RenderHtml for Doctype {
type AsyncOutput = Self;
type Owned = Self;
const MIN_LENGTH: usize = "<!DOCTYPE html>".len();
fn dry_resolve(&mut self, _extra_attrs: ExtraAttrsMut<'_>) {}
fn dry_resolve(&mut self) {}
async fn resolve(
self,
_extra_attrs: ExtraAttrsMut<'_>,
) -> Self::AsyncOutput {
async fn resolve(self) -> Self::AsyncOutput {
self
}
@@ -79,7 +68,7 @@ impl RenderHtml for Doctype {
_position: &mut Position,
_escape: bool,
_mark_branches: bool,
_extra_attrs: Option<Vec<AnyAttribute>>,
_extra_attrs: Vec<AnyAttribute>,
) {
buf.push_str("<!DOCTYPE ");
buf.push_str(self.value);
@@ -90,13 +79,8 @@ impl RenderHtml for Doctype {
self,
_cursor: &Cursor,
_position: &PositionState,
_extra_attrs: Option<Vec<AnyAttribute>>,
) -> Self::State {
}
fn into_owned(self) -> Self::Owned {
self
}
}
/// An element that contains no interactivity, and whose contents can be known at compile time.
@@ -126,21 +110,21 @@ impl Mountable for InertElementState {
fn insert_before_this(&self, child: &mut dyn Mountable) -> bool {
self.1.insert_before_this(child)
}
fn elements(&self) -> Vec<crate::renderer::types::Element> {
vec![self.1.clone()]
}
}
impl Render for InertElement {
type State = InertElementState;
fn build(self, _extra_attrs: Option<Vec<AnyAttribute>>) -> Self::State {
fn build(self) -> Self::State {
let el = Rndr::create_element_from_html(&self.html);
InertElementState(self.html, el)
}
fn rebuild(
self,
state: &mut Self::State,
_extra_attrs: Option<Vec<AnyAttribute>>,
) {
fn rebuild(self, state: &mut Self::State) {
let InertElementState(prev, el) = state;
if &self.html != prev {
let mut new_el = Rndr::create_element_from_html(&self.html);
@@ -171,17 +155,16 @@ impl AddAnyAttr for InertElement {
impl RenderHtml for InertElement {
type AsyncOutput = Self;
type Owned = Self;
const MIN_LENGTH: usize = 0;
fn html_len(&self, _extra_attrs: Option<Vec<&AnyAttribute>>) -> usize {
fn html_len(&self) -> usize {
self.html.len()
}
fn dry_resolve(&mut self, _extra_attrs: ExtraAttrsMut<'_>) {}
fn dry_resolve(&mut self) {}
async fn resolve(self, _extra_attrs: ExtraAttrsMut<'_>) -> Self {
async fn resolve(self) -> Self {
self
}
@@ -191,7 +174,7 @@ impl RenderHtml for InertElement {
position: &mut Position,
_escape: bool,
_mark_branches: bool,
_extra_attrs: Option<Vec<AnyAttribute>>,
_extra_attrs: Vec<AnyAttribute>,
) {
buf.push_str(&self.html);
*position = Position::NextChild;
@@ -201,7 +184,6 @@ impl RenderHtml for InertElement {
self,
cursor: &Cursor,
position: &PositionState,
_extra_attrs: Option<Vec<AnyAttribute>>,
) -> Self::State {
let curr_position = position.get();
if curr_position == Position::FirstChild {
@@ -214,8 +196,4 @@ impl RenderHtml for InertElement {
position.set(Position::NextChild);
InertElementState(self.html, el)
}
fn into_owned(self) -> Self::Owned {
self
}
}

View File

@@ -8,10 +8,7 @@ use crate::{
no_attrs,
prelude::{Mountable, Render, RenderHtml},
renderer::Rndr,
view::{
any_view::ExtraAttrsMut, strings::StrState, Position, PositionState,
ToTemplate,
},
view::{strings::StrState, Position, PositionState, ToTemplate},
};
use oco_ref::Oco;
@@ -24,16 +21,12 @@ pub struct OcoStrState {
impl Render for Oco<'static, str> {
type State = OcoStrState;
fn build(self, _extra_attrs: Option<Vec<AnyAttribute>>) -> Self::State {
fn build(self) -> Self::State {
let node = Rndr::create_text_node(&self);
OcoStrState { node, str: self }
}
fn rebuild(
self,
state: &mut Self::State,
_extra_attrs: Option<Vec<AnyAttribute>>,
) {
fn rebuild(self, state: &mut Self::State) {
let OcoStrState { node, str } = state;
if &self != str {
Rndr::set_text(node, &self);
@@ -46,16 +39,12 @@ no_attrs!(Oco<'static, str>);
impl RenderHtml for Oco<'static, str> {
type AsyncOutput = Self;
type Owned = Self;
const MIN_LENGTH: usize = 0;
fn dry_resolve(&mut self, _extra_attrs: ExtraAttrsMut<'_>) {}
fn dry_resolve(&mut self) {}
async fn resolve(
self,
_extra_attrs: ExtraAttrsMut<'_>,
) -> Self::AsyncOutput {
async fn resolve(self) -> Self::AsyncOutput {
self
}
@@ -65,7 +54,7 @@ impl RenderHtml for Oco<'static, str> {
position: &mut Position,
escape: bool,
mark_branches: bool,
extra_attrs: Option<Vec<AnyAttribute>>,
extra_attrs: Vec<AnyAttribute>,
) {
<&str as RenderHtml>::to_html_with_buf(
&self,
@@ -81,21 +70,13 @@ impl RenderHtml for Oco<'static, str> {
self,
cursor: &Cursor,
position: &PositionState,
extra_attrs: Option<Vec<AnyAttribute>>,
) -> Self::State {
let this: &str = self.as_ref();
let StrState { node, .. } = <&str as RenderHtml>::hydrate::<FROM_SERVER>(
this,
cursor,
position,
extra_attrs,
this, cursor, position,
);
OcoStrState { node, str: self }
}
fn into_owned(self) -> <Self as RenderHtml>::Owned {
self
}
}
impl ToTemplate for Oco<'static, str> {
@@ -130,6 +111,10 @@ impl Mountable for OcoStrState {
fn insert_before_this(&self, child: &mut dyn Mountable) -> bool {
self.node.insert_before_this(child)
}
fn elements(&self) -> Vec<crate::renderer::types::Element> {
vec![]
}
}
impl AttributeValue for Oco<'static, str> {

View File

@@ -352,7 +352,7 @@ where
})
}
fn rebuild(self, state: &mut Self::State, _extra_attrs: Option<Vec<AnyAttribute>>) {
fn rebuild(self, state: &mut Self::State) {
let (names, mut f) = self;
let prev_value = state.take_value();
@@ -433,7 +433,7 @@ where
<String as IntoClass>::build(self.deref().to_owned(), el)
}
fn rebuild(self, state: &mut Self::State, _extra_attrs: Option<Vec<AnyAttribute>>) {
fn rebuild(self, state: &mut Self::State) {
<String as IntoClass>::rebuild(self.deref().to_owned(), state)
}
@@ -447,7 +447,7 @@ where
fn dry_resolve(&mut self) {}
async fn resolve(self, _extra_attrs: Option<Vec<AnyAttribute>>) -> Self::AsyncOutput {
async fn resolve(self) -> Self::AsyncOutput {
self
}
}
@@ -489,7 +489,7 @@ where
)
}
fn rebuild(self, state: &mut Self::State, _extra_attrs: Option<Vec<AnyAttribute>>) {
fn rebuild(self, state: &mut Self::State) {
<(&'static str, bool) as IntoClass>::rebuild(
(self.0, *self.1.deref()),
state,
@@ -506,7 +506,7 @@ where
fn dry_resolve(&mut self) {}
async fn resolve(self, _extra_attrs: Option<Vec<AnyAttribute>>) -> Self::AsyncOutput {
async fn resolve(self) -> Self::AsyncOutput {
self
}
}
@@ -901,7 +901,7 @@ where
state
}
fn rebuild(self, state: &mut Self::State, _extra_attrs: Option<Vec<AnyAttribute>>) {
fn rebuild(self, state: &mut Self::State) {
reactive_graph::spawn_local_scoped({
let state = Rc::clone(state);
async move {
@@ -925,7 +925,7 @@ where
fn dry_resolve(&mut self) {}
async fn resolve(self, _extra_attrs: Option<Vec<AnyAttribute>>) -> Self::AsyncOutput {
async fn resolve(self) -> Self::AsyncOutput {
self.inner.await
}
}

View File

@@ -4,8 +4,8 @@ use crate::{
renderer::Rndr,
ssr::StreamBuilder,
view::{
add_attr::AddAnyAttr, any_view::ExtraAttrsMut, Mountable, Position,
PositionState, Render, RenderHtml, ToTemplate,
add_attr::AddAnyAttr, Mountable, Position, PositionState, Render,
RenderHtml, ToTemplate,
},
};
use reactive_graph::effect::RenderEffect;
@@ -57,7 +57,7 @@ where
type State = RenderEffectState<V::State>;
#[track_caller]
fn build(mut self, extra_attrs: Option<Vec<AnyAttribute>>) -> Self::State {
fn build(mut self) -> Self::State {
let hook = throw_error::get_error_hook();
RenderEffect::new(move |prev| {
let _guard = hook
@@ -65,22 +65,18 @@ where
.map(|h| throw_error::set_error_hook(Arc::clone(h)));
let value = self.invoke();
if let Some(mut state) = prev {
value.rebuild(&mut state, extra_attrs.clone());
value.rebuild(&mut state);
state
} else {
value.build(extra_attrs.clone())
value.build()
}
})
.into()
}
#[track_caller]
fn rebuild(
self,
state: &mut Self::State,
extra_attrs: Option<Vec<AnyAttribute>>,
) {
let new = self.build(extra_attrs);
fn rebuild(self, state: &mut Self::State) {
let new = self.build();
let mut old = std::mem::replace(state, new);
old.insert_before_this(state);
old.unmount();
@@ -123,6 +119,13 @@ where
false
}
}
fn elements(&self) -> Vec<crate::renderer::types::Element> {
self.0
.as_ref()
.map(|inner| inner.elements())
.unwrap_or_default()
}
}
impl<F, V> RenderHtml for F
@@ -132,22 +135,18 @@ where
V::State: 'static,
{
type AsyncOutput = V::AsyncOutput;
type Owned = F;
const MIN_LENGTH: usize = 0;
fn dry_resolve(&mut self, extra_attrs: ExtraAttrsMut<'_>) {
self.invoke().dry_resolve(extra_attrs);
fn dry_resolve(&mut self) {
self.invoke().dry_resolve();
}
async fn resolve(
mut self,
extra_attrs: ExtraAttrsMut<'_>,
) -> Self::AsyncOutput {
self.invoke().resolve(extra_attrs).await
async fn resolve(mut self) -> Self::AsyncOutput {
self.invoke().resolve().await
}
fn html_len(&self, _extra_attrs: Option<Vec<&AnyAttribute>>) -> usize {
fn html_len(&self) -> usize {
V::MIN_LENGTH
}
@@ -157,7 +156,7 @@ where
position: &mut Position,
escape: bool,
mark_branches: bool,
extra_attrs: Option<Vec<AnyAttribute>>,
extra_attrs: Vec<AnyAttribute>,
) {
let value = self.invoke();
value.to_html_with_buf(
@@ -175,7 +174,7 @@ where
position: &mut Position,
escape: bool,
mark_branches: bool,
extra_attrs: Option<Vec<AnyAttribute>>,
extra_attrs: Vec<AnyAttribute>,
) where
Self: Sized,
{
@@ -193,7 +192,6 @@ where
mut self,
cursor: &Cursor,
position: &PositionState,
extra_attrs: Option<Vec<AnyAttribute>>,
) -> Self::State {
let cursor = cursor.clone();
let position = position.clone();
@@ -204,22 +202,14 @@ where
.map(|h| throw_error::set_error_hook(Arc::clone(h)));
let value = self.invoke();
if let Some(mut state) = prev {
value.rebuild(&mut state, extra_attrs.clone());
value.rebuild(&mut state);
state
} else {
value.hydrate::<FROM_SERVER>(
&cursor,
&position,
extra_attrs.clone(),
)
value.hydrate::<FROM_SERVER>(&cursor, &position)
}
})
.into()
}
fn into_owned(self) -> Self::Owned {
self
}
}
impl<F, V> AddAnyAttr for F
@@ -264,6 +254,11 @@ where
self.with_value_mut(|value| value.insert_before_this(child))
.unwrap_or(false)
}
fn elements(&self) -> Vec<crate::renderer::types::Element> {
self.with_value_mut(|inner| inner.elements())
.unwrap_or_default()
}
}
impl<M, E> Mountable for Result<M, E>
@@ -293,6 +288,12 @@ where
false
}
}
fn elements(&self) -> Vec<crate::renderer::types::Element> {
self.as_ref()
.map(|inner| inner.elements())
.unwrap_or_default()
}
}
// Dynamic attributes
@@ -539,8 +540,8 @@ mod stable {
hydration::Cursor,
ssr::StreamBuilder,
view::{
add_attr::AddAnyAttr, any_view::ExtraAttrsMut, Mountable, Position,
PositionState, Render, RenderHtml,
add_attr::AddAnyAttr, Mountable, Position, PositionState, Render,
RenderHtml,
},
};
#[allow(deprecated)]
@@ -565,20 +566,13 @@ mod stable {
type State = RenderEffectState<V::State>;
#[track_caller]
fn build(
self,
extra_attrs: Option<Vec<AnyAttribute>>,
) -> Self::State {
(move || self.get()).build(extra_attrs)
fn build(self) -> Self::State {
(move || self.get()).build()
}
#[track_caller]
fn rebuild(
self,
state: &mut Self::State,
extra_attrs: Option<Vec<AnyAttribute>>,
) {
let new = self.build(extra_attrs);
fn rebuild(self, state: &mut Self::State) {
let new = self.build();
let mut old = std::mem::replace(state, new);
old.insert_before_this(state);
old.unmount();
@@ -611,27 +605,20 @@ mod stable {
V::State: 'static,
{
type AsyncOutput = Self;
type Owned = Self;
const MIN_LENGTH: usize = 0;
fn dry_resolve(&mut self, _extra_attrs: ExtraAttrsMut<'_>) {
fn dry_resolve(&mut self) {
if $dry_resolve {
_ = self.get();
}
}
async fn resolve(
self,
_extra_attrs: ExtraAttrsMut<'_>,
) -> Self::AsyncOutput {
async fn resolve(self) -> Self::AsyncOutput {
self
}
fn html_len(
&self,
_extra_attrs: Option<Vec<&AnyAttribute>>,
) -> usize {
fn html_len(&self) -> usize {
V::MIN_LENGTH
}
@@ -641,7 +628,7 @@ mod stable {
position: &mut Position,
escape: bool,
mark_branches: bool,
extra_attrs: Option<Vec<AnyAttribute>>,
extra_attrs: Vec<AnyAttribute>,
) {
let value = self.get();
value.to_html_with_buf(
@@ -659,7 +646,7 @@ mod stable {
position: &mut Position,
escape: bool,
mark_branches: bool,
extra_attrs: Option<Vec<AnyAttribute>>,
extra_attrs: Vec<AnyAttribute>,
) where
Self: Sized,
{
@@ -677,17 +664,9 @@ mod stable {
self,
cursor: &Cursor,
position: &PositionState,
extra_attrs: Option<Vec<AnyAttribute>>,
) -> Self::State {
(move || self.get()).hydrate::<FROM_SERVER>(
cursor,
position,
extra_attrs,
)
}
fn into_owned(self) -> Self::Owned {
self
(move || self.get())
.hydrate::<FROM_SERVER>(cursor, position)
}
}
@@ -764,20 +743,13 @@ mod stable {
type State = RenderEffectState<V::State>;
#[track_caller]
fn build(
self,
extra_attrs: Option<Vec<AnyAttribute>>,
) -> Self::State {
(move || self.get()).build(extra_attrs)
fn build(self) -> Self::State {
(move || self.get()).build()
}
#[track_caller]
fn rebuild(
self,
state: &mut Self::State,
extra_attrs: Option<Vec<AnyAttribute>>,
) {
let new = self.build(extra_attrs);
fn rebuild(self, state: &mut Self::State) {
let new = self.build();
let mut old = std::mem::replace(state, new);
old.insert_before_this(state);
old.unmount();
@@ -816,27 +788,20 @@ mod stable {
V::State: 'static,
{
type AsyncOutput = Self;
type Owned = Self;
const MIN_LENGTH: usize = 0;
fn dry_resolve(&mut self, _extra_attrs: ExtraAttrsMut<'_>) {
fn dry_resolve(&mut self) {
if $dry_resolve {
_ = self.get();
}
}
async fn resolve(
self,
_extra_attrs: ExtraAttrsMut<'_>,
) -> Self::AsyncOutput {
async fn resolve(self) -> Self::AsyncOutput {
self
}
fn html_len(
&self,
_extra_attrs: Option<Vec<&AnyAttribute>>,
) -> usize {
fn html_len(&self) -> usize {
V::MIN_LENGTH
}
@@ -846,7 +811,7 @@ mod stable {
position: &mut Position,
escape: bool,
mark_branches: bool,
extra_attrs: Option<Vec<AnyAttribute>>,
extra_attrs: Vec<AnyAttribute>,
) {
let value = self.get();
value.to_html_with_buf(
@@ -864,7 +829,7 @@ mod stable {
position: &mut Position,
escape: bool,
mark_branches: bool,
extra_attrs: Option<Vec<AnyAttribute>>,
extra_attrs: Vec<AnyAttribute>,
) where
Self: Sized,
{
@@ -882,17 +847,9 @@ mod stable {
self,
cursor: &Cursor,
position: &PositionState,
extra_attrs: Option<Vec<AnyAttribute>>,
) -> Self::State {
(move || self.get()).hydrate::<FROM_SERVER>(
cursor,
position,
extra_attrs,
)
}
fn into_owned(self) -> Self::Owned {
self
(move || self.get())
.hydrate::<FROM_SERVER>(cursor, position)
}
}
@@ -985,7 +942,7 @@ mod tests {
let count = RwSignal::new(0);
let app: HtmlElement<_, _, _, MockDom> =
button((), move || count.get().to_string());
let el = app.build(None);
let el = app.build();
assert_eq!(el.el.to_debug_html(), "<button>0</button>");
rt.dispose();
}
@@ -996,7 +953,7 @@ mod tests {
let count = RwSignal::new(0);
let app: HtmlElement<_, _, _, MockDom> =
button((), move || count.get().to_string());
let el = app.build(None);
let el = app.build();
assert_eq!(el.el.to_debug_html(), "<button>0</button>");
count.set(1);
assert_eq!(el.el.to_debug_html(), "<button>1</button>");
@@ -1014,7 +971,7 @@ mod tests {
("Hello, my ", move || count.get().to_string(), " friends."),
),
);
let el = app.build(None);
let el = app.build();
assert_eq!(
el.el.to_debug_html(),
"<main><button>Hello, my 0 friends.</button></main>"

View File

@@ -3,10 +3,7 @@ use crate::{
hydration::Cursor,
prelude::Mountable,
ssr::StreamBuilder,
view::{
add_attr::AddAnyAttr, any_view::ExtraAttrsMut, Position, PositionState,
Render, RenderHtml,
},
view::{add_attr::AddAnyAttr, Position, PositionState, Render, RenderHtml},
};
use reactive_graph::{computed::ScopedFuture, owner::Owner};
@@ -56,18 +53,14 @@ where
{
type State = OwnedViewState<T::State>;
fn build(self, extra_attrs: Option<Vec<AnyAttribute>>) -> Self::State {
let state = self.owner.with(|| self.view.build(extra_attrs));
fn build(self) -> Self::State {
let state = self.owner.with(|| self.view.build());
OwnedViewState::new(state, self.owner)
}
fn rebuild(
self,
state: &mut Self::State,
extra_attrs: Option<Vec<AnyAttribute>>,
) {
fn rebuild(self, state: &mut Self::State) {
let OwnedView { owner, view, .. } = self;
owner.with(|| view.rebuild(&mut state.state, extra_attrs));
owner.with(|| view.rebuild(&mut state.state));
state.owner = owner;
}
}
@@ -99,7 +92,6 @@ where
{
// TODO
type AsyncOutput = OwnedView<T::AsyncOutput>;
type Owned = OwnedView<T::Owned>;
const MIN_LENGTH: usize = T::MIN_LENGTH;
@@ -109,7 +101,7 @@ where
position: &mut Position,
escape: bool,
mark_branches: bool,
extra_attrs: Option<Vec<AnyAttribute>>,
extra_attrs: Vec<AnyAttribute>,
) {
self.owner.with(|| {
self.view.to_html_with_buf(
@@ -128,7 +120,7 @@ where
position: &mut Position,
escape: bool,
mark_branches: bool,
extra_attrs: Option<Vec<AnyAttribute>>,
extra_attrs: Vec<AnyAttribute>,
) where
Self: Sized,
{
@@ -153,39 +145,23 @@ where
self,
cursor: &Cursor,
position: &PositionState,
extra_attrs: Option<Vec<AnyAttribute>>,
) -> Self::State {
let state = self.owner.with(|| {
self.view
.hydrate::<FROM_SERVER>(cursor, position, extra_attrs)
});
let state = self
.owner
.with(|| self.view.hydrate::<FROM_SERVER>(cursor, position));
OwnedViewState::new(state, self.owner)
}
async fn resolve(
self,
extra_attrs: ExtraAttrsMut<'_>,
) -> Self::AsyncOutput {
async fn resolve(self) -> Self::AsyncOutput {
let OwnedView { owner, view } = self;
let view = owner
.with(|| {
ScopedFuture::new(
async move { view.resolve(extra_attrs).await },
)
})
.with(|| ScopedFuture::new(async move { view.resolve().await }))
.await;
OwnedView { owner, view }
}
fn dry_resolve(&mut self, extra_attrs: ExtraAttrsMut<'_>) {
self.owner.with(|| self.view.dry_resolve(extra_attrs));
}
fn into_owned(self) -> Self::Owned {
OwnedView {
owner: self.owner,
view: self.view.into_owned(),
}
fn dry_resolve(&mut self) {
self.owner.with(|| self.view.dry_resolve());
}
}
@@ -208,4 +184,8 @@ where
fn insert_before_this(&self, child: &mut dyn Mountable) -> bool {
self.state.insert_before_this(child)
}
fn elements(&self) -> Vec<crate::renderer::types::Element> {
self.state.elements()
}
}

View File

@@ -3,8 +3,8 @@ use crate::{
hydration::Cursor,
ssr::StreamBuilder,
view::{
add_attr::AddAnyAttr, any_view::ExtraAttrsMut, iterators::OptionState,
Mountable, Position, PositionState, Render, RenderHtml,
add_attr::AddAnyAttr, iterators::OptionState, Mountable, Position,
PositionState, Render, RenderHtml,
},
};
use any_spawner::Executor;
@@ -161,6 +161,10 @@ where
fn insert_before_this(&self, child: &mut dyn Mountable) -> bool {
self.inner.borrow_mut().insert_before_this(child)
}
fn elements(&self) -> Vec<crate::renderer::types::Element> {
self.inner.borrow().elements()
}
}
impl<T> Render for Suspend<T>
@@ -169,7 +173,7 @@ where
{
type State = SuspendState<T>;
fn build(self, extra_attrs: Option<Vec<AnyAttribute>>) -> Self::State {
fn build(self) -> Self::State {
let Self { subscriber, inner } = self;
// create a Future that will be aborted on on_cleanup
@@ -184,7 +188,7 @@ where
// otherwise, start with the fallback
let initial = fut.as_mut().now_or_never().and_then(Result::ok);
let initially_pending = initial.is_none();
let inner = Rc::new(RefCell::new(initial.build(extra_attrs.clone())));
let inner = Rc::new(RefCell::new(initial.build()));
// get a unique ID if there's a SuspenseContext
let id = use_context::<SuspenseContext>().map(|sc| sc.task_id());
@@ -205,8 +209,7 @@ where
drop(id);
if let Ok(value) = value {
Some(value)
.rebuild(&mut *state.borrow_mut(), extra_attrs);
Some(value).rebuild(&mut *state.borrow_mut());
}
subscriber.forward();
@@ -219,11 +222,7 @@ where
SuspendState { inner }
}
fn rebuild(
self,
state: &mut Self::State,
extra_attrs: Option<Vec<AnyAttribute>>,
) {
fn rebuild(self, state: &mut Self::State) {
let Self { subscriber, inner } = self;
// create a Future that will be aborted on on_cleanup
@@ -253,7 +252,7 @@ where
// has no parent
Executor::tick().await;
if let Ok(value) = value {
Some(value).rebuild(&mut *state.borrow_mut(), extra_attrs);
Some(value).rebuild(&mut *state.borrow_mut());
}
subscriber.forward();
@@ -289,7 +288,6 @@ where
T: RenderHtml + Sized + 'static,
{
type AsyncOutput = Option<T>;
type Owned = Self;
const MIN_LENGTH: usize = T::MIN_LENGTH;
@@ -299,7 +297,7 @@ where
position: &mut Position,
escape: bool,
mark_branches: bool,
extra_attrs: Option<Vec<AnyAttribute>>,
extra_attrs: Vec<AnyAttribute>,
) {
// TODO wrap this with a Suspense as needed
// currently this is just used for Routes, which creates a Suspend but never actually needs
@@ -321,7 +319,7 @@ where
position: &mut Position,
escape: bool,
mark_branches: bool,
extra_attrs: Option<Vec<AnyAttribute>>,
extra_attrs: Vec<AnyAttribute>,
) where
Self: Sized,
{
@@ -399,7 +397,6 @@ where
self,
cursor: &Cursor,
position: &PositionState,
extra_attrs: Option<Vec<AnyAttribute>>,
) -> Self::State {
let Self { subscriber, inner } = self;
@@ -415,11 +412,9 @@ where
// otherwise, start with the fallback
let initial = fut.as_mut().now_or_never().and_then(Result::ok);
let initially_pending = initial.is_none();
let inner = Rc::new(RefCell::new(initial.hydrate::<FROM_SERVER>(
cursor,
position,
extra_attrs.clone(),
)));
let inner = Rc::new(RefCell::new(
initial.hydrate::<FROM_SERVER>(cursor, position),
));
// get a unique ID if there's a SuspenseContext
let id = use_context::<SuspenseContext>().map(|sc| sc.task_id());
@@ -440,8 +435,7 @@ where
drop(id);
if let Ok(value) = value {
Some(value)
.rebuild(&mut *state.borrow_mut(), extra_attrs);
Some(value).rebuild(&mut *state.borrow_mut());
}
subscriber.forward();
@@ -454,14 +448,11 @@ where
SuspendState { inner }
}
async fn resolve(
self,
_extra_attrs: ExtraAttrsMut<'_>,
) -> Self::AsyncOutput {
async fn resolve(self) -> Self::AsyncOutput {
Some(self.inner.await)
}
fn dry_resolve(&mut self, _extra_attrs: ExtraAttrsMut<'_>) {
fn dry_resolve(&mut self) {
// this is a little crazy, but if a Suspend is immediately available, we end up
// with a situation where polling it multiple times (here in dry_resolve and then in
// resolve) causes a runtime panic
@@ -481,8 +472,4 @@ where
as Pin<Box<dyn Future<Output = T> + Send>>;
}
}
fn into_owned(self) -> Self::Owned {
self
}
}

View File

@@ -466,6 +466,10 @@ impl Mountable for Node {
}
false
}
fn elements(&self) -> Vec<crate::renderer::types::Element> {
vec![]
}
}
impl Mountable for Text {
@@ -486,6 +490,10 @@ impl Mountable for Text {
}
false
}
fn elements(&self) -> Vec<crate::renderer::types::Element> {
vec![]
}
}
impl Mountable for Comment {
@@ -506,6 +514,10 @@ impl Mountable for Comment {
}
false
}
fn elements(&self) -> Vec<crate::renderer::types::Element> {
vec![]
}
}
impl Mountable for Element {
@@ -526,6 +538,10 @@ impl Mountable for Element {
}
false
}
fn elements(&self) -> Vec<crate::renderer::types::Element> {
vec![self.clone()]
}
}
impl CastFrom<Node> for Text {

View File

@@ -106,7 +106,7 @@ impl StreamBuilder {
fallback: View,
position: &mut Position,
mark_branches: bool,
extra_attrs: Option<Vec<AnyAttribute>>,
extra_attrs: Vec<AnyAttribute>,
) where
View: RenderHtml,
{
@@ -165,7 +165,7 @@ impl StreamBuilder {
view: impl Future<Output = Option<View>> + Send + 'static,
position: &mut Position,
mark_branches: bool,
extra_attrs: Option<Vec<AnyAttribute>>,
extra_attrs: Vec<AnyAttribute>,
) where
View: RenderHtml,
{
@@ -185,7 +185,7 @@ impl StreamBuilder {
position: &mut Position,
mark_branches: bool,
nonce: Option<Arc<str>>,
extra_attrs: Option<Vec<AnyAttribute>>,
extra_attrs: Vec<AnyAttribute>,
) where
View: RenderHtml,
{

View File

@@ -1,5 +1,3 @@
#![allow(unused_mut)]
#![allow(clippy::type_complexity)]
#[cfg(feature = "ssr")]
use super::MarkBranch;
use super::{
@@ -8,12 +6,13 @@ use super::{
};
use crate::{
html::attribute::{
any_attribute::{AnyAttribute, IntoAnyAttribute},
any_attribute::{AnyAttribute, AnyAttributeState, IntoAnyAttribute},
Attribute,
},
hydration::Cursor,
ssr::StreamBuilder,
};
use futures::future::{join, join_all};
use std::{
any::{Any, TypeId},
fmt::Debug,
@@ -34,15 +33,14 @@ use std::{future::Future, pin::Pin};
pub struct AnyView {
type_id: TypeId,
value: Box<dyn Any + Send>,
extra_attrs: Vec<AnyAttribute>,
build: fn(Box<dyn Any>, Option<Vec<AnyAttribute>>) -> AnyViewState,
rebuild: fn(Box<dyn Any>, &mut AnyViewState, Option<Vec<AnyAttribute>>),
build: fn(Box<dyn Any>) -> AnyViewState,
rebuild: fn(TypeId, Box<dyn Any>, &mut AnyViewState),
// 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.
// With this type-erased wrapper, however, the compiler is not *always* able to correctly
// eliminate that code.
#[cfg(feature = "ssr")]
html_len: fn(&Box<dyn Any + Send>, Option<Vec<&AnyAttribute>>) -> usize,
html_len: usize,
#[cfg(feature = "ssr")]
to_html: fn(
Box<dyn Any>,
@@ -50,7 +48,7 @@ pub struct AnyView {
&mut Position,
bool,
bool,
Option<Vec<AnyAttribute>>,
Vec<AnyAttribute>,
),
#[cfg(feature = "ssr")]
to_html_async: fn(
@@ -59,7 +57,7 @@ pub struct AnyView {
&mut Position,
bool,
bool,
Option<Vec<AnyAttribute>>,
Vec<AnyAttribute>,
),
#[cfg(feature = "ssr")]
to_html_async_ooo: fn(
@@ -68,26 +66,18 @@ pub struct AnyView {
&mut Position,
bool,
bool,
Option<Vec<AnyAttribute>>,
Vec<AnyAttribute>,
),
#[cfg(feature = "ssr")]
#[allow(clippy::type_complexity)]
resolve: for<'a> fn(
Box<dyn Any>,
ExtraAttrsMut<'a>,
)
-> Pin<Box<dyn Future<Output = AnyView> + Send + 'a>>,
resolve: fn(Box<dyn Any>) -> Pin<Box<dyn Future<Output = AnyView> + Send>>,
#[cfg(feature = "ssr")]
dry_resolve: fn(&mut Box<dyn Any + Send>, ExtraAttrsMut<'_>),
dry_resolve: fn(&mut Box<dyn Any + Send>),
#[cfg(feature = "hydrate")]
#[cfg(feature = "hydrate")]
#[allow(clippy::type_complexity)]
hydrate_from_server: fn(
Box<dyn Any>,
&Cursor,
&PositionState,
Option<Vec<AnyAttribute>>,
) -> AnyViewState,
hydrate_from_server:
fn(Box<dyn Any>, &Cursor, &PositionState) -> AnyViewState,
}
/// Retained view state for [`AnyView`].
@@ -101,6 +91,7 @@ pub struct AnyViewState {
marker: Option<&crate::renderer::types::Node>,
),
insert_before_this: fn(&dyn Any, child: &mut dyn Mountable) -> bool,
elements: fn(&dyn Any) -> Vec<crate::renderer::types::Element>,
}
impl Debug for AnyViewState {
@@ -157,51 +148,49 @@ where
state.insert_before_this(child)
}
#[cfg(feature = "ssr")]
fn resolve<'a, T>(
value: Box<dyn Any>,
extra_attrs: ExtraAttrsMut<'a>,
) -> Pin<Box<dyn Future<Output = AnyView> + Send + 'a>>
fn elements<T>(state: &dyn Any) -> Vec<crate::renderer::types::Element>
where
T: RenderHtml + 'static,
T: Render,
T::State: 'static,
{
let value = value
.downcast::<T>()
.expect("AnyView::resolve could not be downcast");
Box::pin(async move { value.resolve(extra_attrs).await.into_any() })
let state = state
.downcast_ref::<T::State>()
.expect("AnyViewState::insert_before_this couldn't downcast state");
state.elements()
}
impl<T> IntoAny for T
where
T: RenderHtml,
T::Owned: Send,
T: Send,
T: RenderHtml + 'static,
T::State: 'static,
{
fn into_any(self) -> AnyView {
let value = Box::new(self.into_owned()) as Box<dyn Any + Send>;
#[cfg(feature = "ssr")]
let html_len = self.html_len();
let value = Box::new(self) as Box<dyn Any + Send>;
match value.downcast::<AnyView>() {
// if it's already an AnyView, we don't need to double-wrap it
Ok(any_view) => *any_view,
Err(value) => {
#[cfg(feature = "ssr")]
let html_len =
|value: &Box<dyn Any + Send>, extra_attrs: Option<Vec<&AnyAttribute>>| {
let value = value
.downcast_ref::<T::Owned>()
.expect("AnyView::html_len could not be downcast");
value.html_len(extra_attrs)
};
let dry_resolve = |value: &mut Box<dyn Any + Send>| {
let value = value
.downcast_mut::<T>()
.expect("AnyView::resolve could not be downcast");
value.dry_resolve();
};
#[cfg(feature = "ssr")]
let dry_resolve =
|value: &mut Box<dyn Any + Send>,
extra_attrs: ExtraAttrsMut<'_>| {
let value = value
.downcast_mut::<T::Owned>()
.expect("AnyView::resolve could not be downcast");
value.dry_resolve(extra_attrs);
};
let resolve = |value: Box<dyn Any>| {
let value = value
.downcast::<T>()
.expect("AnyView::resolve could not be downcast");
Box::pin(async move { value.resolve().await.into_any() })
as Pin<Box<dyn Future<Output = AnyView> + Send>>
};
#[cfg(feature = "ssr")]
let to_html =
|value: Box<dyn Any>,
@@ -209,12 +198,12 @@ where
position: &mut Position,
escape: bool,
mark_branches: bool,
extra_attrs: Option<Vec<AnyAttribute>>| {
extra_attrs: Vec<AnyAttribute>| {
let type_id = mark_branches
.then(|| format!("{:?}", TypeId::of::<T::Owned>()))
.then(|| format!("{:?}", TypeId::of::<T>()))
.unwrap_or_default();
let value = value
.downcast::<T::Owned>()
.downcast::<T>()
.expect("AnyView::to_html could not be downcast");
if mark_branches {
buf.open_branch(&type_id);
@@ -237,12 +226,12 @@ where
position: &mut Position,
escape: bool,
mark_branches: bool,
extra_attrs: Option<Vec<AnyAttribute>>| {
extra_attrs: Vec<AnyAttribute>| {
let type_id = mark_branches
.then(|| format!("{:?}", TypeId::of::<T::Owned>()))
.then(|| format!("{:?}", TypeId::of::<T>()))
.unwrap_or_default();
let value = value
.downcast::<T::Owned>()
.downcast::<T>()
.expect("AnyView::to_html could not be downcast");
if mark_branches {
buf.open_branch(&type_id);
@@ -259,91 +248,92 @@ where
}
};
#[cfg(feature = "ssr")]
let to_html_async_ooo = |value: Box<dyn Any>,
buf: &mut StreamBuilder,
position: &mut Position,
escape: bool,
mark_branches: bool,
extra_attrs: Option<
Vec<AnyAttribute>,
>| {
let to_html_async_ooo =
|value: Box<dyn Any>,
buf: &mut StreamBuilder,
position: &mut Position,
escape: bool,
mark_branches: bool,
extra_attrs: Vec<AnyAttribute>| {
let value = value
.downcast::<T>()
.expect("AnyView::to_html could not be downcast");
value.to_html_async_with_buf::<true>(
buf,
position,
escape,
mark_branches,
extra_attrs,
);
};
let build = |value: Box<dyn Any>| {
let value = value
.downcast::<T::Owned>()
.expect("AnyView::to_html could not be downcast");
value.to_html_async_with_buf::<true>(
buf,
position,
escape,
mark_branches,
extra_attrs,
);
};
let build = |value: Box<dyn Any>, extra_attrs: Option<Vec<AnyAttribute>>| {
let value = value
.downcast::<T::Owned>()
.downcast::<T>()
.expect("AnyView::build couldn't downcast");
let state = Box::new(value.build(extra_attrs));
let state = Box::new(value.build());
AnyViewState {
type_id: TypeId::of::<T::Owned>(),
type_id: TypeId::of::<T>(),
state,
mount: mount_any::<T::Owned>,
unmount: unmount_any::<T::Owned>,
insert_before_this: insert_before_this::<T::Owned>,
mount: mount_any::<T>,
unmount: unmount_any::<T>,
insert_before_this: insert_before_this::<T>,
elements: elements::<T>,
}
};
#[cfg(feature = "hydrate")]
let hydrate_from_server = |value: Box<dyn Any>,
cursor: &Cursor,
position: &PositionState,
extra_attrs: Option<
Vec<AnyAttribute>,
>| {
let value = value.downcast::<T::Owned>().expect(
"AnyView::hydrate_from_server couldn't downcast",
);
let state = Box::new(value.hydrate::<true>(
cursor,
position,
extra_attrs,
));
let hydrate_from_server =
|value: Box<dyn Any>,
cursor: &Cursor,
position: &PositionState| {
let value = value.downcast::<T>().expect(
"AnyView::hydrate_from_server couldn't downcast",
);
let state =
Box::new(value.hydrate::<true>(cursor, position));
AnyViewState {
type_id: TypeId::of::<T::Owned>(),
state,
mount: mount_any::<T::Owned>,
unmount: unmount_any::<T::Owned>,
insert_before_this: insert_before_this::<T::Owned>,
}
};
AnyViewState {
type_id: TypeId::of::<T>(),
state,
mount: mount_any::<T>,
unmount: unmount_any::<T>,
insert_before_this: insert_before_this::<T>,
elements: elements::<T>,
}
};
let rebuild =
|value: Box<dyn Any>,
state: &mut AnyViewState, extra_attrs: Option<Vec<AnyAttribute>>| {
|new_type_id: TypeId,
value: Box<dyn Any>,
state: &mut AnyViewState| {
let value = value
.downcast::<T::Owned>()
.downcast::<T>()
.expect("AnyView::rebuild couldn't downcast value");
let state = state.state.downcast_mut().expect(
"AnyView::rebuild couldn't downcast state",
);
value.rebuild(state, extra_attrs);
if new_type_id == state.type_id {
let state = state.state.downcast_mut().expect(
"AnyView::rebuild couldn't downcast state",
);
value.rebuild(state);
} else {
let mut new = value.into_any().build();
state.insert_before_this(&mut new);
state.unmount();
*state = new;
}
};
AnyView {
type_id: TypeId::of::<T::Owned>(),
type_id: TypeId::of::<T>(),
value,
extra_attrs: vec![],
build,
rebuild,
#[cfg(feature = "ssr")]
html_len,
#[cfg(feature = "ssr")]
resolve: resolve::<T::Owned>,
resolve,
#[cfg(feature = "ssr")]
dry_resolve,
#[cfg(feature = "ssr")]
html_len,
#[cfg(feature = "ssr")]
to_html,
#[cfg(feature = "ssr")]
to_html_async,
@@ -357,206 +347,61 @@ where
}
}
/// Ignore, this is a hack for pre use<..> syntax.
/// https://github.com/rust-lang/rfcs/blob/master/text/3498-lifetime-capture-rules-2024.md#the-captures-trick
pub trait __Captures<T: ?Sized> {}
impl<T: ?Sized, U: ?Sized> __Captures<T> for U {}
/// A mutable view into the extra attributes stored in an [`AnyView`].
#[derive(Default)]
pub struct ExtraAttrsMut<'a>(Option<Vec<&'a mut Vec<AnyAttribute>>>);
impl<'a> ExtraAttrsMut<'a> {
/// Create a new mutable view from owned attributes.
pub fn from_owned(extra_attrs: &'a mut Option<Vec<AnyAttribute>>) -> Self {
match extra_attrs {
Some(extra_attrs) => {
if extra_attrs.is_empty() {
Self(None)
} else {
Self(Some(vec![extra_attrs]))
}
}
None => Self(None),
}
}
#[cfg(feature = "ssr")]
fn add_layer<'b>(
mut self,
extra_attrs: &'b mut Vec<AnyAttribute>,
) -> ExtraAttrsMut<'b>
where
'a: 'b,
{
match (self.0, extra_attrs.is_empty()) {
(Some(mut extra), false) => {
extra.push(extra_attrs);
ExtraAttrsMut(Some(extra))
}
(Some(mut extra), true) => {
self.0 = Some(extra);
self
}
(None, false) => ExtraAttrsMut(Some(vec![extra_attrs])),
(None, true) => ExtraAttrsMut(None),
}
}
/// Check if there are any extra attributes.
pub fn is_some(&self) -> bool {
match &self.0 {
Some(extra) => extra.is_empty(),
None => true,
}
}
/// "clone" the mutable view, to allow reuse in e.g. a for loop.
/// The same as .as_deref_mut() on Option<&mut T>.
pub fn as_deref_mut(&mut self) -> ExtraAttrsMut<'_> {
ExtraAttrsMut(
self.0
.as_mut()
.map(|inner| inner.iter_mut().map(|v| &mut **v).collect()),
)
}
/// Iterate over the extra attributes.
pub fn iter_mut(
&mut self,
) -> impl Iterator<Item = &mut AnyAttribute> + __Captures<&'a ()> + '_ {
match &mut self.0 {
Some(inner) => itertools::Either::Left(
inner.iter_mut().flat_map(|v| v.iter_mut()),
),
None => itertools::Either::Right(std::iter::empty()),
}
}
/// Call [`RenderHtml::resolve`] on any extra attributes in parallel.
pub async fn resolve(self) {
if let Some(extra_attr_groups) = self.0 {
futures::future::join_all(extra_attr_groups.into_iter().map(
|extra_attrs| async move {
*extra_attrs =
Attribute::resolve(std::mem::take(extra_attrs)).await;
},
))
.await;
}
}
}
fn combine_owned_extra_attrs(
parent_extra_attrs: Option<Vec<AnyAttribute>>,
extra_attrs: Vec<AnyAttribute>,
) -> Option<Vec<AnyAttribute>> {
let extra_attrs = if let Some(mut parent_extra_attrs) = parent_extra_attrs {
for attr in extra_attrs {
parent_extra_attrs.push(attr);
}
parent_extra_attrs
} else {
extra_attrs
};
if extra_attrs.is_empty() {
None
} else {
Some(extra_attrs)
}
}
impl Render for AnyView {
type State = AnyViewState;
fn build(self, extra_attrs: Option<Vec<AnyAttribute>>) -> Self::State {
(self.build)(
self.value,
combine_owned_extra_attrs(extra_attrs, self.extra_attrs),
)
fn build(self) -> Self::State {
(self.build)(self.value)
}
fn rebuild(
self,
state: &mut Self::State,
extra_attrs: Option<Vec<AnyAttribute>>,
) {
if self.type_id == state.type_id {
(self.rebuild)(
self.value,
state,
combine_owned_extra_attrs(extra_attrs, self.extra_attrs),
)
} else {
let mut new = (self.build)(
self.value,
combine_owned_extra_attrs(extra_attrs, self.extra_attrs),
);
state.insert_before_this(&mut new);
state.unmount();
*state = new;
}
fn rebuild(self, state: &mut Self::State) {
(self.rebuild)(self.type_id, self.value, state)
}
}
impl AddAnyAttr for AnyView {
type Output<SomeNewAttr: Attribute> = Self;
type Output<SomeNewAttr: Attribute> = AnyViewWithAttrs;
#[allow(unused_variables)]
fn add_any_attr<NewAttr: Attribute>(
mut self,
self,
attr: NewAttr,
) -> Self::Output<NewAttr>
where
Self::Output<NewAttr>: RenderHtml,
{
self.extra_attrs
.push(attr.into_cloneable_owned().into_any_attr());
self
AnyViewWithAttrs {
view: self,
attrs: vec![attr.into_cloneable_owned().into_any_attr()],
}
}
}
impl RenderHtml for AnyView {
type AsyncOutput = Self;
type Owned = Self;
fn dry_resolve(&mut self, extra_attrs: ExtraAttrsMut<'_>) {
fn dry_resolve(&mut self) {
#[cfg(feature = "ssr")]
{
(self.dry_resolve)(
&mut self.value,
extra_attrs.add_layer(&mut self.extra_attrs),
);
(self.dry_resolve)(&mut self.value)
}
#[cfg(not(feature = "ssr"))]
{
_ = extra_attrs;
panic!(
"You are rendering AnyView to HTML without the `ssr` feature \
enabled."
);
}
panic!(
"You are rendering AnyView to HTML without the `ssr` feature \
enabled."
);
}
async fn resolve(
mut self,
extra_attrs: ExtraAttrsMut<'_>,
) -> Self::AsyncOutput {
async fn resolve(self) -> Self::AsyncOutput {
#[cfg(feature = "ssr")]
{
(self.resolve)(
self.value,
extra_attrs.add_layer(&mut self.extra_attrs),
)
.await
(self.resolve)(self.value).await
}
#[cfg(not(feature = "ssr"))]
{
_ = extra_attrs;
panic!(
"You are rendering AnyView to HTML without the `ssr` feature \
enabled."
);
}
panic!(
"You are rendering AnyView to HTML without the `ssr` feature \
enabled."
);
}
const MIN_LENGTH: usize = 0;
@@ -567,19 +412,17 @@ impl RenderHtml for AnyView {
position: &mut Position,
escape: bool,
mark_branches: bool,
extra_attrs: Option<Vec<AnyAttribute>>,
extra_attrs: Vec<AnyAttribute>,
) {
#[cfg(feature = "ssr")]
{
(self.to_html)(
self.value,
buf,
position,
escape,
mark_branches,
combine_owned_extra_attrs(extra_attrs, self.extra_attrs),
);
}
(self.to_html)(
self.value,
buf,
position,
escape,
mark_branches,
extra_attrs,
);
#[cfg(not(feature = "ssr"))]
{
_ = mark_branches;
@@ -600,31 +443,29 @@ impl RenderHtml for AnyView {
position: &mut Position,
escape: bool,
mark_branches: bool,
extra_attrs: Option<Vec<AnyAttribute>>,
extra_attrs: Vec<AnyAttribute>,
) where
Self: Sized,
{
#[cfg(feature = "ssr")]
{
if OUT_OF_ORDER {
(self.to_html_async_ooo)(
self.value,
buf,
position,
escape,
mark_branches,
combine_owned_extra_attrs(extra_attrs, self.extra_attrs),
);
} else {
(self.to_html_async)(
self.value,
buf,
position,
escape,
mark_branches,
combine_owned_extra_attrs(extra_attrs, self.extra_attrs),
);
}
if OUT_OF_ORDER {
(self.to_html_async_ooo)(
self.value,
buf,
position,
escape,
mark_branches,
extra_attrs,
);
} else {
(self.to_html_async)(
self.value,
buf,
position,
escape,
mark_branches,
extra_attrs,
);
}
#[cfg(not(feature = "ssr"))]
{
@@ -644,29 +485,20 @@ impl RenderHtml for AnyView {
self,
cursor: &Cursor,
position: &PositionState,
extra_attrs: Option<Vec<AnyAttribute>>,
) -> Self::State {
#[cfg(feature = "hydrate")]
{
if FROM_SERVER {
(self.hydrate_from_server)(
self.value,
cursor,
position,
combine_owned_extra_attrs(extra_attrs, self.extra_attrs),
)
} else {
panic!(
"hydrating AnyView from inside a ViewTemplate is not \
supported."
);
}
if FROM_SERVER {
(self.hydrate_from_server)(self.value, cursor, position)
} else {
panic!(
"hydrating AnyView from inside a ViewTemplate is not \
supported."
);
}
#[cfg(not(feature = "hydrate"))]
{
_ = cursor;
_ = position;
_ = extra_attrs;
panic!(
"You are trying to hydrate AnyView without the `hydrate` \
feature enabled."
@@ -674,34 +506,16 @@ impl RenderHtml for AnyView {
}
}
fn html_len(&self, extra_attrs: Option<Vec<&AnyAttribute>>) -> usize {
fn html_len(&self) -> usize {
#[cfg(feature = "ssr")]
{
(self.html_len)(
&self.value,
match (extra_attrs, self.extra_attrs.is_empty()) {
(Some(mut extra_attrs), false) => {
for attr in &self.extra_attrs {
extra_attrs.push(attr);
}
Some(extra_attrs)
}
(Some(extra_attrs), true) => Some(extra_attrs),
(None, false) => Some(self.extra_attrs.iter().collect()),
(None, true) => None,
},
)
self.html_len
}
#[cfg(not(feature = "ssr"))]
{
_ = extra_attrs;
0
}
}
fn into_owned(self) -> Self::Owned {
self
}
}
impl Mountable for AnyViewState {
@@ -720,7 +534,163 @@ impl Mountable for AnyViewState {
fn insert_before_this(&self, child: &mut dyn Mountable) -> bool {
(self.insert_before_this)(&*self.state, child)
}
fn elements(&self) -> Vec<crate::renderer::types::Element> {
(self.elements)(&*self.state)
}
}
/// wip
pub struct AnyViewWithAttrs {
view: AnyView,
attrs: Vec<AnyAttribute>,
}
impl Render for AnyViewWithAttrs {
type State = AnyViewWithAttrsState;
fn build(self) -> Self::State {
let view = self.view.build();
let elements = view.elements();
let mut attrs = Vec::with_capacity(elements.len() * self.attrs.len());
for attr in self.attrs {
for el in &elements {
attrs.push(attr.clone().build(el))
}
}
AnyViewWithAttrsState { view, attrs }
}
fn rebuild(self, state: &mut Self::State) {
self.view.rebuild(&mut state.view);
self.attrs.rebuild(&mut state.attrs);
}
}
impl RenderHtml for AnyViewWithAttrs {
type AsyncOutput = Self;
const MIN_LENGTH: usize = 0;
fn dry_resolve(&mut self) {
self.view.dry_resolve();
for attr in &mut self.attrs {
attr.dry_resolve();
}
}
async fn resolve(self) -> Self::AsyncOutput {
let resolve_view = self.view.resolve();
let resolve_attrs =
join_all(self.attrs.into_iter().map(|attr| attr.resolve()));
let (view, attrs) = join(resolve_view, resolve_attrs).await;
Self { view, attrs }
}
fn to_html_with_buf(
self,
buf: &mut String,
position: &mut Position,
escape: bool,
mark_branches: bool,
mut extra_attrs: Vec<AnyAttribute>,
) {
// `extra_attrs` will be empty here in most cases, but it will have
// attributes in it already if this is, itself, receiving additional attrs
extra_attrs.extend(self.attrs);
self.view.to_html_with_buf(
buf,
position,
escape,
mark_branches,
extra_attrs,
);
}
fn to_html_async_with_buf<const OUT_OF_ORDER: bool>(
self,
buf: &mut StreamBuilder,
position: &mut Position,
escape: bool,
mark_branches: bool,
mut extra_attrs: Vec<AnyAttribute>,
) where
Self: Sized,
{
extra_attrs.extend(self.attrs);
self.view.to_html_async_with_buf::<OUT_OF_ORDER>(
buf,
position,
escape,
mark_branches,
extra_attrs,
);
}
fn hydrate<const FROM_SERVER: bool>(
self,
cursor: &Cursor,
position: &PositionState,
) -> Self::State {
let view = self.view.hydrate::<FROM_SERVER>(cursor, position);
let elements = view.elements();
let mut attrs = Vec::with_capacity(elements.len() * self.attrs.len());
for attr in self.attrs {
for el in &elements {
attrs.push(attr.clone().hydrate::<FROM_SERVER>(el));
}
}
AnyViewWithAttrsState { view, attrs }
}
fn html_len(&self) -> usize {
self.view.html_len()
+ self.attrs.iter().map(|attr| attr.html_len()).sum::<usize>()
}
}
impl AddAnyAttr for AnyViewWithAttrs {
type Output<SomeNewAttr: Attribute> = AnyViewWithAttrs;
fn add_any_attr<NewAttr: Attribute>(
mut self,
attr: NewAttr,
) -> Self::Output<NewAttr>
where
Self::Output<NewAttr>: RenderHtml,
{
self.attrs.push(attr.into_cloneable_owned().into_any_attr());
self
}
}
/// wip
pub struct AnyViewWithAttrsState {
view: AnyViewState,
attrs: Vec<AnyAttributeState>,
}
impl Mountable for AnyViewWithAttrsState {
fn unmount(&mut self) {
self.view.unmount();
}
fn mount(
&mut self,
parent: &crate::renderer::types::Element,
marker: Option<&crate::renderer::types::Node>,
) {
self.view.mount(parent, marker)
}
fn insert_before_this(&self, child: &mut dyn Mountable) -> bool {
self.view.insert_before_this(child)
}
fn elements(&self) -> Vec<crate::renderer::types::Element> {
self.view.elements()
}
}
/*
#[cfg(test)]
mod tests {

View File

@@ -1,6 +1,6 @@
use super::{
add_attr::AddAnyAttr, any_view::ExtraAttrsMut, MarkBranch, Mountable,
Position, PositionState, Render, RenderHtml,
add_attr::AddAnyAttr, MarkBranch, Mountable, Position, PositionState,
Render, RenderHtml,
};
use crate::{
html::attribute::{any_attribute::AnyAttribute, Attribute},
@@ -17,33 +17,29 @@ where
{
type State = Either<A::State, B::State>;
fn build(self, extra_attrs: Option<Vec<AnyAttribute>>) -> Self::State {
fn build(self) -> Self::State {
match self {
Either::Left(left) => Either::Left(left.build(extra_attrs)),
Either::Right(right) => Either::Right(right.build(extra_attrs)),
Either::Left(left) => Either::Left(left.build()),
Either::Right(right) => Either::Right(right.build()),
}
}
fn rebuild(
self,
state: &mut Self::State,
extra_attrs: Option<Vec<AnyAttribute>>,
) {
fn rebuild(self, state: &mut Self::State) {
match (self, &mut *state) {
(Either::Left(new), Either::Left(old)) => {
new.rebuild(old, extra_attrs);
new.rebuild(old);
}
(Either::Right(new), Either::Right(old)) => {
new.rebuild(old, extra_attrs);
new.rebuild(old);
}
(Either::Right(new), Either::Left(old)) => {
let mut new_state = new.build(extra_attrs);
let mut new_state = new.build();
old.insert_before_this(&mut new_state);
old.unmount();
*state = Either::Right(new_state);
}
(Either::Left(new), Either::Right(old)) => {
let mut new_state = new.build(extra_attrs);
let mut new_state = new.build();
old.insert_before_this(&mut new_state);
old.unmount();
*state = Either::Left(new_state);
@@ -81,6 +77,13 @@ where
Either::Right(right) => right.insert_before_this(child),
}
}
fn elements(&self) -> Vec<crate::renderer::types::Element> {
match &self {
Either::Left(left) => left.elements(),
Either::Right(right) => right.elements(),
}
}
}
impl<A, B> AddAnyAttr for Either<A, B>
@@ -126,34 +129,28 @@ where
B: RenderHtml,
{
type AsyncOutput = Either<A::AsyncOutput, B::AsyncOutput>;
type Owned = Either<A::Owned, B::Owned>;
fn dry_resolve(&mut self, extra_attrs: ExtraAttrsMut<'_>) {
fn dry_resolve(&mut self) {
match self {
Either::Left(left) => left.dry_resolve(extra_attrs),
Either::Right(right) => right.dry_resolve(extra_attrs),
Either::Left(left) => left.dry_resolve(),
Either::Right(right) => right.dry_resolve(),
}
}
async fn resolve(
self,
extra_attrs: ExtraAttrsMut<'_>,
) -> Self::AsyncOutput {
async fn resolve(self) -> Self::AsyncOutput {
match self {
Either::Left(left) => Either::Left(left.resolve(extra_attrs).await),
Either::Right(right) => {
Either::Right(right.resolve(extra_attrs).await)
}
Either::Left(left) => Either::Left(left.resolve().await),
Either::Right(right) => Either::Right(right.resolve().await),
}
}
const MIN_LENGTH: usize = max_usize(&[A::MIN_LENGTH, B::MIN_LENGTH]);
#[inline(always)]
fn html_len(&self, extra_attrs: Option<Vec<&AnyAttribute>>) -> usize {
fn html_len(&self) -> usize {
match self {
Either::Left(i) => i.html_len(extra_attrs),
Either::Right(i) => i.html_len(extra_attrs),
Either::Left(i) => i.html_len(),
Either::Right(i) => i.html_len(),
}
}
@@ -163,7 +160,7 @@ where
position: &mut Position,
escape: bool,
mark_branches: bool,
extra_attrs: Option<Vec<AnyAttribute>>,
extra_attrs: Vec<AnyAttribute>,
) {
match self {
Either::Left(left) => {
@@ -205,7 +202,7 @@ where
position: &mut Position,
escape: bool,
mark_branches: bool,
extra_attrs: Option<Vec<AnyAttribute>>,
extra_attrs: Vec<AnyAttribute>,
) where
Self: Sized,
{
@@ -247,24 +244,14 @@ where
self,
cursor: &Cursor,
position: &PositionState,
extra_attrs: Option<Vec<AnyAttribute>>,
) -> Self::State {
match self {
Either::Left(left) => Either::Left(left.hydrate::<FROM_SERVER>(
cursor,
position,
extra_attrs,
)),
Either::Right(right) => Either::Right(
right.hydrate::<FROM_SERVER>(cursor, position, extra_attrs),
),
}
}
fn into_owned(self) -> Self::Owned {
match self {
Either::Left(left) => Either::Left(left.into_owned()),
Either::Right(right) => Either::Right(right.into_owned()),
Either::Left(left) => {
Either::Left(left.hydrate::<FROM_SERVER>(cursor, position))
}
Either::Right(right) => {
Either::Right(right.hydrate::<FROM_SERVER>(cursor, position))
}
}
}
}
@@ -293,29 +280,25 @@ where
{
type State = EitherKeepAliveState<A::State, B::State>;
fn build(self, extra_attrs: Option<Vec<AnyAttribute>>) -> Self::State {
fn build(self) -> Self::State {
let showing_b = self.show_b;
let a = self.a.map(|val| Render::build(val, extra_attrs.clone()));
let b = self.b.map(|val| Render::build(val, extra_attrs));
let a = self.a.map(Render::build);
let b = self.b.map(Render::build);
EitherKeepAliveState { a, b, showing_b }
}
fn rebuild(
self,
state: &mut Self::State,
extra_attrs: Option<Vec<AnyAttribute>>,
) {
fn rebuild(self, state: &mut Self::State) {
// set or update A -- `None` just means "no change"
match (self.a, &mut state.a) {
(Some(new), Some(old)) => new.rebuild(old, extra_attrs.clone()),
(Some(new), None) => state.a = Some(new.build(extra_attrs.clone())),
(Some(new), Some(old)) => new.rebuild(old),
(Some(new), None) => state.a = Some(new.build()),
_ => {}
}
// set or update B
match (self.b, &mut state.b) {
(Some(new), Some(old)) => new.rebuild(old, extra_attrs),
(Some(new), None) => state.b = Some(new.build(extra_attrs)),
(Some(new), Some(old)) => new.rebuild(old),
(Some(new), None) => state.b = Some(new.build()),
_ => {}
}
@@ -375,58 +358,35 @@ where
B: RenderHtml,
{
type AsyncOutput = EitherKeepAlive<A::AsyncOutput, B::AsyncOutput>;
type Owned = EitherKeepAlive<A::Owned, B::Owned>;
const MIN_LENGTH: usize = 0;
fn dry_resolve(&mut self, mut extra_attrs: ExtraAttrsMut<'_>) {
fn dry_resolve(&mut self) {
if let Some(inner) = &mut self.a {
inner.dry_resolve(extra_attrs.as_deref_mut());
inner.dry_resolve();
}
if let Some(inner) = &mut self.b {
inner.dry_resolve(extra_attrs);
inner.dry_resolve();
}
}
async fn resolve(
self,
mut extra_attrs: ExtraAttrsMut<'_>,
) -> Self::AsyncOutput {
async fn resolve(self) -> Self::AsyncOutput {
let EitherKeepAlive { a, b, show_b } = self;
// Has to be sequential if extra attrs are present:
let (a, b) = if extra_attrs.is_some() {
let a = match a {
Some(a) => Some(a.resolve(extra_attrs.as_deref_mut()).await),
None => None,
};
let b = match b {
Some(b) => Some(b.resolve(extra_attrs.as_deref_mut()).await),
None => None,
};
(a, b)
} else {
join(
async move {
match a {
Some(a) => {
Some(a.resolve(ExtraAttrsMut::default()).await)
}
None => None,
}
},
async move {
match b {
Some(b) => {
Some(b.resolve(ExtraAttrsMut::default()).await)
}
None => None,
}
},
)
.await
};
let (a, b) = join(
async move {
match a {
Some(a) => Some(a.resolve().await),
None => None,
}
},
async move {
match b {
Some(b) => Some(b.resolve().await),
None => None,
}
},
)
.await;
EitherKeepAlive { a, b, show_b }
}
@@ -436,7 +396,7 @@ where
position: &mut Position,
escape: bool,
mark_branches: bool,
extra_attrs: Option<Vec<AnyAttribute>>,
extra_attrs: Vec<AnyAttribute>,
) {
if self.show_b {
self.b
@@ -467,7 +427,7 @@ where
position: &mut Position,
escape: bool,
mark_branches: bool,
extra_attrs: Option<Vec<AnyAttribute>>,
extra_attrs: Vec<AnyAttribute>,
) where
Self: Sized,
{
@@ -498,34 +458,25 @@ where
self,
cursor: &Cursor,
position: &PositionState,
extra_attrs: Option<Vec<AnyAttribute>>,
) -> Self::State {
let showing_b = self.show_b;
let a = self.a.map(|a| {
if showing_b {
a.build(extra_attrs.clone())
a.build()
} else {
a.hydrate::<FROM_SERVER>(cursor, position, extra_attrs.clone())
a.hydrate::<FROM_SERVER>(cursor, position)
}
});
let b = self.b.map(|b| {
if showing_b {
b.hydrate::<FROM_SERVER>(cursor, position, extra_attrs)
b.hydrate::<FROM_SERVER>(cursor, position)
} else {
b.build(extra_attrs)
b.build()
}
});
EitherKeepAliveState { showing_b, a, b }
}
fn into_owned(self) -> Self::Owned {
EitherKeepAlive {
a: self.a.map(|a| a.into_owned()),
b: self.b.map(|b| b.into_owned()),
show_b: self.show_b,
}
}
}
impl<A, B> Mountable for EitherKeepAliveState<A, B>
@@ -572,6 +523,20 @@ where
.insert_before_this(child)
}
}
fn elements(&self) -> Vec<crate::renderer::types::Element> {
if self.showing_b {
self.b
.as_ref()
.map(|inner| inner.elements())
.unwrap_or_default()
} else {
self.a
.as_ref()
.map(|inner| inner.elements())
.unwrap_or_default()
}
}
}
macro_rules! tuples {
@@ -615,6 +580,12 @@ macro_rules! tuples {
$([<EitherOf $num>]::$ty(this) =>this.insert_before_this(child),)*
}
}
fn elements(&self) -> Vec<crate::renderer::types::Element> {
match &self.state {
$([<EitherOf $num>]::$ty(this) => this.elements(),)*
}
}
}
impl<$($ty,)*> Render for [<EitherOf $num>]<$($ty,)*>
@@ -625,20 +596,20 @@ macro_rules! tuples {
type State = [<EitherOf $num State>]<$($ty,)*>;
fn build(self, extra_attrs: Option<Vec<AnyAttribute>>) -> Self::State {
fn build(self) -> Self::State {
let state = match self {
$([<EitherOf $num>]::$ty(this) => [<EitherOf $num>]::$ty(this.build(extra_attrs)),)*
$([<EitherOf $num>]::$ty(this) => [<EitherOf $num>]::$ty(this.build()),)*
};
Self::State { state }
}
fn rebuild(self, state: &mut Self::State, extra_attrs: Option<Vec<AnyAttribute>>) {
fn rebuild(self, state: &mut Self::State) {
let new_state = match (self, &mut state.state) {
// rebuild same state and return early
$(([<EitherOf $num>]::$ty(new), [<EitherOf $num>]::$ty(old)) => { return new.rebuild(old, extra_attrs); },)*
$(([<EitherOf $num>]::$ty(new), [<EitherOf $num>]::$ty(old)) => { return new.rebuild(old); },)*
// or mount new state
$(([<EitherOf $num>]::$ty(new), _) => {
let mut new = new.build(extra_attrs);
let mut new = new.build();
state.insert_before_this(&mut new);
[<EitherOf $num>]::$ty(new)
},)*
@@ -682,33 +653,39 @@ macro_rules! tuples {
{
type AsyncOutput = [<EitherOf $num>]<$($ty::AsyncOutput,)*>;
type Owned = [<EitherOf $num>]<$($ty::Owned,)*>;
const MIN_LENGTH: usize = max_usize(&[$($ty ::MIN_LENGTH,)*]);
fn dry_resolve(&mut self, extra_attrs: ExtraAttrsMut<'_>) {
fn dry_resolve(&mut self) {
match self {
$([<EitherOf $num>]::$ty(this) => {
this.dry_resolve(extra_attrs);
this.dry_resolve();
})*
}
}
async fn resolve(self, extra_attrs: ExtraAttrsMut<'_>) -> Self::AsyncOutput {
async fn resolve(self) -> Self::AsyncOutput {
match self {
$([<EitherOf $num>]::$ty(this) => [<EitherOf $num>]::$ty(this.resolve(extra_attrs).await),)*
$([<EitherOf $num>]::$ty(this) => [<EitherOf $num>]::$ty(this.resolve().await),)*
}
}
#[inline(always)]
fn html_len(&self, extra_attrs: Option<Vec<&AnyAttribute>>) -> usize {
fn html_len(&self) -> usize {
match self {
$([<EitherOf $num>]::$ty(i) => i.html_len(extra_attrs),)*
$([<EitherOf $num>]::$ty(i) => i.html_len(),)*
}
}
fn to_html_with_buf(self, buf: &mut String, position: &mut Position, escape: bool, mark_branches: bool, extra_attrs: Option<Vec<AnyAttribute>>) {
fn to_html_with_buf(
self,
buf: &mut String,
position: &mut Position,
escape: bool,
mark_branches: bool,
extra_attrs: Vec<AnyAttribute>
) {
match self {
$([<EitherOf $num>]::$ty(this) => {
if mark_branches {
@@ -724,7 +701,12 @@ macro_rules! tuples {
fn to_html_async_with_buf<const OUT_OF_ORDER: bool>(
self,
buf: &mut StreamBuilder, position: &mut Position, escape: bool, mark_branches: bool, extra_attrs: Option<Vec<AnyAttribute>>) where
buf: &mut StreamBuilder,
position: &mut Position,
escape: bool,
mark_branches: bool,
extra_attrs: Vec<AnyAttribute>
) where
Self: Sized,
{
match self {
@@ -744,24 +726,15 @@ macro_rules! tuples {
self,
cursor: &Cursor,
position: &PositionState,
extra_attrs: Option<Vec<AnyAttribute>>,
) -> Self::State {
let state = match self {
$([<EitherOf $num>]::$ty(this) => {
[<EitherOf $num>]::$ty(this.hydrate::<FROM_SERVER>(cursor, position, extra_attrs))
[<EitherOf $num>]::$ty(this.hydrate::<FROM_SERVER>(cursor, position))
})*
};
Self::State { state }
}
fn into_owned(self) -> Self::Owned {
match self {
$([<EitherOf $num>]::$ty(this) => {
[<EitherOf $num>]::$ty(this.into_owned())
})*
}
}
}
}
}

View File

@@ -1,7 +1,4 @@
use super::{
add_attr::AddAnyAttr, any_view::ExtraAttrsMut, Position, PositionState,
RenderHtml,
};
use super::{add_attr::AddAnyAttr, Position, PositionState, RenderHtml};
use crate::{
html::attribute::{any_attribute::AnyAttribute, Attribute},
hydration::Cursor,
@@ -19,23 +16,19 @@ where
{
type State = ResultState<T>;
fn build(self, extra_attrs: Option<Vec<AnyAttribute>>) -> Self::State {
fn build(self) -> Self::State {
let hook = throw_error::get_error_hook();
let (state, error) = match self {
Ok(view) => (Either::Left(view.build(extra_attrs)), None),
Ok(view) => (Either::Left(view.build()), None),
Err(e) => (
Either::Right(Render::build((), None)),
Either::Right(Render::build(())),
Some(throw_error::throw(e.into())),
),
};
ResultState { state, error, hook }
}
fn rebuild(
self,
state: &mut Self::State,
extra_attrs: Option<Vec<AnyAttribute>>,
) {
fn rebuild(self, state: &mut Self::State) {
let _guard = state.hook.clone().map(throw_error::set_error_hook);
match (&mut state.state, self) {
// both errors: throw the new error and replace
@@ -44,11 +37,11 @@ where
}
// both Ok: need to rebuild child
(Either::Left(old), Ok(new)) => {
T::rebuild(new, old, extra_attrs);
T::rebuild(new, old);
}
// Ok => Err: unmount, replace with marker, and throw
(Either::Left(old), Err(err)) => {
let mut new_state = Render::build((), None);
let mut new_state = Render::build(());
old.insert_before_this(&mut new_state);
old.unmount();
state.state = Either::Right(new_state);
@@ -59,7 +52,7 @@ where
if let Some(err) = state.error.take() {
throw_error::clear(&err);
}
let mut new_state = new.build(extra_attrs);
let mut new_state = new.build();
old.insert_before_this(&mut new_state);
old.unmount();
state.state = Either::Left(new_state);
@@ -111,6 +104,10 @@ where
fn insert_before_this(&self, child: &mut dyn Mountable) -> bool {
self.state.insert_before_this(child)
}
fn elements(&self) -> Vec<crate::renderer::types::Element> {
self.state.elements()
}
}
impl<T, E> AddAnyAttr for Result<T, E>
@@ -139,29 +136,25 @@ where
E: Into<AnyError> + Send + 'static,
{
type AsyncOutput = Result<T::AsyncOutput, E>;
type Owned = Result<T::Owned, E>;
const MIN_LENGTH: usize = T::MIN_LENGTH;
fn dry_resolve(&mut self, extra_attrs: ExtraAttrsMut<'_>) {
fn dry_resolve(&mut self) {
if let Ok(inner) = self.as_mut() {
inner.dry_resolve(extra_attrs)
inner.dry_resolve()
}
}
async fn resolve(
self,
extra_attrs: ExtraAttrsMut<'_>,
) -> Self::AsyncOutput {
async fn resolve(self) -> Self::AsyncOutput {
match self {
Ok(view) => Ok(view.resolve(extra_attrs).await),
Ok(view) => Ok(view.resolve().await),
Err(e) => Err(e),
}
}
fn html_len(&self, extra_attrs: Option<Vec<&AnyAttribute>>) -> usize {
fn html_len(&self) -> usize {
match self {
Ok(i) => i.html_len(extra_attrs) + 3,
Ok(i) => i.html_len() + 3,
Err(_) => 0,
}
}
@@ -172,16 +165,18 @@ where
position: &mut super::Position,
escape: bool,
mark_branches: bool,
extra_attrs: Option<Vec<AnyAttribute>>,
extra_attrs: Vec<AnyAttribute>,
) {
match self {
Ok(inner) => inner.to_html_with_buf(
buf,
position,
escape,
mark_branches,
extra_attrs,
),
Ok(inner) => {
inner.to_html_with_buf(
buf,
position,
escape,
mark_branches,
extra_attrs,
);
}
Err(e) => {
buf.push_str("<!>");
throw_error::throw(e);
@@ -195,7 +190,7 @@ where
position: &mut Position,
escape: bool,
mark_branches: bool,
extra_attrs: Option<Vec<AnyAttribute>>,
extra_attrs: Vec<AnyAttribute>,
) where
Self: Sized,
{
@@ -218,35 +213,19 @@ where
self,
cursor: &Cursor,
position: &PositionState,
extra_attrs: Option<Vec<AnyAttribute>>,
) -> Self::State {
let hook = throw_error::get_error_hook();
let (state, error) = match self {
Ok(view) => (
Either::Left(view.hydrate::<FROM_SERVER>(
cursor,
position,
extra_attrs,
)),
Either::Left(view.hydrate::<FROM_SERVER>(cursor, position)),
None,
),
Err(e) => {
let state = RenderHtml::hydrate::<FROM_SERVER>(
(),
cursor,
position,
extra_attrs,
);
let state =
RenderHtml::hydrate::<FROM_SERVER>((), cursor, position);
(Either::Right(state), Some(throw_error::throw(e.into())))
}
};
ResultState { state, error, hook }
}
fn into_owned(self) -> Self::Owned {
match self {
Ok(view) => Ok(view.into_owned()),
Err(e) => Err(e),
}
}
}

View File

@@ -1,7 +1,6 @@
use super::{
add_attr::AddAnyAttr, any_view::ExtraAttrsMut,
batch_resolve_items_with_extra_attrs, Mountable, Position, PositionState,
Render, RenderHtml,
add_attr::AddAnyAttr, Mountable, Position, PositionState, Render,
RenderHtml,
};
use crate::{
html::attribute::{any_attribute::AnyAttribute, Attribute},
@@ -21,24 +20,20 @@ where
{
type State = OptionState<T>;
fn build(self, extra_attrs: Option<Vec<AnyAttribute>>) -> Self::State {
fn build(self) -> Self::State {
match self {
Some(value) => Either::Left(value),
None => Either::Right(()),
}
.build(extra_attrs)
.build()
}
fn rebuild(
self,
state: &mut Self::State,
extra_attrs: Option<Vec<AnyAttribute>>,
) {
fn rebuild(self, state: &mut Self::State) {
match self {
Some(value) => Either::Left(value),
None => Either::Right(()),
}
.rebuild(state, extra_attrs)
.rebuild(state)
}
}
@@ -65,29 +60,25 @@ where
T: RenderHtml,
{
type AsyncOutput = Option<T::AsyncOutput>;
type Owned = Option<T::Owned>;
const MIN_LENGTH: usize = T::MIN_LENGTH;
fn dry_resolve(&mut self, extra_attrs: ExtraAttrsMut<'_>) {
fn dry_resolve(&mut self) {
if let Some(inner) = self.as_mut() {
inner.dry_resolve(extra_attrs);
inner.dry_resolve();
}
}
async fn resolve(
self,
extra_attrs: ExtraAttrsMut<'_>,
) -> Self::AsyncOutput {
async fn resolve(self) -> Self::AsyncOutput {
match self {
None => None,
Some(value) => Some(value.resolve(extra_attrs).await),
Some(value) => Some(value.resolve().await),
}
}
fn html_len(&self, extra_attrs: Option<Vec<&AnyAttribute>>) -> usize {
fn html_len(&self) -> usize {
match self {
Some(i) => i.html_len(extra_attrs) + 3,
Some(i) => i.html_len() + 3,
None => 3,
}
}
@@ -98,7 +89,7 @@ where
position: &mut Position,
escape: bool,
mark_branches: bool,
extra_attrs: Option<Vec<AnyAttribute>>,
extra_attrs: Vec<AnyAttribute>,
) {
match self {
Some(value) => Either::Left(value),
@@ -119,7 +110,7 @@ where
position: &mut Position,
escape: bool,
mark_branches: bool,
extra_attrs: Option<Vec<AnyAttribute>>,
extra_attrs: Vec<AnyAttribute>,
) where
Self: Sized,
{
@@ -141,17 +132,12 @@ where
self,
cursor: &Cursor,
position: &PositionState,
extra_attrs: Option<Vec<AnyAttribute>>,
) -> Self::State {
match self {
Some(value) => Either::Left(value),
None => Either::Right(()),
}
.hydrate::<FROM_SERVER>(cursor, position, extra_attrs)
}
fn into_owned(self) -> Self::Owned {
self.map(RenderHtml::into_owned)
.hydrate::<FROM_SERVER>(cursor, position)
}
}
@@ -161,27 +147,20 @@ where
{
type State = VecState<T::State>;
fn build(self, extra_attrs: Option<Vec<AnyAttribute>>) -> Self::State {
fn build(self) -> Self::State {
let marker = Rndr::create_placeholder();
VecState {
states: self
.into_iter()
.map(|val| T::build(val, extra_attrs.clone()))
.collect(),
states: self.into_iter().map(T::build).collect(),
marker,
}
}
fn rebuild(
self,
state: &mut Self::State,
extra_attrs: Option<Vec<AnyAttribute>>,
) {
fn rebuild(self, state: &mut Self::State) {
let VecState { states, marker } = state;
let old = states;
// this is an unkeyed diff
if old.is_empty() {
let mut new = self.build(extra_attrs).states;
let mut new = self.build().states;
for item in new.iter_mut() {
Rndr::mount_before(item, marker.as_ref());
}
@@ -198,10 +177,10 @@ where
for item in self.into_iter().zip_longest(old.iter_mut()) {
match item {
itertools::EitherOrBoth::Both(new, old) => {
T::rebuild(new, old, extra_attrs.clone())
T::rebuild(new, old)
}
itertools::EitherOrBoth::Left(new) => {
let mut new_state = new.build(extra_attrs.clone());
let mut new_state = new.build();
Rndr::mount_before(&mut new_state, marker.as_ref());
adds.push(new_state);
}
@@ -259,6 +238,13 @@ where
self.marker.insert_before_this(child)
}
}
fn elements(&self) -> Vec<crate::renderer::types::Element> {
self.states
.iter()
.flat_map(|item| item.elements())
.collect()
}
}
impl<T> AddAnyAttr for Vec<T>
@@ -287,31 +273,24 @@ where
T: RenderHtml,
{
type AsyncOutput = Vec<T::AsyncOutput>;
type Owned = Vec<T::Owned>;
const MIN_LENGTH: usize = 0;
fn dry_resolve(&mut self, mut extra_attrs: ExtraAttrsMut<'_>) {
fn dry_resolve(&mut self) {
for inner in self.iter_mut() {
inner.dry_resolve(extra_attrs.as_deref_mut());
inner.dry_resolve();
}
}
async fn resolve(
self,
extra_attrs: ExtraAttrsMut<'_>,
) -> Self::AsyncOutput {
batch_resolve_items_with_extra_attrs(self, extra_attrs)
async fn resolve(self) -> Self::AsyncOutput {
futures::future::join_all(self.into_iter().map(T::resolve))
.await
.into_iter()
.collect()
}
fn html_len(&self, extra_attrs: Option<Vec<&AnyAttribute>>) -> usize {
self.iter()
.map(|n| n.html_len(extra_attrs.clone()))
.sum::<usize>()
+ 3
fn html_len(&self) -> usize {
self.iter().map(|n| n.html_len()).sum::<usize>() + 3
}
fn to_html_with_buf(
@@ -320,7 +299,7 @@ where
position: &mut Position,
escape: bool,
mark_branches: bool,
extra_attrs: Option<Vec<AnyAttribute>>,
extra_attrs: Vec<AnyAttribute>,
) {
let mut children = self.into_iter();
if let Some(first) = children.next() {
@@ -338,6 +317,7 @@ where
position,
escape,
mark_branches,
// each child will have the extra attributes applied
extra_attrs.clone(),
);
}
@@ -350,7 +330,7 @@ where
position: &mut Position,
escape: bool,
mark_branches: bool,
extra_attrs: Option<Vec<AnyAttribute>>,
extra_attrs: Vec<AnyAttribute>,
) where
Self: Sized,
{
@@ -380,27 +360,16 @@ where
self,
cursor: &Cursor,
position: &PositionState,
extra_attrs: Option<Vec<AnyAttribute>>,
) -> Self::State {
let states = self
.into_iter()
.map(|child| {
child.hydrate::<FROM_SERVER>(
cursor,
position,
extra_attrs.clone(),
)
})
.map(|child| child.hydrate::<FROM_SERVER>(cursor, position))
.collect();
let marker = cursor.next_placeholder(position);
VecState { states, marker }
}
fn into_owned(self) -> Self::Owned {
self.into_iter().map(RenderHtml::into_owned).collect()
}
}
impl<T, const N: usize> Render for [T; N]
@@ -409,23 +378,19 @@ where
{
type State = ArrayState<T::State, N>;
fn build(self, extra_attrs: Option<Vec<AnyAttribute>>) -> Self::State {
fn build(self) -> Self::State {
Self::State {
states: self.map(|val| T::build(val, extra_attrs.clone())),
states: self.map(T::build),
}
}
fn rebuild(
self,
state: &mut Self::State,
extra_attrs: Option<Vec<AnyAttribute>>,
) {
fn rebuild(self, state: &mut Self::State) {
let Self::State { states } = state;
let old = states;
// this is an unkeyed diff
self.into_iter()
.zip(old.iter_mut())
.for_each(|(new, old)| T::rebuild(new, old, extra_attrs.clone()));
.for_each(|(new, old)| T::rebuild(new, old));
}
}
@@ -462,6 +427,13 @@ where
false
}
}
fn elements(&self) -> Vec<crate::renderer::types::Element> {
self.states
.iter()
.flat_map(|item| item.elements())
.collect()
}
}
impl<T, const N: usize> AddAnyAttr for [T; N]
where
@@ -487,21 +459,17 @@ where
T: RenderHtml,
{
type AsyncOutput = [T::AsyncOutput; N];
type Owned = Vec<T::Owned>;
const MIN_LENGTH: usize = 0;
fn dry_resolve(&mut self, mut extra_attrs: ExtraAttrsMut<'_>) {
fn dry_resolve(&mut self) {
for inner in self.iter_mut() {
inner.dry_resolve(extra_attrs.as_deref_mut());
inner.dry_resolve();
}
}
async fn resolve(
self,
extra_attrs: ExtraAttrsMut<'_>,
) -> Self::AsyncOutput {
batch_resolve_items_with_extra_attrs(self, extra_attrs)
async fn resolve(self) -> Self::AsyncOutput {
futures::future::join_all(self.into_iter().map(T::resolve))
.await
.into_iter()
.collect::<Vec<_>>()
@@ -509,10 +477,8 @@ where
.unwrap_or_else(|_| unreachable!())
}
fn html_len(&self, extra_attrs: Option<Vec<&AnyAttribute>>) -> usize {
self.iter()
.map(|val| RenderHtml::html_len(val, extra_attrs.clone()))
.sum::<usize>()
fn html_len(&self) -> usize {
self.iter().map(RenderHtml::html_len).sum::<usize>()
}
fn to_html_with_buf(
@@ -521,7 +487,7 @@ where
position: &mut Position,
escape: bool,
mark_branches: bool,
extra_attrs: Option<Vec<AnyAttribute>>,
extra_attrs: Vec<AnyAttribute>,
) {
for child in self.into_iter() {
child.to_html_with_buf(
@@ -540,7 +506,7 @@ where
position: &mut Position,
escape: bool,
mark_branches: bool,
extra_attrs: Option<Vec<AnyAttribute>>,
extra_attrs: Vec<AnyAttribute>,
) where
Self: Sized,
{
@@ -559,17 +525,9 @@ where
self,
cursor: &Cursor,
position: &PositionState,
extra_attrs: Option<Vec<AnyAttribute>>,
) -> Self::State {
let states = self.map(|child| {
child.hydrate::<FROM_SERVER>(cursor, position, extra_attrs.clone())
});
let states =
self.map(|child| child.hydrate::<FROM_SERVER>(cursor, position));
ArrayState { states }
}
fn into_owned(self) -> Self::Owned {
self.into_iter()
.map(RenderHtml::into_owned)
.collect::<Vec<_>>()
}
}

View File

@@ -1,7 +1,6 @@
use super::{
add_attr::AddAnyAttr, any_view::ExtraAttrsMut,
batch_resolve_items_with_extra_attrs, Mountable, Position, PositionState,
Render, RenderHtml,
add_attr::AddAnyAttr, Mountable, Position, PositionState, Render,
RenderHtml,
};
use crate::{
html::attribute::{any_attribute::AnyAttribute, Attribute},
@@ -76,7 +75,7 @@ where
type State = KeyedState<K, VFS, V>;
// TODO fallible state and try_build()/try_rebuild() here
fn build(self, extra_attrs: Option<Vec<AnyAttribute>>) -> Self::State {
fn build(self) -> Self::State {
let items = self.items.into_iter();
let (capacity, _) = items.size_hint();
let mut hashed_items =
@@ -85,8 +84,7 @@ where
for (index, item) in items.enumerate() {
hashed_items.insert((self.key_fn)(&item));
let (set_index, view) = (self.view_fn)(index, item);
rendered_items
.push(Some((set_index, view.build(extra_attrs.clone()))));
rendered_items.push(Some((set_index, view.build())));
}
KeyedState {
parent: None,
@@ -96,11 +94,7 @@ where
}
}
fn rebuild(
self,
state: &mut Self::State,
extra_attrs: Option<Vec<AnyAttribute>>,
) {
fn rebuild(self, state: &mut Self::State) {
let KeyedState {
parent,
marker,
@@ -129,7 +123,6 @@ where
rendered_items,
&self.view_fn,
items,
extra_attrs,
);
*hashed_items = new_hashed_items;
@@ -138,9 +131,9 @@ where
impl<T, I, K, KF, VF, VFS, V> AddAnyAttr for Keyed<T, I, K, KF, VF, VFS, V>
where
I: IntoIterator<Item = T> + Send + 'static,
I: IntoIterator<Item = T> + Send,
K: Eq + Hash + 'static,
KF: Fn(&T) -> K + Send + 'static,
KF: Fn(&T) -> K + Send,
V: RenderHtml,
V: 'static,
VF: Fn(usize, T) -> (VFS, V) + Send + 'static,
@@ -191,38 +184,29 @@ where
impl<T, I, K, KF, VF, VFS, V> RenderHtml for Keyed<T, I, K, KF, VF, VFS, V>
where
I: IntoIterator<Item = T> + Send + 'static,
I: IntoIterator<Item = T> + Send,
K: Eq + Hash + 'static,
KF: Fn(&T) -> K + Send + 'static,
KF: Fn(&T) -> K + Send,
V: RenderHtml + 'static,
VF: Fn(usize, T) -> (VFS, V) + Send + 'static,
VFS: Fn(usize) + 'static,
T: 'static,
{
type AsyncOutput = Vec<V::AsyncOutput>; // TODO
type Owned = Keyed<T, I, K, KF, VF, VFS, V>;
const MIN_LENGTH: usize = 0;
fn dry_resolve(&mut self, _extra_attrs: ExtraAttrsMut<'_>) {
fn dry_resolve(&mut self) {
// TODO...
}
async fn resolve(
self,
extra_attrs: ExtraAttrsMut<'_>,
) -> Self::AsyncOutput {
batch_resolve_items_with_extra_attrs(
self.items
.into_iter()
.enumerate()
.map(|(index, item)| {
let (_, view) = (self.view_fn)(index, item);
view
})
.collect::<Vec<_>>(),
extra_attrs,
)
async fn resolve(self) -> Self::AsyncOutput {
futures::future::join_all(self.items.into_iter().enumerate().map(
|(index, item)| {
let (_, view) = (self.view_fn)(index, item);
view.resolve()
},
))
.await
.into_iter()
.collect::<Vec<_>>()
@@ -234,7 +218,7 @@ where
position: &mut Position,
escape: bool,
mark_branches: bool,
extra_attrs: Option<Vec<AnyAttribute>>,
extra_attrs: Vec<AnyAttribute>,
) {
for (index, item) in self.items.into_iter().enumerate() {
let (_, item) = (self.view_fn)(index, item);
@@ -256,7 +240,7 @@ where
position: &mut Position,
escape: bool,
mark_branches: bool,
extra_attrs: Option<Vec<AnyAttribute>>,
extra_attrs: Vec<AnyAttribute>,
) {
for (index, item) in self.items.into_iter().enumerate() {
let (_, item) = (self.view_fn)(index, item);
@@ -276,7 +260,6 @@ where
self,
cursor: &Cursor,
position: &PositionState,
extra_attrs: Option<Vec<AnyAttribute>>,
) -> Self::State {
// get parent and position
let current = cursor.current();
@@ -298,11 +281,7 @@ where
for (index, item) in items.enumerate() {
hashed_items.insert((self.key_fn)(&item));
let (set_index, view) = (self.view_fn)(index, item);
let item = view.hydrate::<FROM_SERVER>(
cursor,
position,
extra_attrs.clone(),
);
let item = view.hydrate::<FROM_SERVER>(cursor, position);
rendered_items.push(Some((set_index, item)));
}
let marker = cursor.next_placeholder(position);
@@ -313,10 +292,6 @@ where
rendered_items,
}
}
fn into_owned(self) -> Self::Owned {
self
}
}
impl<K, VFS, V> Mountable for KeyedState<K, VFS, V>
@@ -356,6 +331,14 @@ where
})
.unwrap_or_else(|| self.marker.insert_before_this(child))
}
fn elements(&self) -> Vec<crate::renderer::types::Element> {
self.rendered_items
.iter()
.flatten()
.flat_map(|item| item.1.elements())
.collect()
}
}
trait VecExt<T> {
@@ -544,7 +527,6 @@ fn apply_diff<T, VFS, V>(
children: &mut Vec<Option<(VFS, V::State)>>,
view_fn: impl Fn(usize, T) -> (VFS, V),
mut items: Vec<Option<T>>,
extra_attrs: Option<Vec<AnyAttribute>>,
) where
VFS: Fn(usize),
V: Render,
@@ -618,7 +600,7 @@ fn apply_diff<T, VFS, V>(
for DiffOpAdd { at, mode } in add_cmds {
let item = items[at].take().unwrap();
let (set_index, item) = view_fn(at, item);
let mut item = item.build(extra_attrs.clone());
let mut item = item.build();
match mode {
DiffOpAddMode::Normal => {
@@ -729,7 +711,7 @@ mod tests {
#[test]
fn keyed_creates_list() {
let el = ul((), keyed(1..=3, |k| *k, item));
let el_state = el.build(None);
let el_state = el.build();
assert_eq!(
el_state.el.to_debug_html(),
"<ul><li>1</li><li>2</li><li>3</li></ul>"
@@ -739,7 +721,7 @@ mod tests {
#[test]
fn adding_items_updates_list() {
let el = ul((), keyed(1..=3, |k| *k, item));
let mut el_state = el.build(None);
let mut el_state = el.build();
let el = ul((), keyed(1..=5, |k| *k, item));
el.rebuild(&mut el_state);
assert_eq!(
@@ -751,7 +733,7 @@ mod tests {
#[test]
fn removing_items_updates_list() {
let el = ul((), keyed(1..=3, |k| *k, item));
let mut el_state = el.build(None);
let mut el_state = el.build();
let el = ul((), keyed(1..=2, |k| *k, item));
el.rebuild(&mut el_state);
assert_eq!(
@@ -763,7 +745,7 @@ mod tests {
#[test]
fn swapping_items_updates_list() {
let el = ul((), keyed([1, 2, 3, 4, 5], |k| *k, item));
let mut el_state = el.build(None);
let mut el_state = el.build();
let el = ul((), keyed([1, 4, 3, 2, 5], |k| *k, item));
el.rebuild(&mut el_state);
assert_eq!(
@@ -775,7 +757,7 @@ mod tests {
#[test]
fn swapping_and_removing_orders_correctly() {
let el = ul((), keyed([1, 2, 3, 4, 5], |k| *k, item));
let mut el_state = el.build(None);
let mut el_state = el.build();
let el = ul((), keyed([1, 4, 3, 5], |k| *k, item));
el.rebuild(&mut el_state);
assert_eq!(
@@ -787,7 +769,7 @@ mod tests {
#[test]
fn arbitrarily_hard_adjustment() {
let el = ul((), keyed([1, 2, 3, 4, 5], |k| *k, item));
let mut el_state = el.build(None);
let mut el_state = el.build();
let el = ul((), keyed([2, 4, 3], |k| *k, item));
el.rebuild(&mut el_state);
assert_eq!(
@@ -799,7 +781,7 @@ mod tests {
#[test]
fn a_series_of_moves() {
let el = ul((), keyed([1, 2, 3, 4, 5], |k| *k, item));
let mut el_state = el.build(None);
let mut el_state = el.build();
let el = ul((), keyed([2, 4, 3], |k| *k, item));
el.rebuild(&mut el_state);
let el = ul((), keyed([1, 7, 5, 11, 13, 17], |k| *k, item));
@@ -819,7 +801,7 @@ mod tests {
#[test]
fn clearing_works() {
let el = ul((), keyed([1, 2, 3, 4, 5], |k| *k, item));
let mut el_state = el.build(None);
let mut el_state = el.build();
let el = ul((), keyed([], |k| *k, item));
el.rebuild(&mut el_state);
assert_eq!(el_state.el.to_debug_html(), "<ul></ul>");

View File

@@ -3,7 +3,6 @@ use crate::{
html::attribute::any_attribute::AnyAttribute, hydration::Cursor,
ssr::StreamBuilder,
};
use any_view::ExtraAttrsMut;
use parking_lot::RwLock;
use std::{cell::RefCell, future::Future, rc::Rc, sync::Arc};
@@ -41,14 +40,10 @@ pub trait Render: Sized {
type State: Mountable;
/// Creates the view for the first time, without hydrating from existing HTML.
fn build(self, extra_attrs: Option<Vec<AnyAttribute>>) -> Self::State;
fn build(self) -> Self::State;
/// Updates the view with new data.
fn rebuild(
self,
state: &mut Self::State,
extra_attrs: Option<Vec<AnyAttribute>>,
);
fn rebuild(self, state: &mut Self::State);
}
pub(crate) trait MarkBranch {
@@ -104,9 +99,6 @@ where
/// The type of the view after waiting for all asynchronous data to load.
type AsyncOutput: RenderHtml;
/// A static version of this type.
type Owned: RenderHtml + 'static;
/// The minimum length of HTML created when this view is rendered.
const MIN_LENGTH: usize;
@@ -116,20 +108,17 @@ where
/// “Runs” the view without other side effects. For primitive types, this is a no-op. For
/// reactive types, this can be used to gather data about reactivity or about asynchronous data
/// that needs to be loaded.
fn dry_resolve(&mut self, extra_attrs: ExtraAttrsMut<'_>);
fn dry_resolve(&mut self);
/// Waits for any asynchronous sections of the view to load and returns the output.
fn resolve(
self,
extra_attrs: ExtraAttrsMut<'_>,
) -> impl Future<Output = Self::AsyncOutput> + Send;
fn resolve(self) -> impl Future<Output = Self::AsyncOutput> + Send;
/// An estimated length for this view, when rendered to HTML.
///
/// This is used for calculating the string buffer size when rendering HTML. It does not need
/// to be precise, but should be an appropriate estimate. The more accurate, the fewer
/// reallocations will be required and the faster server-side rendering will be.
fn html_len(&self, _extra_attrs: Option<Vec<&AnyAttribute>>) -> usize {
fn html_len(&self) -> usize {
Self::MIN_LENGTH
}
@@ -138,13 +127,13 @@ where
where
Self: Sized,
{
let mut buf = String::with_capacity(self.html_len(None));
let mut buf = String::with_capacity(self.html_len());
self.to_html_with_buf(
&mut buf,
&mut Position::FirstChild,
true,
false,
None,
vec![],
);
buf
}
@@ -156,13 +145,13 @@ where
where
Self: Sized,
{
let mut buf = String::with_capacity(self.html_len(None));
let mut buf = String::with_capacity(self.html_len());
self.to_html_with_buf(
&mut buf,
&mut Position::FirstChild,
true,
true,
None,
vec![],
);
buf
}
@@ -172,14 +161,13 @@ where
where
Self: Sized,
{
let mut builder =
StreamBuilder::with_capacity(self.html_len(None), None);
let mut builder = StreamBuilder::with_capacity(self.html_len(), None);
self.to_html_async_with_buf::<false>(
&mut builder,
&mut Position::FirstChild,
true,
false,
None,
vec![],
);
builder.finish()
}
@@ -191,14 +179,13 @@ where
where
Self: Sized,
{
let mut builder =
StreamBuilder::with_capacity(self.html_len(None), None);
let mut builder = StreamBuilder::with_capacity(self.html_len(), None);
self.to_html_async_with_buf::<false>(
&mut builder,
&mut Position::FirstChild,
true,
true,
None,
vec![],
);
builder.finish()
}
@@ -210,14 +197,14 @@ where
{
//let capacity = self.html_len();
let mut builder =
StreamBuilder::with_capacity(self.html_len(None), Some(vec![0]));
StreamBuilder::with_capacity(self.html_len(), Some(vec![0]));
self.to_html_async_with_buf::<true>(
&mut builder,
&mut Position::FirstChild,
true,
false,
None,
vec![],
);
builder.finish()
}
@@ -230,14 +217,14 @@ where
Self: Sized,
{
let mut builder =
StreamBuilder::with_capacity(self.html_len(None), Some(vec![0]));
StreamBuilder::with_capacity(self.html_len(), Some(vec![0]));
self.to_html_async_with_buf::<true>(
&mut builder,
&mut Position::FirstChild,
true,
true,
None,
vec![],
);
builder.finish()
}
@@ -249,7 +236,7 @@ where
position: &mut Position,
escape: bool,
mark_branches: bool,
extra_attrs: Option<Vec<AnyAttribute>>,
extra_attrs: Vec<AnyAttribute>,
);
/// Renders a view into a buffer of (synchronous or asynchronous) HTML chunks.
@@ -259,7 +246,7 @@ where
position: &mut Position,
escape: bool,
mark_branches: bool,
extra_attrs: Option<Vec<AnyAttribute>>,
extra_attrs: Vec<AnyAttribute>,
) where
Self: Sized,
{
@@ -285,7 +272,6 @@ where
self,
cursor: &Cursor,
position: &PositionState,
extra_attrs: Option<Vec<AnyAttribute>>,
) -> Self::State;
/// Hydrates using [`RenderHtml::hydrate`], beginning at the given element.
@@ -310,49 +296,8 @@ where
{
let cursor = Cursor::new(el.clone());
let position = PositionState::new(position);
self.hydrate::<FROM_SERVER>(&cursor, &position, None)
self.hydrate::<FROM_SERVER>(&cursor, &position)
}
/// Converts this view into a owned/static type.
fn into_owned(self) -> Self::Owned;
}
/// Resolving multiple children when extra_attrs exist is tricky, because the extra_attrs are potentially shared.
///
/// The current assumption is:
/// - resolve() should not be run on an AnyAttribute more than once,
/// - any of the children could resolve() the extra_attr_states
///
/// Therefore, if any extra_attrs exist, resolving must happen sequentially, until any of the children resolves the extra_attrs.
/// After that, the remaining can be done in parallel, like they will be if no extra_attrs exist.
pub(crate) async fn batch_resolve_items_with_extra_attrs<T: RenderHtml>(
items: impl IntoIterator<Item = T>,
mut extra_attrs: ExtraAttrsMut<'_>,
) -> impl IntoIterator<Item = T::AsyncOutput> {
let mut item_iter = items.into_iter();
let mut preresolved = vec![];
if extra_attrs.is_some() {
// Reset resolved state to fresh if dirty:
extra_attrs
.as_deref_mut()
.iter_mut()
.for_each(|attr| attr.resolved = false);
for item in item_iter.by_ref() {
preresolved.push(item.resolve(extra_attrs.as_deref_mut()).await);
// Once all resolved, can switch to parallel and not pass in extra_attrs anymore,
// once they've already all resolved:
if extra_attrs.iter_mut().all(|attr| attr.resolved) {
break;
}
}
}
preresolved.into_iter().chain(
futures::future::join_all(
item_iter.map(|val| T::resolve(val, ExtraAttrsMut::default())),
)
.await
.into_iter(),
)
}
/// Allows a type to be mounted to the DOM.
@@ -383,6 +328,9 @@ pub trait Mountable {
child.mount(parent, marker);
}
}
/// wip
fn elements(&self) -> Vec<crate::renderer::types::Element>;
}
/// Indicates where a node should be mounted to its parent.
@@ -418,6 +366,12 @@ where
.map(|inner| inner.insert_before_this(child))
.unwrap_or(false)
}
fn elements(&self) -> Vec<crate::renderer::types::Element> {
self.as_ref()
.map(|inner| inner.elements())
.unwrap_or_default()
}
}
impl<T> Mountable for Rc<RefCell<T>>
@@ -439,6 +393,10 @@ where
fn insert_before_this(&self, child: &mut dyn Mountable) -> bool {
self.borrow().insert_before_this(child)
}
fn elements(&self) -> Vec<crate::renderer::types::Element> {
self.borrow().elements()
}
}
/// Allows data to be added to a static template.

View File

@@ -1,7 +1,4 @@
use super::{
any_view::ExtraAttrsMut, Mountable, Position, PositionState, Render,
RenderHtml,
};
use super::{Mountable, Position, PositionState, Render, RenderHtml};
use crate::{
html::attribute::any_attribute::AnyAttribute,
hydration::Cursor,
@@ -44,18 +41,22 @@ macro_rules! render_primitive {
) -> bool {
self.0.insert_before_this(child)
}
fn elements(&self) -> Vec<crate::renderer::types::Element> {
vec![]
}
}
impl Render for $child_type {
type State = [<$child_type:camel State>];
fn build(self, _extra_attrs: Option<Vec<AnyAttribute>>) -> Self::State {
fn build(self) -> Self::State {
let node = Rndr::create_text_node(&self.to_string());
[<$child_type:camel State>](node, self)
}
fn rebuild(self, state: &mut Self::State, _extra_attrs: Option<Vec<AnyAttribute>>) {
fn rebuild(self, state: &mut Self::State) {
let [<$child_type:camel State>](node, this) = state;
if &self != this {
Rndr::set_text(node, &self.to_string());
@@ -69,17 +70,16 @@ macro_rules! render_primitive {
impl RenderHtml for $child_type
{
type AsyncOutput = Self;
type Owned = Self;
const MIN_LENGTH: usize = 0;
fn dry_resolve(&mut self, _extra_attrs: ExtraAttrsMut<'_>) {}
fn dry_resolve(&mut self) {}
async fn resolve(self, _extra_attrs: ExtraAttrsMut<'_>) -> Self::AsyncOutput {
async fn resolve(self) -> Self::AsyncOutput {
self
}
fn to_html_with_buf(self, buf: &mut String, position: &mut Position, _escape: bool, _mark_branches: bool, _extra_attrs: Option<Vec<AnyAttribute>>) {
fn to_html_with_buf(self, buf: &mut String, position: &mut Position, _escape: bool, _mark_branches: bool, _extra_attrs: Vec<AnyAttribute>) {
// add a comment node to separate from previous sibling, if any
if matches!(position, Position::NextChildAfterText) {
buf.push_str("<!>")
@@ -92,7 +92,6 @@ macro_rules! render_primitive {
self,
cursor: &Cursor,
position: &PositionState,
_extra_attrs: Option<Vec<AnyAttribute>>,
) -> Self::State {
if position.get() == Position::FirstChild {
cursor.child();
@@ -116,10 +115,6 @@ macro_rules! render_primitive {
[<$child_type:camel State>](node, self)
}
fn into_owned(self) -> Self::Owned {
self
}
}
impl<'a> ToTemplate for $child_type {

View File

@@ -1,6 +1,6 @@
use super::{
add_attr::AddAnyAttr, any_view::ExtraAttrsMut, Mountable, Position,
PositionState, Render, RenderHtml, ToTemplate,
add_attr::AddAnyAttr, Mountable, Position, PositionState, Render,
RenderHtml, ToTemplate,
};
use crate::{
html::attribute::{
@@ -148,36 +148,27 @@ where
{
type State = Option<crate::renderer::types::Text>;
fn build(self, _extra_attrs: Option<Vec<AnyAttribute>>) -> Self::State {
fn build(self) -> Self::State {
// a view state has to be returned so it can be mounted
Some(Rndr::create_text_node(V))
}
// This type is specified as static, so no rebuilding is done.
fn rebuild(
self,
_state: &mut Self::State,
_extra_attrs: Option<Vec<AnyAttribute>>,
) {
}
fn rebuild(self, _state: &mut Self::State) {}
}
impl<const V: &'static str> RenderHtml for Static<V> {
type AsyncOutput = Self;
type Owned = Self;
const MIN_LENGTH: usize = V.len();
fn dry_resolve(&mut self, _extra_attrs: ExtraAttrsMut<'_>) {}
fn dry_resolve(&mut self) {}
// this won't actually compile because if a weird interaction because the const &'static str and
// the RPITIT, so we just refine it to a concrete future type; this will never change in any
// case
#[allow(refining_impl_trait)]
fn resolve(
self,
_extra_attrs: ExtraAttrsMut<'_>,
) -> std::future::Ready<Self> {
fn resolve(self) -> std::future::Ready<Self> {
std::future::ready(self)
}
@@ -187,7 +178,7 @@ impl<const V: &'static str> RenderHtml for Static<V> {
position: &mut Position,
escape: bool,
_mark_branches: bool,
_extra_attrs: Option<Vec<AnyAttribute>>,
_extra_attrs: Vec<AnyAttribute>,
) {
// add a comment node to separate from previous sibling, if any
if matches!(position, Position::NextChildAfterText) {
@@ -208,7 +199,6 @@ impl<const V: &'static str> RenderHtml for Static<V> {
self,
cursor: &Cursor,
position: &PositionState,
_extra_attrs: Option<Vec<AnyAttribute>>,
) -> Self::State {
if position.get() == Position::FirstChild {
cursor.child();
@@ -231,10 +221,6 @@ impl<const V: &'static str> RenderHtml for Static<V> {
Some(node)
}
fn into_owned(self) -> Self::Owned {
self
}
}
impl<const V: &'static str> AddAnyAttr for Static<V> {

View File

@@ -1,6 +1,5 @@
use super::{
any_view::ExtraAttrsMut, Mountable, Position, PositionState, Render,
RenderHtml, ToTemplate,
Mountable, Position, PositionState, Render, RenderHtml, ToTemplate,
};
use crate::{
html::attribute::any_attribute::AnyAttribute,
@@ -24,16 +23,12 @@ pub struct StrState<'a> {
impl<'a> Render for &'a str {
type State = StrState<'a>;
fn build(self, _extra_attrs: Option<Vec<AnyAttribute>>) -> Self::State {
fn build(self) -> Self::State {
let node = Rndr::create_text_node(self);
StrState { node, str: self }
}
fn rebuild(
self,
state: &mut Self::State,
_extra_attrs: Option<Vec<AnyAttribute>>,
) {
fn rebuild(self, state: &mut Self::State) {
let StrState { node, str } = state;
if &self != str {
Rndr::set_text(node, self);
@@ -44,20 +39,16 @@ impl<'a> Render for &'a str {
impl RenderHtml for &str {
type AsyncOutput = Self;
type Owned = String;
const MIN_LENGTH: usize = 0;
fn dry_resolve(&mut self, _extra_attrs: ExtraAttrsMut<'_>) {}
fn dry_resolve(&mut self) {}
async fn resolve(
self,
_extra_attrs: ExtraAttrsMut<'_>,
) -> Self::AsyncOutput {
async fn resolve(self) -> Self::AsyncOutput {
self
}
fn html_len(&self, _extra_attrs: Option<Vec<&AnyAttribute>>) -> usize {
fn html_len(&self) -> usize {
self.len()
}
@@ -67,7 +58,7 @@ impl RenderHtml for &str {
position: &mut Position,
escape: bool,
_mark_branches: bool,
_extra_attrs: Option<Vec<AnyAttribute>>,
_extra_attrs: Vec<AnyAttribute>,
) {
// add a comment node to separate from previous sibling, if any
if matches!(position, Position::NextChildAfterText) {
@@ -88,7 +79,6 @@ impl RenderHtml for &str {
self,
cursor: &Cursor,
position: &PositionState,
_extra_attrs: Option<Vec<AnyAttribute>>,
) -> Self::State {
if position.get() == Position::FirstChild {
cursor.child();
@@ -114,10 +104,6 @@ impl RenderHtml for &str {
StrState { node, str: self }
}
fn into_owned(self) -> Self::Owned {
self.to_string()
}
}
impl ToTemplate for &str {
@@ -154,6 +140,10 @@ impl Mountable for StrState<'_> {
fn insert_before_this(&self, child: &mut dyn Mountable) -> bool {
self.node.insert_before_this(child)
}
fn elements(&self) -> Vec<crate::renderer::types::Element> {
vec![]
}
}
/// Retained view state for `String`.
@@ -165,16 +155,12 @@ pub struct StringState {
impl Render for String {
type State = StringState;
fn build(self, _extra_attrs: Option<Vec<AnyAttribute>>) -> Self::State {
fn build(self) -> Self::State {
let node = Rndr::create_text_node(&self);
StringState { node, str: self }
}
fn rebuild(
self,
state: &mut Self::State,
_extra_attrs: Option<Vec<AnyAttribute>>,
) {
fn rebuild(self, state: &mut Self::State) {
let StringState { node, str } = state;
if &self != str {
Rndr::set_text(node, &self);
@@ -186,18 +172,14 @@ impl Render for String {
impl RenderHtml for String {
const MIN_LENGTH: usize = 0;
type AsyncOutput = Self;
type Owned = Self;
fn dry_resolve(&mut self, _extra_attrs: ExtraAttrsMut<'_>) {}
fn dry_resolve(&mut self) {}
async fn resolve(
self,
_extra_attrs: ExtraAttrsMut<'_>,
) -> Self::AsyncOutput {
async fn resolve(self) -> Self::AsyncOutput {
self
}
fn html_len(&self, _extra_attrs: Option<Vec<&AnyAttribute>>) -> usize {
fn html_len(&self) -> usize {
self.len()
}
@@ -207,7 +189,7 @@ impl RenderHtml for String {
position: &mut Position,
escape: bool,
mark_branches: bool,
extra_attrs: Option<Vec<AnyAttribute>>,
extra_attrs: Vec<AnyAttribute>,
) {
<&str as RenderHtml>::to_html_with_buf(
self.as_str(),
@@ -223,17 +205,11 @@ impl RenderHtml for String {
self,
cursor: &Cursor,
position: &PositionState,
extra_attrs: Option<Vec<AnyAttribute>>,
) -> Self::State {
let StrState { node, .. } =
self.as_str()
.hydrate::<FROM_SERVER>(cursor, position, extra_attrs);
self.as_str().hydrate::<FROM_SERVER>(cursor, position);
StringState { node, str: self }
}
fn into_owned(self) -> Self::Owned {
self
}
}
impl ToTemplate for String {
@@ -268,6 +244,10 @@ impl Mountable for StringState {
fn insert_before_this(&self, child: &mut dyn Mountable) -> bool {
self.node.insert_before_this(child)
}
fn elements(&self) -> Vec<crate::renderer::types::Element> {
vec![]
}
}
/// Retained view state for `Rc<str>`.
@@ -279,16 +259,12 @@ pub struct RcStrState {
impl Render for Rc<str> {
type State = RcStrState;
fn build(self, _extra_attrs: Option<Vec<AnyAttribute>>) -> Self::State {
fn build(self) -> Self::State {
let node = Rndr::create_text_node(&self);
RcStrState { node, str: self }
}
fn rebuild(
self,
state: &mut Self::State,
_extra_attrs: Option<Vec<AnyAttribute>>,
) {
fn rebuild(self, state: &mut Self::State) {
let RcStrState { node, str } = state;
if !Rc::ptr_eq(&self, str) {
Rndr::set_text(node, &self);
@@ -308,11 +284,11 @@ where
const MIN_LENGTH: usize = 0;
async fn resolve(self, _extra_attrs: Option<Vec<AnyAttribute>>) -> Self::AsyncOutput {
async fn resolve(self) -> Self::AsyncOutput {
self
}
fn html_len(&self, _extra_attrs: Option<Vec<&AnyAttribute>>) -> usize {
fn html_len(&self) -> usize {
self.len()
}
@@ -324,7 +300,6 @@ where
self,
cursor: &Cursor,
position: &PositionState,
_extra_attrs: Option<Vec<AnyAttribute>>,
) -> Self::State {
let this: &str = self.as_ref();
let StrState { node, .. } =
@@ -365,6 +340,10 @@ impl Mountable for RcStrState {
fn insert_before_this(&self, child: &mut dyn Mountable) -> bool {
self.node.insert_before_this(child)
}
fn elements(&self) -> Vec<crate::renderer::types::Element> {
vec![]
}
}
/// Retained view state for `Arc<str>`.
@@ -376,16 +355,12 @@ pub struct ArcStrState {
impl Render for Arc<str> {
type State = ArcStrState;
fn build(self, _extra_attrs: Option<Vec<AnyAttribute>>) -> Self::State {
fn build(self) -> Self::State {
let node = Rndr::create_text_node(&self);
ArcStrState { node, str: self }
}
fn rebuild(
self,
state: &mut Self::State,
_extra_attrs: Option<Vec<AnyAttribute>>,
) {
fn rebuild(self, state: &mut Self::State) {
let ArcStrState { node, str } = state;
if !Arc::ptr_eq(&self, str) {
Rndr::set_text(node, &self);
@@ -396,20 +371,16 @@ impl Render for Arc<str> {
impl RenderHtml for Arc<str> {
type AsyncOutput = Self;
type Owned = Arc<str>;
const MIN_LENGTH: usize = 0;
fn dry_resolve(&mut self, _extra_attrs: ExtraAttrsMut<'_>) {}
fn dry_resolve(&mut self) {}
async fn resolve(
self,
_extra_attrs: ExtraAttrsMut<'_>,
) -> Self::AsyncOutput {
async fn resolve(self) -> Self::AsyncOutput {
self
}
fn html_len(&self, _extra_attrs: Option<Vec<&AnyAttribute>>) -> usize {
fn html_len(&self) -> usize {
self.len()
}
@@ -419,7 +390,7 @@ impl RenderHtml for Arc<str> {
position: &mut Position,
escape: bool,
mark_branches: bool,
extra_attrs: Option<Vec<AnyAttribute>>,
extra_attrs: Vec<AnyAttribute>,
) {
<&str as RenderHtml>::to_html_with_buf(
&self,
@@ -435,17 +406,12 @@ impl RenderHtml for Arc<str> {
self,
cursor: &Cursor,
position: &PositionState,
extra_attrs: Option<Vec<AnyAttribute>>,
) -> Self::State {
let this: &str = self.as_ref();
let StrState { node, .. } =
this.hydrate::<FROM_SERVER>(cursor, position, extra_attrs);
this.hydrate::<FROM_SERVER>(cursor, position);
ArcStrState { node, str: self }
}
fn into_owned(self) -> Self::Owned {
self
}
}
impl ToTemplate for Arc<str> {
@@ -480,6 +446,10 @@ impl Mountable for ArcStrState {
fn insert_before_this(&self, child: &mut dyn Mountable) -> bool {
self.node.insert_before_this(child)
}
fn elements(&self) -> Vec<crate::renderer::types::Element> {
vec![]
}
}
/// Retained view state for `Cow<'_, str>`.
@@ -491,16 +461,12 @@ pub struct CowStrState<'a> {
impl<'a> Render for Cow<'a, str> {
type State = CowStrState<'a>;
fn build(self, _extra_attrs: Option<Vec<AnyAttribute>>) -> Self::State {
fn build(self) -> Self::State {
let node = Rndr::create_text_node(&self);
CowStrState { node, str: self }
}
fn rebuild(
self,
state: &mut Self::State,
_extra_attrs: Option<Vec<AnyAttribute>>,
) {
fn rebuild(self, state: &mut Self::State) {
let CowStrState { node, str } = state;
if self != *str {
Rndr::set_text(node, &self);
@@ -511,20 +477,16 @@ impl<'a> Render for Cow<'a, str> {
impl RenderHtml for Cow<'_, str> {
type AsyncOutput = Self;
type Owned = String;
const MIN_LENGTH: usize = 0;
fn dry_resolve(&mut self, _extra_attrs: ExtraAttrsMut<'_>) {}
fn dry_resolve(&mut self) {}
async fn resolve(
self,
_extra_attrs: ExtraAttrsMut<'_>,
) -> Self::AsyncOutput {
async fn resolve(self) -> Self::AsyncOutput {
self
}
fn html_len(&self, _extra_attrs: Option<Vec<&AnyAttribute>>) -> usize {
fn html_len(&self) -> usize {
self.len()
}
@@ -534,7 +496,7 @@ impl RenderHtml for Cow<'_, str> {
position: &mut Position,
escape: bool,
mark_branches: bool,
extra_attrs: Option<Vec<AnyAttribute>>,
extra_attrs: Vec<AnyAttribute>,
) {
<&str as RenderHtml>::to_html_with_buf(
&self,
@@ -550,17 +512,12 @@ impl RenderHtml for Cow<'_, str> {
self,
cursor: &Cursor,
position: &PositionState,
extra_attrs: Option<Vec<AnyAttribute>>,
) -> Self::State {
let this: &str = self.as_ref();
let StrState { node, .. } =
this.hydrate::<FROM_SERVER>(cursor, position, extra_attrs);
this.hydrate::<FROM_SERVER>(cursor, position);
CowStrState { node, str: self }
}
fn into_owned(self) -> <Self as RenderHtml>::Owned {
self.into_owned()
}
}
impl ToTemplate for Cow<'_, str> {
@@ -595,4 +552,8 @@ impl Mountable for CowStrState<'_> {
fn insert_before_this(&self, child: &mut dyn Mountable) -> bool {
self.node.insert_before_this(child)
}
fn elements(&self) -> Vec<crate::renderer::types::Element> {
vec![]
}
}

View File

@@ -1,6 +1,6 @@
use super::{
add_attr::AddAnyAttr, any_view::ExtraAttrsMut, Mountable, Position,
PositionState, Render, RenderHtml, ToTemplate,
add_attr::AddAnyAttr, Mountable, Position, PositionState, Render,
RenderHtml, ToTemplate,
};
use crate::{
html::attribute::{any_attribute::AnyAttribute, Attribute},
@@ -40,22 +40,15 @@ where
// TODO try_build/try_rebuild()
fn build(self, extra_attrs: Option<Vec<AnyAttribute>>) -> Self::State {
fn build(self) -> Self::State {
let tpl = Self::to_template();
let contents = Rndr::clone_template(&tpl);
self.view.hydrate::<false>(
&Cursor::new(contents),
&Default::default(),
extra_attrs,
)
self.view
.hydrate::<false>(&Cursor::new(contents), &Default::default())
}
fn rebuild(
self,
state: &mut Self::State,
extra_attrs: Option<Vec<AnyAttribute>>,
) {
self.view.rebuild(state, extra_attrs)
fn rebuild(self, state: &mut Self::State) {
self.view.rebuild(state)
}
}
@@ -83,7 +76,6 @@ where
V::State: Mountable,
{
type AsyncOutput = V::AsyncOutput;
type Owned = V::Owned;
const MIN_LENGTH: usize = V::MIN_LENGTH;
@@ -93,7 +85,7 @@ where
position: &mut Position,
escape: bool,
mark_branches: bool,
extra_attrs: Option<Vec<AnyAttribute>>,
extra_attrs: Vec<AnyAttribute>,
) {
self.view.to_html_with_buf(
buf,
@@ -108,25 +100,16 @@ where
self,
cursor: &Cursor,
position: &PositionState,
extra_attrs: Option<Vec<AnyAttribute>>,
) -> Self::State {
self.view
.hydrate::<FROM_SERVER>(cursor, position, extra_attrs)
self.view.hydrate::<FROM_SERVER>(cursor, position)
}
fn dry_resolve(&mut self, extra_attrs: ExtraAttrsMut<'_>) {
self.view.dry_resolve(extra_attrs);
fn dry_resolve(&mut self) {
self.view.dry_resolve();
}
async fn resolve(
self,
extra_attrs: ExtraAttrsMut<'_>,
) -> Self::AsyncOutput {
self.view.resolve(extra_attrs).await
}
fn into_owned(self) -> Self::Owned {
self.view.into_owned()
async fn resolve(self) -> Self::AsyncOutput {
self.view.resolve().await
}
}

View File

@@ -1,6 +1,5 @@
use super::{
any_view::ExtraAttrsMut, Mountable, Position, PositionState, Render,
RenderHtml, ToTemplate,
Mountable, Position, PositionState, Render, RenderHtml, ToTemplate,
};
use crate::{
html::attribute::{any_attribute::AnyAttribute, Attribute},
@@ -15,21 +14,15 @@ use const_str_slice_concat::{
impl Render for () {
type State = crate::renderer::types::Placeholder;
fn build(self, _extra_attrs: Option<Vec<AnyAttribute>>) -> Self::State {
fn build(self) -> Self::State {
Rndr::create_placeholder()
}
fn rebuild(
self,
_state: &mut Self::State,
_extra_attrs: Option<Vec<AnyAttribute>>,
) {
}
fn rebuild(self, _state: &mut Self::State) {}
}
impl RenderHtml for () {
type AsyncOutput = ();
type Owned = ();
const MIN_LENGTH: usize = 3;
const EXISTS: bool = false;
@@ -40,7 +33,7 @@ impl RenderHtml for () {
position: &mut Position,
escape: bool,
_mark_branches: bool,
_extra_attrs: Option<Vec<AnyAttribute>>,
_extra_attrs: Vec<AnyAttribute>,
) {
if escape {
buf.push_str("<!>");
@@ -52,20 +45,13 @@ impl RenderHtml for () {
self,
cursor: &Cursor,
position: &PositionState,
_extra_attrs: Option<Vec<AnyAttribute>>,
) -> Self::State {
cursor.next_placeholder(position)
}
async fn resolve(
self,
_extra_attrs: ExtraAttrsMut<'_>,
) -> Self::AsyncOutput {
}
async fn resolve(self) -> Self::AsyncOutput {}
fn dry_resolve(&mut self, _extra_attrs: ExtraAttrsMut<'_>) {}
fn into_owned(self) -> Self::Owned {}
fn dry_resolve(&mut self) {}
}
impl AddAnyAttr for () {
@@ -94,6 +80,10 @@ impl Mountable for () {
fn insert_before_this(&self, _child: &mut dyn Mountable) -> bool {
false
}
fn elements(&self) -> Vec<crate::renderer::types::Element> {
vec![]
}
}
impl ToTemplate for () {
@@ -113,16 +103,12 @@ impl ToTemplate for () {
impl<A: Render> Render for (A,) {
type State = A::State;
fn build(self, extra_attrs: Option<Vec<AnyAttribute>>) -> Self::State {
self.0.build(extra_attrs)
fn build(self) -> Self::State {
self.0.build()
}
fn rebuild(
self,
state: &mut Self::State,
extra_attrs: Option<Vec<AnyAttribute>>,
) {
self.0.rebuild(state, extra_attrs)
fn rebuild(self, state: &mut Self::State) {
self.0.rebuild(state)
}
}
@@ -131,12 +117,11 @@ where
A: RenderHtml,
{
type AsyncOutput = (A::AsyncOutput,);
type Owned = (A::Owned,);
const MIN_LENGTH: usize = A::MIN_LENGTH;
fn html_len(&self, extra_attrs: Option<Vec<&AnyAttribute>>) -> usize {
self.0.html_len(extra_attrs)
fn html_len(&self) -> usize {
self.0.html_len()
}
fn to_html_with_buf(
@@ -145,7 +130,7 @@ where
position: &mut Position,
escape: bool,
mark_branches: bool,
extra_attrs: Option<Vec<AnyAttribute>>,
extra_attrs: Vec<AnyAttribute>,
) {
self.0.to_html_with_buf(
buf,
@@ -162,7 +147,7 @@ where
position: &mut Position,
escape: bool,
mark_branches: bool,
extra_attrs: Option<Vec<AnyAttribute>>,
extra_attrs: Vec<AnyAttribute>,
) where
Self: Sized,
{
@@ -179,24 +164,16 @@ where
self,
cursor: &Cursor,
position: &PositionState,
extra_attrs: Option<Vec<AnyAttribute>>,
) -> Self::State {
self.0.hydrate::<FROM_SERVER>(cursor, position, extra_attrs)
self.0.hydrate::<FROM_SERVER>(cursor, position)
}
async fn resolve(
self,
extra_attrs: ExtraAttrsMut<'_>,
) -> Self::AsyncOutput {
(self.0.resolve(extra_attrs).await,)
async fn resolve(self) -> Self::AsyncOutput {
(self.0.resolve().await,)
}
fn dry_resolve(&mut self, extra_attrs: ExtraAttrsMut<'_>) {
self.0.dry_resolve(extra_attrs);
}
fn into_owned(self) -> Self::Owned {
(self.0.into_owned(),)
fn dry_resolve(&mut self) {
self.0.dry_resolve();
}
}
@@ -244,21 +221,21 @@ macro_rules! impl_view_for_tuples {
type State = ($first::State, $($ty::State,)*);
fn build(self, extra_attrs: Option<Vec<AnyAttribute>>) -> Self::State {
fn build(self) -> Self::State {
#[allow(non_snake_case)]
let ($first, $($ty,)*) = self;
(
$first.build(extra_attrs.clone()),
$($ty.build(extra_attrs.clone())),*
$first.build(),
$($ty.build()),*
)
}
fn rebuild(self, state: &mut Self::State, extra_attrs: Option<Vec<AnyAttribute>>) {
fn rebuild(self, state: &mut Self::State) {
paste::paste! {
let ([<$first:lower>], $([<$ty:lower>],)*) = self;
let ([<view_ $first:lower>], $([<view_ $ty:lower>],)*) = state;
[<$first:lower>].rebuild([<view_ $first:lower>], extra_attrs.clone());
$([<$ty:lower>].rebuild([<view_ $ty:lower>], extra_attrs.clone()));*
[<$first:lower>].rebuild([<view_ $first:lower>]);
$([<$ty:lower>].rebuild([<view_ $ty:lower>]));*
}
}
}
@@ -270,18 +247,24 @@ macro_rules! impl_view_for_tuples {
{
type AsyncOutput = ($first::AsyncOutput, $($ty::AsyncOutput,)*);
type Owned = ($first::Owned, $($ty::Owned,)*);
const MIN_LENGTH: usize = $first::MIN_LENGTH $(+ $ty::MIN_LENGTH)*;
#[inline(always)]
fn html_len(&self, extra_attrs: Option<Vec<&AnyAttribute>>) -> usize {
fn html_len(&self) -> usize {
#[allow(non_snake_case)]
let ($first, $($ty,)* ) = self;
$($ty.html_len(extra_attrs.clone()) +)* $first.html_len(extra_attrs.clone())
$($ty.html_len() +)* $first.html_len()
}
fn to_html_with_buf(self, buf: &mut String, position: &mut Position, escape: bool, mark_branches: bool, extra_attrs: Option<Vec<AnyAttribute>>) {
fn to_html_with_buf(
self,
buf: &mut String,
position: &mut Position,
escape: bool,
mark_branches: bool,
extra_attrs: Vec<AnyAttribute>
) {
#[allow(non_snake_case)]
let ($first, $($ty,)* ) = self;
$first.to_html_with_buf(buf, position, escape, mark_branches, extra_attrs.clone());
@@ -290,7 +273,12 @@ macro_rules! impl_view_for_tuples {
fn to_html_async_with_buf<const OUT_OF_ORDER: bool>(
self,
buf: &mut StreamBuilder, position: &mut Position, escape: bool, mark_branches: bool, extra_attrs: Option<Vec<AnyAttribute>>) where
buf: &mut StreamBuilder,
position: &mut Position,
escape: bool,
mark_branches: bool,
extra_attrs: Vec<AnyAttribute>
) where
Self: Sized,
{
#[allow(non_snake_case)]
@@ -299,47 +287,30 @@ macro_rules! impl_view_for_tuples {
$($ty.to_html_async_with_buf::<OUT_OF_ORDER>(buf, position, escape, mark_branches, extra_attrs.clone()));*
}
fn hydrate<const FROM_SERVER: bool>(self, cursor: &Cursor, position: &PositionState, extra_attrs: Option<Vec<AnyAttribute>>) -> Self::State {
fn hydrate<const FROM_SERVER: bool>(self, cursor: &Cursor, position: &PositionState) -> Self::State {
#[allow(non_snake_case)]
let ($first, $($ty,)* ) = self;
(
$first.hydrate::<FROM_SERVER>(cursor, position, extra_attrs.clone()),
$($ty.hydrate::<FROM_SERVER>(cursor, position, extra_attrs.clone())),*
$first.hydrate::<FROM_SERVER>(cursor, position),
$($ty.hydrate::<FROM_SERVER>(cursor, position)),*
)
}
async fn resolve(self, mut extra_attrs: ExtraAttrsMut<'_>) -> Self::AsyncOutput {
async fn resolve(self) -> Self::AsyncOutput {
#[allow(non_snake_case)]
let ($first, $($ty,)*) = self;
if extra_attrs.is_some() {
// TODO: any good way to make this only partially sequantial like with batch_resolve_items_with_extra_attrs_tuples_inner()?
(
$first.resolve(extra_attrs.as_deref_mut()).await,
$($ty.resolve(extra_attrs.as_deref_mut()).await),*
)
} else {
futures::join!(
$first.resolve(ExtraAttrsMut::default()),
$($ty.resolve(ExtraAttrsMut::default())),*
)
}
}
fn dry_resolve(&mut self, mut extra_attrs: ExtraAttrsMut<'_>) {
#[allow(non_snake_case)]
let ($first, $($ty,)*) = self;
$first.dry_resolve(extra_attrs.as_deref_mut());
$($ty.dry_resolve(extra_attrs.as_deref_mut()));*
}
fn into_owned(self) -> Self::Owned {
#[allow(non_snake_case)]
let ($first, $($ty,)*) = self;
(
$first.into_owned(),
$($ty.into_owned()),*
futures::join!(
$first.resolve(),
$($ty.resolve()),*
)
}
fn dry_resolve(&mut self) {
#[allow(non_snake_case)]
let ($first, $($ty,)*) = self;
$first.dry_resolve();
$($ty.dry_resolve());*
}
}
impl<$first, $($ty),*> ToTemplate for ($first, $($ty,)*)
@@ -394,6 +365,14 @@ macro_rules! impl_view_for_tuples {
$first.insert_before_this(child)
$(|| $ty.insert_before_this(child))*
}
fn elements(&self) -> Vec<crate::renderer::types::Element> {
#[allow(non_snake_case)] // better macro performance
let ($first, $($ty,)*) = self;
$first.elements().into_iter()
$(.chain($ty.elements()))*
.collect()
}
}
impl<$first, $($ty,)*> AddAnyAttr for ($first, $($ty,)*)