1#![allow(clippy::type_complexity)]
2#[cfg(feature = "ssr")]
3use super::MarkBranch;
4use super::{
5 add_attr::AddAnyAttr, Mountable, Position, PositionState, Render,
6 RenderHtml,
7};
8use crate::{
9 erased::{Erased, ErasedLocal},
10 html::attribute::{
11 any_attribute::{AnyAttribute, AnyAttributeState, IntoAnyAttribute},
12 Attribute,
13 },
14 hydration::Cursor,
15 renderer::Rndr,
16 ssr::StreamBuilder,
17};
18use futures::future::{join, join_all};
19use std::{any::TypeId, fmt::Debug};
20#[cfg(any(feature = "ssr", feature = "hydrate"))]
21use std::{future::Future, pin::Pin};
22
23pub struct AnyView {
34 type_id: TypeId,
35 value: Erased,
36 build: fn(Erased) -> AnyViewState,
37 rebuild: fn(Erased, &mut AnyViewState),
38 #[cfg(feature = "ssr")]
43 html_len: usize,
44 #[cfg(feature = "ssr")]
45 to_html:
46 fn(Erased, &mut String, &mut Position, bool, bool, Vec<AnyAttribute>),
47 #[cfg(feature = "ssr")]
48 to_html_async: fn(
49 Erased,
50 &mut StreamBuilder,
51 &mut Position,
52 bool,
53 bool,
54 Vec<AnyAttribute>,
55 ),
56 #[cfg(feature = "ssr")]
57 to_html_async_ooo: fn(
58 Erased,
59 &mut StreamBuilder,
60 &mut Position,
61 bool,
62 bool,
63 Vec<AnyAttribute>,
64 ),
65 #[cfg(feature = "ssr")]
66 #[allow(clippy::type_complexity)]
67 resolve: fn(Erased) -> Pin<Box<dyn Future<Output = AnyView> + Send>>,
68 #[cfg(feature = "ssr")]
69 dry_resolve: fn(&mut Erased),
70 #[cfg(feature = "hydrate")]
71 #[allow(clippy::type_complexity)]
72 hydrate_from_server: fn(Erased, &Cursor, &PositionState) -> AnyViewState,
73 #[cfg(feature = "hydrate")]
74 #[allow(clippy::type_complexity)]
75 hydrate_async: fn(
76 Erased,
77 &Cursor,
78 &PositionState,
79 ) -> Pin<Box<dyn Future<Output = AnyViewState>>>,
80}
81
82impl AnyView {
83 #[doc(hidden)]
84 pub fn as_type_id(&self) -> TypeId {
85 self.type_id
86 }
87}
88
89impl Debug for AnyView {
90 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
91 f.debug_struct("AnyView")
92 .field("type_id", &self.type_id)
93 .finish_non_exhaustive()
94 }
95}
96pub struct AnyViewState {
98 type_id: TypeId,
99 state: ErasedLocal,
100 unmount: fn(&mut ErasedLocal),
101 mount: fn(
102 &mut ErasedLocal,
103 parent: &crate::renderer::types::Element,
104 marker: Option<&crate::renderer::types::Node>,
105 ),
106 insert_before_this: fn(&ErasedLocal, child: &mut dyn Mountable) -> bool,
107 elements: fn(&ErasedLocal) -> Vec<crate::renderer::types::Element>,
108 placeholder: Option<crate::renderer::types::Placeholder>,
109}
110
111impl Debug for AnyViewState {
112 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
113 f.debug_struct("AnyViewState")
114 .field("type_id", &self.type_id)
115 .field("state", &"")
116 .field("unmount", &self.unmount)
117 .field("mount", &self.mount)
118 .field("insert_before_this", &self.insert_before_this)
119 .finish()
120 }
121}
122
123pub trait IntoAny {
125 fn into_any(self) -> AnyView;
127}
128
129pub trait IntoMaybeErased {
132 type Output: IntoMaybeErased;
134
135 fn into_maybe_erased(self) -> Self::Output;
137}
138
139impl<T> IntoMaybeErased for T
140where
141 T: RenderHtml,
142{
143 #[cfg(not(erase_components))]
144 type Output = Self;
145
146 #[cfg(erase_components)]
147 type Output = AnyView;
148
149 fn into_maybe_erased(self) -> Self::Output {
150 #[cfg(not(erase_components))]
151 {
152 self
153 }
154 #[cfg(erase_components)]
155 {
156 self.into_owned().into_any()
157 }
158 }
159}
160
161fn mount_any<T>(
162 state: &mut ErasedLocal,
163 parent: &crate::renderer::types::Element,
164 marker: Option<&crate::renderer::types::Node>,
165) where
166 T: Render,
167 T::State: 'static,
168{
169 state.get_mut::<T::State>().mount(parent, marker)
170}
171
172fn unmount_any<T>(state: &mut ErasedLocal)
173where
174 T: Render,
175 T::State: 'static,
176{
177 state.get_mut::<T::State>().unmount();
178}
179
180fn insert_before_this<T>(state: &ErasedLocal, child: &mut dyn Mountable) -> bool
181where
182 T: Render,
183 T::State: 'static,
184{
185 state.get_ref::<T::State>().insert_before_this(child)
186}
187
188fn elements<T>(state: &ErasedLocal) -> Vec<crate::renderer::types::Element>
189where
190 T: Render,
191 T::State: 'static,
192{
193 state.get_ref::<T::State>().elements()
194}
195
196impl<T> IntoAny for T
197where
198 T: Send,
199 T: RenderHtml,
200{
201 fn into_any(self) -> AnyView {
202 #[cfg(feature = "ssr")]
203 fn dry_resolve<T: RenderHtml + 'static>(value: &mut Erased) {
204 value.get_mut::<T>().dry_resolve();
205 }
206
207 #[cfg(feature = "ssr")]
208 fn resolve<T: RenderHtml + 'static>(
209 value: Erased,
210 ) -> Pin<Box<dyn Future<Output = AnyView> + Send>> {
211 use futures::FutureExt;
212
213 async move { value.into_inner::<T>().resolve().await.into_any() }
214 .boxed()
215 }
216
217 #[cfg(feature = "ssr")]
218 fn to_html<T: RenderHtml + 'static>(
219 value: Erased,
220 buf: &mut String,
221 position: &mut Position,
222 escape: bool,
223 mark_branches: bool,
224 extra_attrs: Vec<AnyAttribute>,
225 ) {
226 value.into_inner::<T>().to_html_with_buf(
227 buf,
228 position,
229 escape,
230 mark_branches,
231 extra_attrs,
232 );
233 if !T::EXISTS {
234 buf.push_str("<!--<() />-->");
235 }
236 }
237
238 #[cfg(feature = "ssr")]
239 fn to_html_async<T: RenderHtml + 'static>(
240 value: Erased,
241 buf: &mut StreamBuilder,
242 position: &mut Position,
243 escape: bool,
244 mark_branches: bool,
245 extra_attrs: Vec<AnyAttribute>,
246 ) {
247 value.into_inner::<T>().to_html_async_with_buf::<false>(
248 buf,
249 position,
250 escape,
251 mark_branches,
252 extra_attrs,
253 );
254 if !T::EXISTS {
255 buf.push_sync("<!--<() />-->");
256 }
257 }
258
259 #[cfg(feature = "ssr")]
260 fn to_html_async_ooo<T: RenderHtml + 'static>(
261 value: Erased,
262 buf: &mut StreamBuilder,
263 position: &mut Position,
264 escape: bool,
265 mark_branches: bool,
266 extra_attrs: Vec<AnyAttribute>,
267 ) {
268 value.into_inner::<T>().to_html_async_with_buf::<true>(
269 buf,
270 position,
271 escape,
272 mark_branches,
273 extra_attrs,
274 );
275 if !T::EXISTS {
276 buf.push_sync("<!--<() />-->");
277 }
278 }
279
280 fn build<T: RenderHtml + 'static>(value: Erased) -> AnyViewState {
281 let state = ErasedLocal::new(value.into_inner::<T>().build());
282 let placeholder = (!T::EXISTS).then(Rndr::create_placeholder);
283 AnyViewState {
284 type_id: TypeId::of::<T>(),
285 state,
286 mount: mount_any::<T>,
287 unmount: unmount_any::<T>,
288 insert_before_this: insert_before_this::<T>,
289 elements: elements::<T>,
290 placeholder,
291 }
292 }
293
294 #[cfg(feature = "hydrate")]
295 fn hydrate_from_server<T: RenderHtml + 'static>(
296 value: Erased,
297 cursor: &Cursor,
298 position: &PositionState,
299 ) -> AnyViewState {
300 let state = ErasedLocal::new(
301 value.into_inner::<T>().hydrate::<true>(cursor, position),
302 );
303 let placeholder =
304 (!T::EXISTS).then(|| cursor.next_placeholder(position));
305 AnyViewState {
306 type_id: TypeId::of::<T>(),
307 state,
308 mount: mount_any::<T>,
309 unmount: unmount_any::<T>,
310 insert_before_this: insert_before_this::<T>,
311 elements: elements::<T>,
312 placeholder,
313 }
314 }
315
316 #[cfg(feature = "hydrate")]
317 fn hydrate_async<T: RenderHtml + 'static>(
318 value: Erased,
319 cursor: &Cursor,
320 position: &PositionState,
321 ) -> Pin<Box<dyn Future<Output = AnyViewState>>> {
322 let cursor = cursor.clone();
323 let position = position.clone();
324 Box::pin(async move {
325 let state = ErasedLocal::new(
326 value
327 .into_inner::<T>()
328 .hydrate_async(&cursor, &position)
329 .await,
330 );
331 let placeholder =
332 (!T::EXISTS).then(|| cursor.next_placeholder(&position));
333 AnyViewState {
334 type_id: TypeId::of::<T>(),
335 state,
336 mount: mount_any::<T>,
337 unmount: unmount_any::<T>,
338 insert_before_this: insert_before_this::<T>,
339 elements: elements::<T>,
340 placeholder,
341 }
342 })
343 }
344
345 fn rebuild<T: RenderHtml + 'static>(
346 value: Erased,
347 state: &mut AnyViewState,
348 ) {
349 let state = state.state.get_mut::<<T as Render>::State>();
350 value.into_inner::<T>().rebuild(state);
351 }
352
353 let value = self.into_owned();
354 AnyView {
355 type_id: TypeId::of::<T::Owned>(),
356 build: build::<T::Owned>,
357 rebuild: rebuild::<T::Owned>,
358 #[cfg(feature = "ssr")]
359 resolve: resolve::<T::Owned>,
360 #[cfg(feature = "ssr")]
361 dry_resolve: dry_resolve::<T::Owned>,
362 #[cfg(feature = "ssr")]
363 html_len: value.html_len(),
364 #[cfg(feature = "ssr")]
365 to_html: to_html::<T::Owned>,
366 #[cfg(feature = "ssr")]
367 to_html_async: to_html_async::<T::Owned>,
368 #[cfg(feature = "ssr")]
369 to_html_async_ooo: to_html_async_ooo::<T::Owned>,
370 #[cfg(feature = "hydrate")]
371 hydrate_from_server: hydrate_from_server::<T::Owned>,
372 #[cfg(feature = "hydrate")]
373 hydrate_async: hydrate_async::<T::Owned>,
374 value: Erased::new(value),
375 }
376 }
377}
378
379impl Render for AnyView {
380 type State = AnyViewState;
381
382 fn build(self) -> Self::State {
383 (self.build)(self.value)
384 }
385
386 fn rebuild(self, state: &mut Self::State) {
387 if self.type_id == state.type_id {
388 (self.rebuild)(self.value, state)
389 } else {
390 let mut new = self.build();
391 if let Some(placeholder) = &mut state.placeholder {
392 placeholder.insert_before_this(&mut new);
393 placeholder.unmount();
394 } else {
395 state.insert_before_this(&mut new);
396 }
397 state.unmount();
398 *state = new;
399 }
400 }
401}
402
403impl AddAnyAttr for AnyView {
404 type Output<SomeNewAttr: Attribute> = AnyViewWithAttrs;
405
406 #[allow(unused_variables)]
407 fn add_any_attr<NewAttr: Attribute>(
408 self,
409 attr: NewAttr,
410 ) -> Self::Output<NewAttr>
411 where
412 Self::Output<NewAttr>: RenderHtml,
413 {
414 AnyViewWithAttrs {
415 view: self,
416 attrs: vec![attr.into_cloneable_owned().into_any_attr()],
417 }
418 }
419}
420
421impl RenderHtml for AnyView {
422 type AsyncOutput = Self;
423 type Owned = Self;
424
425 fn dry_resolve(&mut self) {
426 #[cfg(feature = "ssr")]
427 {
428 (self.dry_resolve)(&mut self.value)
429 }
430 #[cfg(not(feature = "ssr"))]
431 panic!(
432 "You are rendering AnyView to HTML without the `ssr` feature \
433 enabled."
434 );
435 }
436
437 async fn resolve(self) -> Self::AsyncOutput {
438 #[cfg(feature = "ssr")]
439 {
440 (self.resolve)(self.value).await
441 }
442 #[cfg(not(feature = "ssr"))]
443 panic!(
444 "You are rendering AnyView to HTML without the `ssr` feature \
445 enabled."
446 );
447 }
448
449 const MIN_LENGTH: usize = 0;
450
451 fn to_html_with_buf(
452 self,
453 buf: &mut String,
454 position: &mut Position,
455 escape: bool,
456 mark_branches: bool,
457 extra_attrs: Vec<AnyAttribute>,
458 ) {
459 #[cfg(feature = "ssr")]
460 {
461 let type_id = if mark_branches && escape {
462 format!("{:?}", self.type_id)
463 } else {
464 Default::default()
465 };
466 if mark_branches && escape {
467 buf.open_branch(&type_id);
468 }
469 (self.to_html)(
470 self.value,
471 buf,
472 position,
473 escape,
474 mark_branches,
475 extra_attrs,
476 );
477 if mark_branches && escape {
478 buf.close_branch(&type_id);
479 if *position == Position::NextChildAfterText {
480 *position = Position::NextChild;
481 }
482 }
483 }
484 #[cfg(not(feature = "ssr"))]
485 {
486 _ = mark_branches;
487 _ = buf;
488 _ = position;
489 _ = escape;
490 _ = extra_attrs;
491 panic!(
492 "You are rendering AnyView to HTML without the `ssr` feature \
493 enabled."
494 );
495 }
496 }
497
498 fn to_html_async_with_buf<const OUT_OF_ORDER: bool>(
499 self,
500 buf: &mut StreamBuilder,
501 position: &mut Position,
502 escape: bool,
503 mark_branches: bool,
504 extra_attrs: Vec<AnyAttribute>,
505 ) where
506 Self: Sized,
507 {
508 #[cfg(feature = "ssr")]
509 if OUT_OF_ORDER {
510 let type_id = if mark_branches && escape {
511 format!("{:?}", self.type_id)
512 } else {
513 Default::default()
514 };
515 if mark_branches && escape {
516 buf.open_branch(&type_id);
517 }
518 (self.to_html_async_ooo)(
519 self.value,
520 buf,
521 position,
522 escape,
523 mark_branches,
524 extra_attrs,
525 );
526 if mark_branches && escape {
527 buf.close_branch(&type_id);
528 if *position == Position::NextChildAfterText {
529 *position = Position::NextChild;
530 }
531 }
532 } else {
533 let type_id = if mark_branches && escape {
534 format!("{:?}", self.type_id)
535 } else {
536 Default::default()
537 };
538 if mark_branches && escape {
539 buf.open_branch(&type_id);
540 }
541 (self.to_html_async)(
542 self.value,
543 buf,
544 position,
545 escape,
546 mark_branches,
547 extra_attrs,
548 );
549 if mark_branches && escape {
550 buf.close_branch(&type_id);
551 if *position == Position::NextChildAfterText {
552 *position = Position::NextChild;
553 }
554 }
555 }
556 #[cfg(not(feature = "ssr"))]
557 {
558 _ = buf;
559 _ = position;
560 _ = escape;
561 _ = mark_branches;
562 _ = extra_attrs;
563 panic!(
564 "You are rendering AnyView to HTML without the `ssr` feature \
565 enabled."
566 );
567 }
568 }
569
570 fn hydrate<const FROM_SERVER: bool>(
571 self,
572 cursor: &Cursor,
573 position: &PositionState,
574 ) -> Self::State {
575 #[cfg(feature = "hydrate")]
576 {
577 if FROM_SERVER {
578 (self.hydrate_from_server)(self.value, cursor, position)
579 } else {
580 panic!(
581 "hydrating AnyView from inside a ViewTemplate is not \
582 supported."
583 );
584 }
585 }
586 #[cfg(not(feature = "hydrate"))]
587 {
588 _ = cursor;
589 _ = position;
590 panic!(
591 "You are trying to hydrate AnyView without the `hydrate` \
592 feature enabled."
593 );
594 }
595 }
596
597 async fn hydrate_async(
598 self,
599 cursor: &Cursor,
600 position: &PositionState,
601 ) -> Self::State {
602 #[cfg(feature = "hydrate")]
603 {
604 let state =
605 (self.hydrate_async)(self.value, cursor, position).await;
606 state
607 }
608 #[cfg(not(feature = "hydrate"))]
609 {
610 _ = cursor;
611 _ = position;
612 panic!(
613 "You are trying to hydrate AnyView without the `hydrate` \
614 feature enabled."
615 );
616 }
617 }
618
619 fn html_len(&self) -> usize {
620 #[cfg(feature = "ssr")]
621 {
622 self.html_len
623 }
624 #[cfg(not(feature = "ssr"))]
625 {
626 0
627 }
628 }
629
630 fn into_owned(self) -> Self::Owned {
631 self
632 }
633}
634
635impl Mountable for AnyViewState {
636 fn unmount(&mut self) {
637 (self.unmount)(&mut self.state);
638 if let Some(placeholder) = &mut self.placeholder {
639 placeholder.unmount();
640 }
641 }
642
643 fn mount(
644 &mut self,
645 parent: &crate::renderer::types::Element,
646 marker: Option<&crate::renderer::types::Node>,
647 ) {
648 (self.mount)(&mut self.state, parent, marker);
649 if let Some(placeholder) = &mut self.placeholder {
650 placeholder.mount(parent, marker);
651 }
652 }
653
654 fn insert_before_this(&self, child: &mut dyn Mountable) -> bool {
655 let before_view = (self.insert_before_this)(&self.state, child);
656 if before_view {
657 return true;
658 }
659
660 if let Some(placeholder) = &self.placeholder {
661 placeholder.insert_before_this(child)
662 } else {
663 false
664 }
665 }
666
667 fn elements(&self) -> Vec<crate::renderer::types::Element> {
668 (self.elements)(&self.state)
669 }
670}
671
672pub struct AnyViewWithAttrs {
674 view: AnyView,
675 attrs: Vec<AnyAttribute>,
676}
677
678impl Render for AnyViewWithAttrs {
679 type State = AnyViewWithAttrsState;
680
681 fn build(self) -> Self::State {
682 let view = self.view.build();
683 let elements = view.elements();
684 let mut attrs = Vec::with_capacity(elements.len() * self.attrs.len());
685 for attr in self.attrs {
686 for el in &elements {
687 attrs.push(attr.clone().build(el))
688 }
689 }
690 AnyViewWithAttrsState { view, attrs }
691 }
692
693 fn rebuild(self, state: &mut Self::State) {
694 self.view.rebuild(&mut state.view);
695
696 for element in state.elements() {
702 self.attrs
704 .clone()
705 .rebuild(&mut (element.clone(), Vec::new()));
706 self.attrs.clone().build(&element);
708 }
709 }
710}
711
712impl RenderHtml for AnyViewWithAttrs {
713 type AsyncOutput = Self;
714 type Owned = Self;
715 const MIN_LENGTH: usize = 0;
716
717 fn dry_resolve(&mut self) {
718 self.view.dry_resolve();
719 for attr in &mut self.attrs {
720 attr.dry_resolve();
721 }
722 }
723
724 async fn resolve(self) -> Self::AsyncOutput {
725 let resolve_view = self.view.resolve();
726 let resolve_attrs =
727 join_all(self.attrs.into_iter().map(|attr| attr.resolve()));
728 let (view, attrs) = join(resolve_view, resolve_attrs).await;
729 Self { view, attrs }
730 }
731
732 fn to_html_with_buf(
733 self,
734 buf: &mut String,
735 position: &mut Position,
736 escape: bool,
737 mark_branches: bool,
738 mut extra_attrs: Vec<AnyAttribute>,
739 ) {
740 extra_attrs.extend(self.attrs);
743 self.view.to_html_with_buf(
744 buf,
745 position,
746 escape,
747 mark_branches,
748 extra_attrs,
749 );
750 }
751
752 fn to_html_async_with_buf<const OUT_OF_ORDER: bool>(
753 self,
754 buf: &mut StreamBuilder,
755 position: &mut Position,
756 escape: bool,
757 mark_branches: bool,
758 mut extra_attrs: Vec<AnyAttribute>,
759 ) where
760 Self: Sized,
761 {
762 extra_attrs.extend(self.attrs);
763 self.view.to_html_async_with_buf::<OUT_OF_ORDER>(
764 buf,
765 position,
766 escape,
767 mark_branches,
768 extra_attrs,
769 );
770 }
771
772 fn hydrate<const FROM_SERVER: bool>(
773 self,
774 cursor: &Cursor,
775 position: &PositionState,
776 ) -> Self::State {
777 let view = self.view.hydrate::<FROM_SERVER>(cursor, position);
778 let elements = view.elements();
779 let mut attrs = Vec::with_capacity(elements.len() * self.attrs.len());
780 for attr in self.attrs {
781 for el in &elements {
782 attrs.push(attr.clone().hydrate::<FROM_SERVER>(el));
783 }
784 }
785 AnyViewWithAttrsState { view, attrs }
786 }
787
788 async fn hydrate_async(
789 self,
790 cursor: &Cursor,
791 position: &PositionState,
792 ) -> Self::State {
793 let view = self.view.hydrate_async(cursor, position).await;
794 let elements = view.elements();
795 let mut attrs = Vec::with_capacity(elements.len() * self.attrs.len());
796 for attr in self.attrs {
797 for el in &elements {
798 attrs.push(attr.clone().hydrate::<true>(el));
799 }
800 }
801 AnyViewWithAttrsState { view, attrs }
802 }
803
804 fn html_len(&self) -> usize {
805 self.view.html_len()
806 + self.attrs.iter().map(|attr| attr.html_len()).sum::<usize>()
807 }
808
809 fn into_owned(self) -> Self::Owned {
810 self
811 }
812}
813
814impl AddAnyAttr for AnyViewWithAttrs {
815 type Output<SomeNewAttr: Attribute> = AnyViewWithAttrs;
816
817 fn add_any_attr<NewAttr: Attribute>(
818 mut self,
819 attr: NewAttr,
820 ) -> Self::Output<NewAttr>
821 where
822 Self::Output<NewAttr>: RenderHtml,
823 {
824 self.attrs.push(attr.into_cloneable_owned().into_any_attr());
825 self
826 }
827}
828
829pub struct AnyViewWithAttrsState {
831 view: AnyViewState,
832 #[allow(dead_code)] attrs: Vec<AnyAttributeState>,
834}
835
836impl Mountable for AnyViewWithAttrsState {
837 fn unmount(&mut self) {
838 self.view.unmount();
839 }
840
841 fn mount(
842 &mut self,
843 parent: &crate::renderer::types::Element,
844 marker: Option<&crate::renderer::types::Node>,
845 ) {
846 self.view.mount(parent, marker)
847 }
848
849 fn insert_before_this(&self, child: &mut dyn Mountable) -> bool {
850 self.view.insert_before_this(child)
851 }
852
853 fn elements(&self) -> Vec<crate::renderer::types::Element> {
854 self.view.elements()
855 }
856}
857
858