1#[cfg(feature = "ssr")]
2use super::MarkBranch;
3use super::{
4 add_attr::AddAnyAttr, Mountable, Position, PositionState, Render,
5 RenderHtml,
6};
7use crate::{
8 html::attribute::Attribute, hydration::Cursor, ssr::StreamBuilder,
9};
10use std::{
11 any::{Any, TypeId},
12 fmt::Debug,
13};
14#[cfg(feature = "ssr")]
15use std::{future::Future, pin::Pin};
16
17pub struct AnyView {
28 type_id: TypeId,
29 value: Box<dyn Any + Send>,
30 build: fn(Box<dyn Any>) -> AnyViewState,
31 rebuild: fn(TypeId, Box<dyn Any>, &mut AnyViewState),
32 #[cfg(erase_components)]
34 add_any_attr: fn(
35 Box<dyn Any>,
36 crate::html::attribute::any_attribute::AnyAttribute,
37 ) -> AnyView,
38 #[cfg(feature = "ssr")]
43 html_len: usize,
44 #[cfg(feature = "ssr")]
45 to_html: fn(Box<dyn Any>, &mut String, &mut Position, bool, bool),
46 #[cfg(feature = "ssr")]
47 to_html_async:
48 fn(Box<dyn Any>, &mut StreamBuilder, &mut Position, bool, bool),
49 #[cfg(feature = "ssr")]
50 to_html_async_ooo:
51 fn(Box<dyn Any>, &mut StreamBuilder, &mut Position, bool, bool),
52 #[cfg(feature = "ssr")]
53 #[allow(clippy::type_complexity)]
54 resolve: fn(Box<dyn Any>) -> Pin<Box<dyn Future<Output = AnyView> + Send>>,
55 #[cfg(feature = "ssr")]
56 dry_resolve: fn(&mut Box<dyn Any + Send>),
57 #[cfg(feature = "hydrate")]
58 #[cfg(feature = "hydrate")]
59 #[allow(clippy::type_complexity)]
60 hydrate_from_server:
61 fn(Box<dyn Any>, &Cursor, &PositionState) -> AnyViewState,
62}
63
64pub struct AnyViewState {
66 type_id: TypeId,
67 state: Box<dyn Any>,
68 unmount: fn(&mut dyn Any),
69 mount: fn(
70 &mut dyn Any,
71 parent: &crate::renderer::types::Element,
72 marker: Option<&crate::renderer::types::Node>,
73 ),
74 insert_before_this: fn(&dyn Any, child: &mut dyn Mountable) -> bool,
75}
76
77impl Debug for AnyViewState {
78 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
79 f.debug_struct("AnyViewState")
80 .field("type_id", &self.type_id)
81 .field("state", &self.state)
82 .field("unmount", &self.unmount)
83 .field("mount", &self.mount)
84 .field("insert_before_this", &self.insert_before_this)
85 .finish()
86 }
87}
88
89pub trait IntoAny {
91 fn into_any(self) -> AnyView;
93}
94
95fn mount_any<T>(
96 state: &mut dyn Any,
97 parent: &crate::renderer::types::Element,
98 marker: Option<&crate::renderer::types::Node>,
99) where
100 T: Render,
101 T::State: 'static,
102{
103 let state = state
104 .downcast_mut::<T::State>()
105 .expect("AnyViewState::as_mountable couldn't downcast state");
106 state.mount(parent, marker)
107}
108
109fn unmount_any<T>(state: &mut dyn Any)
110where
111 T: Render,
112 T::State: 'static,
113{
114 let state = state
115 .downcast_mut::<T::State>()
116 .expect("AnyViewState::unmount couldn't downcast state");
117 state.unmount();
118}
119
120fn insert_before_this<T>(state: &dyn Any, child: &mut dyn Mountable) -> bool
121where
122 T: Render,
123 T::State: 'static,
124{
125 let state = state
126 .downcast_ref::<T::State>()
127 .expect("AnyViewState::insert_before_this couldn't downcast state");
128 state.insert_before_this(child)
129}
130
131impl<T> IntoAny for T
132where
133 T: Send,
134 T: RenderHtml + 'static,
135 T::State: 'static,
136{
137 fn into_any(self) -> AnyView {
138 #[cfg(feature = "ssr")]
139 let html_len = self.html_len();
140
141 let value = Box::new(self) as Box<dyn Any + Send>;
142
143 match value.downcast::<AnyView>() {
144 Ok(any_view) => *any_view,
146 Err(value) => {
147 #[cfg(feature = "ssr")]
148 let dry_resolve = |value: &mut Box<dyn Any + Send>| {
149 let value = value
150 .downcast_mut::<T>()
151 .expect("AnyView::resolve could not be downcast");
152 value.dry_resolve();
153 };
154
155 #[cfg(feature = "ssr")]
156 let resolve = |value: Box<dyn Any>| {
157 let value = value
158 .downcast::<T>()
159 .expect("AnyView::resolve could not be downcast");
160 Box::pin(async move { value.resolve().await.into_any() })
161 as Pin<Box<dyn Future<Output = AnyView> + Send>>
162 };
163 #[cfg(feature = "ssr")]
164 let to_html =
165 |value: Box<dyn Any>,
166 buf: &mut String,
167 position: &mut Position,
168 escape: bool,
169 mark_branches: bool| {
170 let type_id = mark_branches
171 .then(|| format!("{:?}", TypeId::of::<T>()))
172 .unwrap_or_default();
173 let value = value
174 .downcast::<T>()
175 .expect("AnyView::to_html could not be downcast");
176 if mark_branches {
177 buf.open_branch(&type_id);
178 }
179 value.to_html_with_buf(
180 buf,
181 position,
182 escape,
183 mark_branches,
184 );
185 if mark_branches {
186 buf.close_branch(&type_id);
187 }
188 };
189 #[cfg(feature = "ssr")]
190 let to_html_async =
191 |value: Box<dyn Any>,
192 buf: &mut StreamBuilder,
193 position: &mut Position,
194 escape: bool,
195 mark_branches: bool| {
196 let type_id = mark_branches
197 .then(|| format!("{:?}", TypeId::of::<T>()))
198 .unwrap_or_default();
199 let value = value
200 .downcast::<T>()
201 .expect("AnyView::to_html could not be downcast");
202 if mark_branches {
203 buf.open_branch(&type_id);
204 }
205 value.to_html_async_with_buf::<false>(
206 buf,
207 position,
208 escape,
209 mark_branches,
210 );
211 if mark_branches {
212 buf.close_branch(&type_id);
213 }
214 };
215 #[cfg(feature = "ssr")]
216 let to_html_async_ooo =
217 |value: Box<dyn Any>,
218 buf: &mut StreamBuilder,
219 position: &mut Position,
220 escape: bool,
221 mark_branches: bool| {
222 let value = value
223 .downcast::<T>()
224 .expect("AnyView::to_html could not be downcast");
225 value.to_html_async_with_buf::<true>(
226 buf,
227 position,
228 escape,
229 mark_branches,
230 );
231 };
232 let build = |value: Box<dyn Any>| {
233 let value = value
234 .downcast::<T>()
235 .expect("AnyView::build couldn't downcast");
236 let state = Box::new(value.build());
237
238 AnyViewState {
239 type_id: TypeId::of::<T>(),
240 state,
241
242 mount: mount_any::<T>,
243 unmount: unmount_any::<T>,
244 insert_before_this: insert_before_this::<T>,
245 }
246 };
247 #[cfg(feature = "hydrate")]
248 let hydrate_from_server =
249 |value: Box<dyn Any>,
250 cursor: &Cursor,
251 position: &PositionState| {
252 let value = value.downcast::<T>().expect(
253 "AnyView::hydrate_from_server couldn't downcast",
254 );
255 let state =
256 Box::new(value.hydrate::<true>(cursor, position));
257
258 AnyViewState {
259 type_id: TypeId::of::<T>(),
260 state,
261
262 mount: mount_any::<T>,
263 unmount: unmount_any::<T>,
264 insert_before_this: insert_before_this::<T>,
265 }
266 };
267
268 let rebuild =
269 |new_type_id: TypeId,
270 value: Box<dyn Any>,
271 state: &mut AnyViewState| {
272 let value = value
273 .downcast::<T>()
274 .expect("AnyView::rebuild couldn't downcast value");
275 if new_type_id == state.type_id {
276 let state = state.state.downcast_mut().expect(
277 "AnyView::rebuild couldn't downcast state",
278 );
279 value.rebuild(state);
280 } else {
281 let mut new = value.into_any().build();
282 state.insert_before_this(&mut new);
283 state.unmount();
284 *state = new;
285 }
286 };
287
288 #[cfg(erase_components)]
290 let add_any_attr = |value: Box<dyn Any>, attr: crate::html::attribute::any_attribute::AnyAttribute| {
291 let value = value
292 .downcast::<T>()
293 .expect("AnyView::add_any_attr could not be downcast");
294 value.add_any_attr(attr).into_any()
295 };
296
297 AnyView {
298 type_id: TypeId::of::<T>(),
299 value,
300 build,
301 rebuild,
302 #[cfg(erase_components)]
304 add_any_attr,
305 #[cfg(feature = "ssr")]
306 resolve,
307 #[cfg(feature = "ssr")]
308 dry_resolve,
309 #[cfg(feature = "ssr")]
310 html_len,
311 #[cfg(feature = "ssr")]
312 to_html,
313 #[cfg(feature = "ssr")]
314 to_html_async,
315 #[cfg(feature = "ssr")]
316 to_html_async_ooo,
317 #[cfg(feature = "hydrate")]
318 hydrate_from_server,
319 }
320 }
321 }
322 }
323}
324
325impl Render for AnyView {
326 type State = AnyViewState;
327
328 fn build(self) -> Self::State {
329 (self.build)(self.value)
330 }
331
332 fn rebuild(self, state: &mut Self::State) {
333 (self.rebuild)(self.type_id, self.value, state)
334 }
335}
336
337impl AddAnyAttr for AnyView {
338 type Output<SomeNewAttr: Attribute> = Self;
339
340 #[allow(unused_variables)]
341 fn add_any_attr<NewAttr: Attribute>(
342 self,
343 attr: NewAttr,
344 ) -> Self::Output<NewAttr>
345 where
346 Self::Output<NewAttr>: RenderHtml,
347 {
348 #[cfg(erase_components)]
350 {
351 use crate::html::attribute::any_attribute::IntoAnyAttribute;
352
353 let attr = attr.into_cloneable_owned();
354 (self.add_any_attr)(self.value, attr.into_any_attr())
355 }
356 #[cfg(not(erase_components))]
357 {
358 self
359 }
360 }
361}
362
363impl RenderHtml for AnyView {
364 type AsyncOutput = Self;
365
366 fn dry_resolve(&mut self) {
367 #[cfg(feature = "ssr")]
368 {
369 (self.dry_resolve)(&mut self.value)
370 }
371 #[cfg(not(feature = "ssr"))]
372 panic!(
373 "You are rendering AnyView to HTML without the `ssr` feature \
374 enabled."
375 );
376 }
377
378 async fn resolve(self) -> Self::AsyncOutput {
379 #[cfg(feature = "ssr")]
380 {
381 (self.resolve)(self.value).await
382 }
383 #[cfg(not(feature = "ssr"))]
384 panic!(
385 "You are rendering AnyView to HTML without the `ssr` feature \
386 enabled."
387 );
388 }
389
390 const MIN_LENGTH: usize = 0;
391
392 fn to_html_with_buf(
393 self,
394 buf: &mut String,
395 position: &mut Position,
396 escape: bool,
397 mark_branches: bool,
398 ) {
399 #[cfg(feature = "ssr")]
400 (self.to_html)(self.value, buf, position, escape, mark_branches);
401 #[cfg(not(feature = "ssr"))]
402 {
403 _ = mark_branches;
404 _ = buf;
405 _ = position;
406 _ = escape;
407 panic!(
408 "You are rendering AnyView to HTML without the `ssr` feature \
409 enabled."
410 );
411 }
412 }
413
414 fn to_html_async_with_buf<const OUT_OF_ORDER: bool>(
415 self,
416 buf: &mut StreamBuilder,
417 position: &mut Position,
418 escape: bool,
419 mark_branches: bool,
420 ) where
421 Self: Sized,
422 {
423 #[cfg(feature = "ssr")]
424 if OUT_OF_ORDER {
425 (self.to_html_async_ooo)(
426 self.value,
427 buf,
428 position,
429 escape,
430 mark_branches,
431 );
432 } else {
433 (self.to_html_async)(
434 self.value,
435 buf,
436 position,
437 escape,
438 mark_branches,
439 );
440 }
441 #[cfg(not(feature = "ssr"))]
442 {
443 _ = buf;
444 _ = position;
445 _ = escape;
446 _ = mark_branches;
447 panic!(
448 "You are rendering AnyView to HTML without the `ssr` feature \
449 enabled."
450 );
451 }
452 }
453
454 fn hydrate<const FROM_SERVER: bool>(
455 self,
456 cursor: &Cursor,
457 position: &PositionState,
458 ) -> Self::State {
459 #[cfg(feature = "hydrate")]
460 if FROM_SERVER {
461 (self.hydrate_from_server)(self.value, cursor, position)
462 } else {
463 panic!(
464 "hydrating AnyView from inside a ViewTemplate is not \
465 supported."
466 );
467 }
468 #[cfg(not(feature = "hydrate"))]
469 {
470 _ = cursor;
471 _ = position;
472 panic!(
473 "You are trying to hydrate AnyView without the `hydrate` \
474 feature enabled."
475 );
476 }
477 }
478
479 fn html_len(&self) -> usize {
480 #[cfg(feature = "ssr")]
481 {
482 self.html_len
483 }
484 #[cfg(not(feature = "ssr"))]
485 {
486 0
487 }
488 }
489}
490
491impl Mountable for AnyViewState {
492 fn unmount(&mut self) {
493 (self.unmount)(&mut *self.state)
494 }
495
496 fn mount(
497 &mut self,
498 parent: &crate::renderer::types::Element,
499 marker: Option<&crate::renderer::types::Node>,
500 ) {
501 (self.mount)(&mut *self.state, parent, marker)
502 }
503
504 fn insert_before_this(&self, child: &mut dyn Mountable) -> bool {
505 (self.insert_before_this)(&*self.state, child)
506 }
507}
508