1use std::any::{Any, TypeId};
4use std::fmt;
5use std::rc::Rc;
6
7#[cfg(feature = "ssr")]
8use futures::future::{FutureExt, LocalBoxFuture};
9#[cfg(feature = "csr")]
10use web_sys::Element;
11
12use super::Key;
13#[cfg(feature = "hydration")]
14use crate::dom_bundle::Fragment;
15#[cfg(feature = "csr")]
16use crate::dom_bundle::{BSubtree, DomSlot, DynamicDomSlot};
17#[cfg(any(feature = "ssr", feature = "csr"))]
18use crate::html::AnyScope;
19#[cfg(feature = "csr")]
20use crate::html::Scoped;
21use crate::html::{BaseComponent, Scope};
22use crate::scheduler::Shared;
23#[cfg(feature = "ssr")]
24use crate::{feat_ssr::VTagKind, platform::fmt::BufWriter};
25
26pub struct VComp {
28 pub(crate) type_id: TypeId,
29 pub(crate) mountable: Box<dyn Mountable>,
30 pub(crate) key: Option<Key>,
31 _marker: u32,
33}
34
35impl fmt::Debug for VComp {
36 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
37 f.debug_struct("VComp")
38 .field("type_id", &self.type_id)
39 .field("mountable", &"..")
40 .field("key", &self.key)
41 .finish()
42 }
43}
44
45impl Clone for VComp {
46 fn clone(&self) -> Self {
47 Self {
48 type_id: self.type_id,
49 mountable: self.mountable.copy(),
50 key: self.key.clone(),
51 _marker: 0,
52 }
53 }
54}
55
56pub(crate) trait Mountable {
57 fn copy(&self) -> Box<dyn Mountable>;
58
59 fn mountable_eq(&self, rhs: &dyn Mountable) -> bool;
60 fn as_any(&self) -> &dyn Any;
61
62 #[cfg(feature = "csr")]
63 fn mount(
64 self: Box<Self>,
65 root: &BSubtree,
66 parent_scope: &AnyScope,
67 parent: Element,
68 slot: DomSlot,
69 ) -> (Box<dyn Scoped>, DynamicDomSlot);
70
71 #[cfg(feature = "csr")]
72 fn reuse(self: Box<Self>, scope: &dyn Scoped, slot: DomSlot);
73
74 #[cfg(feature = "ssr")]
75 fn render_into_stream<'a>(
76 &'a self,
77 w: &'a mut BufWriter,
78 parent_scope: &'a AnyScope,
79 hydratable: bool,
80 parent_vtag_kind: VTagKind,
81 ) -> LocalBoxFuture<'a, ()>;
82
83 #[cfg(feature = "hydration")]
84 fn hydrate(
85 self: Box<Self>,
86 root: BSubtree,
87 parent_scope: &AnyScope,
88 parent: Element,
89 fragment: &mut Fragment,
90 prev_next_sibling: &mut Option<DynamicDomSlot>,
91 ) -> (Box<dyn Scoped>, DynamicDomSlot);
92}
93
94pub(crate) struct PropsWrapper<COMP: BaseComponent> {
95 props: Rc<COMP::Properties>,
96 scope_ref: Option<Shared<Option<Scope<COMP>>>>,
97}
98
99impl<COMP: BaseComponent> PropsWrapper<COMP> {
100 pub fn new(props: Rc<COMP::Properties>) -> Self {
101 Self {
102 props,
103 scope_ref: None,
104 }
105 }
106
107 pub fn new_with_ref(
108 props: Rc<COMP::Properties>,
109 scope_ref: Shared<Option<Scope<COMP>>>,
110 ) -> Self {
111 Self {
112 props,
113 scope_ref: Some(scope_ref),
114 }
115 }
116}
117
118impl<COMP: BaseComponent> Mountable for PropsWrapper<COMP> {
119 fn copy(&self) -> Box<dyn Mountable> {
120 let wrapper: PropsWrapper<COMP> = PropsWrapper {
121 props: Rc::clone(&self.props),
122 scope_ref: self.scope_ref.clone(),
123 };
124 Box::new(wrapper)
125 }
126
127 fn as_any(&self) -> &dyn Any {
128 self
129 }
130
131 fn mountable_eq(&self, rhs: &dyn Mountable) -> bool {
132 rhs.as_any()
133 .downcast_ref::<Self>()
134 .map(|rhs| self.props == rhs.props)
135 .unwrap_or(false)
136 }
137
138 #[cfg(feature = "csr")]
139 fn mount(
140 self: Box<Self>,
141 root: &BSubtree,
142 parent_scope: &AnyScope,
143 parent: Element,
144 slot: DomSlot,
145 ) -> (Box<dyn Scoped>, DynamicDomSlot) {
146 let scope: Scope<COMP> = Scope::new(Some(parent_scope.clone()));
147 if let Some(scope_ref) = self.scope_ref {
148 *scope_ref.borrow_mut() = Some(scope.clone());
149 }
150 let own_slot = scope.mount_in_place(root.clone(), parent, slot, self.props);
151
152 (Box::new(scope), own_slot)
153 }
154
155 #[cfg(feature = "csr")]
156 fn reuse(self: Box<Self>, scope: &dyn Scoped, slot: DomSlot) {
157 let scope: Scope<COMP> = scope.to_any().downcast::<COMP>();
158 scope.reuse(self.props, slot);
159 }
160
161 #[cfg(feature = "ssr")]
162 fn render_into_stream<'a>(
163 &'a self,
164 w: &'a mut BufWriter,
165 parent_scope: &'a AnyScope,
166 hydratable: bool,
167 parent_vtag_kind: VTagKind,
168 ) -> LocalBoxFuture<'a, ()> {
169 let scope: Scope<COMP> = Scope::new(Some(parent_scope.clone()));
170
171 async move {
172 scope
173 .render_into_stream(w, self.props.clone(), hydratable, parent_vtag_kind)
174 .await;
175 }
176 .boxed_local()
177 }
178
179 #[cfg(feature = "hydration")]
180 fn hydrate(
181 self: Box<Self>,
182 root: BSubtree,
183 parent_scope: &AnyScope,
184 parent: Element,
185 fragment: &mut Fragment,
186 prev_next_sibling: &mut Option<DynamicDomSlot>,
187 ) -> (Box<dyn Scoped>, DynamicDomSlot) {
188 let scope: Scope<COMP> = Scope::new(Some(parent_scope.clone()));
189 let own_slot =
190 scope.hydrate_in_place(root, parent, fragment, self.props, prev_next_sibling);
191
192 (Box::new(scope), own_slot)
193 }
194}
195
196pub struct VChild<COMP: BaseComponent> {
198 pub props: Rc<COMP::Properties>,
200 key: Option<Key>,
202}
203
204impl<COMP: BaseComponent> implicit_clone::ImplicitClone for VChild<COMP> {}
205
206impl<COMP: BaseComponent> Clone for VChild<COMP> {
207 fn clone(&self) -> Self {
208 VChild {
209 props: Rc::clone(&self.props),
210 key: self.key.clone(),
211 }
212 }
213}
214
215impl<COMP: BaseComponent> PartialEq for VChild<COMP>
216where
217 COMP::Properties: PartialEq,
218{
219 fn eq(&self, other: &VChild<COMP>) -> bool {
220 self.props == other.props
221 }
222}
223
224impl<COMP> VChild<COMP>
225where
226 COMP: BaseComponent,
227{
228 pub fn new(props: COMP::Properties, key: Option<Key>) -> Self {
230 Self {
231 props: Rc::new(props),
232 key,
233 }
234 }
235}
236
237impl<COMP> VChild<COMP>
238where
239 COMP: BaseComponent,
240 COMP::Properties: Clone,
241{
242 pub fn get_mut(&mut self) -> &mut COMP::Properties {
244 Rc::make_mut(&mut self.props)
245 }
246}
247
248impl<COMP> From<VChild<COMP>> for VComp
249where
250 COMP: BaseComponent,
251{
252 fn from(vchild: VChild<COMP>) -> Self {
253 VComp::new::<COMP>(vchild.props, vchild.key)
254 }
255}
256
257impl VComp {
258 pub fn new<COMP>(props: Rc<COMP::Properties>, key: Option<Key>) -> Self
260 where
261 COMP: BaseComponent,
262 {
263 VComp {
264 type_id: TypeId::of::<COMP>(),
265 mountable: Box::new(PropsWrapper::<COMP>::new(props)),
266 key,
267 _marker: 0,
268 }
269 }
270
271 pub(crate) fn new_with_ref<COMP: BaseComponent>(
273 props: Rc<COMP::Properties>,
274 scope_ref: Shared<Option<Scope<COMP>>>,
275 ) -> Self {
276 VComp {
277 type_id: TypeId::of::<COMP>(),
278 mountable: Box::new(PropsWrapper::<COMP>::new_with_ref(props, scope_ref)),
279 key: None,
280 _marker: 0,
281 }
282 }
283}
284
285impl PartialEq for VComp {
286 fn eq(&self, other: &VComp) -> bool {
287 self.key == other.key
288 && self.type_id == other.type_id
289 && self.mountable.mountable_eq(other.mountable.as_ref())
290 }
291}
292
293impl<COMP: BaseComponent> fmt::Debug for VChild<COMP> {
294 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
295 f.write_str("VChild<_>")
296 }
297}
298
299#[cfg(feature = "ssr")]
300mod feat_ssr {
301 use super::*;
302 use crate::html::AnyScope;
303
304 impl VComp {
305 #[inline]
306 pub(crate) async fn render_into_stream(
307 &self,
308 w: &mut BufWriter,
309 parent_scope: &AnyScope,
310 hydratable: bool,
311 parent_vtag_kind: VTagKind,
312 ) {
313 self.mountable
314 .as_ref()
315 .render_into_stream(w, parent_scope, hydratable, parent_vtag_kind)
316 .await;
317 }
318 }
319}
320
321#[cfg(all(test, not(target_arch = "wasm32"), feature = "ssr"))]
322mod ssr_tests {
323 use tokio::test;
324
325 use crate::prelude::*;
326 use crate::ServerRenderer;
327
328 #[test]
329 async fn test_props() {
330 #[derive(PartialEq, Properties, Debug)]
331 struct ChildProps {
332 name: String,
333 }
334
335 #[component]
336 fn Child(props: &ChildProps) -> Html {
337 html! { <div>{"Hello, "}{&props.name}{"!"}</div> }
338 }
339
340 #[component]
341 fn Comp() -> Html {
342 html! {
343 <div>
344 <Child name="Jane" />
345 <Child name="John" />
346 <Child name="Josh" />
347 </div>
348 }
349 }
350
351 let s = ServerRenderer::<Comp>::new()
352 .hydratable(false)
353 .render()
354 .await;
355
356 assert_eq!(
357 s,
358 "<div><div>Hello, Jane!</div><div>Hello, John!</div><div>Hello, Josh!</div></div>"
359 );
360 }
361}