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