1use std::cmp::PartialEq;
4use std::iter::FromIterator;
5use std::rc::Rc;
6use std::{fmt, mem};
7
8use web_sys::Node;
9
10use super::{Key, VChild, VComp, VList, VPortal, VSuspense, VTag, VText};
11use crate::html::{BaseComponent, ImplicitClone};
12use crate::virtual_dom::VRaw;
13use crate::AttrValue;
14
15#[derive(Clone, ImplicitClone, PartialEq)]
17#[must_use = "html does not do anything unless returned to Yew for rendering."]
18pub enum VNode {
19 VTag(Rc<VTag>),
21 VText(VText),
23 VComp(Rc<VComp>),
25 VList(Rc<VList>),
27 VPortal(Rc<VPortal>),
29 VRef(Node),
31 VSuspense(Rc<VSuspense>),
33 VRaw(VRaw),
37}
38
39impl VNode {
40 pub fn key(&self) -> Option<&Key> {
41 match self {
42 VNode::VComp(vcomp) => vcomp.key.as_ref(),
43 VNode::VList(vlist) => vlist.key.as_ref(),
44 VNode::VRef(_) => None,
45 VNode::VTag(vtag) => vtag.key.as_ref(),
46 VNode::VText(_) => None,
47 VNode::VPortal(vportal) => vportal.node.key(),
48 VNode::VSuspense(vsuspense) => vsuspense.key.as_ref(),
49 VNode::VRaw(_) => None,
50 }
51 }
52
53 pub fn has_key(&self) -> bool {
55 self.key().is_some()
56 }
57
58 pub fn to_vlist_mut(&mut self) -> &mut VList {
62 loop {
63 match *self {
64 Self::VList(ref mut m) => return Rc::make_mut(m),
65 _ => {
66 *self = VNode::VList(Rc::new(VList::from(mem::take(self))));
67 }
68 }
69 }
70 }
71
72 pub fn from_html_unchecked(html: AttrValue) -> Self {
103 VNode::VRaw(VRaw { html })
104 }
105}
106
107impl Default for VNode {
108 fn default() -> Self {
109 thread_local! {
110 static EMPTY_VLIST: Rc<VList> = Rc::new(VList::default());
111 }
112 VNode::VList(EMPTY_VLIST.with(Rc::clone))
113 }
114}
115
116impl From<VText> for VNode {
117 #[inline]
118 fn from(vtext: VText) -> Self {
119 VNode::VText(vtext)
120 }
121}
122
123impl From<VList> for VNode {
124 #[inline]
125 fn from(vlist: VList) -> Self {
126 VNode::VList(Rc::new(vlist))
127 }
128}
129
130impl From<VTag> for VNode {
131 #[inline]
132 fn from(vtag: VTag) -> Self {
133 VNode::VTag(Rc::new(vtag))
134 }
135}
136
137impl From<VComp> for VNode {
138 #[inline]
139 fn from(vcomp: VComp) -> Self {
140 VNode::VComp(Rc::new(vcomp))
141 }
142}
143
144impl From<VSuspense> for VNode {
145 #[inline]
146 fn from(vsuspense: VSuspense) -> Self {
147 VNode::VSuspense(Rc::new(vsuspense))
148 }
149}
150
151impl From<VPortal> for VNode {
152 #[inline]
153 fn from(vportal: VPortal) -> Self {
154 VNode::VPortal(Rc::new(vportal))
155 }
156}
157
158impl<COMP> From<VChild<COMP>> for VNode
159where
160 COMP: BaseComponent,
161{
162 fn from(vchild: VChild<COMP>) -> Self {
163 VNode::VComp(Rc::new(VComp::from(vchild)))
164 }
165}
166
167impl<T: ToString> From<T> for VNode {
168 fn from(value: T) -> Self {
169 VNode::VText(VText::new(value.to_string()))
170 }
171}
172
173impl<A: Into<VNode>> FromIterator<A> for VNode {
174 fn from_iter<T: IntoIterator<Item = A>>(iter: T) -> Self {
175 VNode::VList(Rc::new(VList::from_iter(
176 iter.into_iter().map(|n| n.into()),
177 )))
178 }
179}
180
181impl fmt::Debug for VNode {
182 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
183 match *self {
184 VNode::VTag(ref vtag) => vtag.fmt(f),
185 VNode::VText(ref vtext) => vtext.fmt(f),
186 VNode::VComp(ref vcomp) => vcomp.fmt(f),
187 VNode::VList(ref vlist) => vlist.fmt(f),
188 VNode::VRef(ref vref) => write!(f, "VRef ( \"{}\" )", crate::utils::print_node(vref)),
189 VNode::VPortal(ref vportal) => vportal.fmt(f),
190 VNode::VSuspense(ref vsuspense) => vsuspense.fmt(f),
191 VNode::VRaw(ref vraw) => write!(f, "VRaw {{ {} }}", vraw.html),
192 }
193 }
194}
195
196#[cfg(feature = "ssr")]
197mod feat_ssr {
198 use futures::future::{FutureExt, LocalBoxFuture};
199
200 use super::*;
201 use crate::feat_ssr::VTagKind;
202 use crate::html::AnyScope;
203 use crate::platform::fmt::BufWriter;
204
205 impl VNode {
206 pub(crate) fn render_into_stream<'a>(
207 &'a self,
208 w: &'a mut BufWriter,
209 parent_scope: &'a AnyScope,
210 hydratable: bool,
211 parent_vtag_kind: VTagKind,
212 ) -> LocalBoxFuture<'a, ()> {
213 async fn render_into_stream_(
214 this: &VNode,
215 w: &mut BufWriter,
216 parent_scope: &AnyScope,
217 hydratable: bool,
218 parent_vtag_kind: VTagKind,
219 ) {
220 match this {
221 VNode::VTag(vtag) => vtag.render_into_stream(w, parent_scope, hydratable).await,
222 VNode::VText(vtext) => {
223 vtext
224 .render_into_stream(w, parent_scope, hydratable, parent_vtag_kind)
225 .await
226 }
227 VNode::VComp(vcomp) => {
228 vcomp
229 .render_into_stream(w, parent_scope, hydratable, parent_vtag_kind)
230 .await
231 }
232 VNode::VList(vlist) => {
233 vlist
234 .render_into_stream(w, parent_scope, hydratable, parent_vtag_kind)
235 .await
236 }
237 VNode::VRef(_) => {
243 panic!("VRef is not possible to be rendered in to a string.")
244 }
245 VNode::VPortal(_) => {}
247 VNode::VSuspense(vsuspense) => {
248 vsuspense
249 .render_into_stream(w, parent_scope, hydratable, parent_vtag_kind)
250 .await
251 }
252
253 VNode::VRaw(vraw) => vraw.render_into_stream(w, parent_scope, hydratable).await,
254 }
255 }
256
257 async move {
258 render_into_stream_(self, w, parent_scope, hydratable, parent_vtag_kind).await
259 }.boxed_local()
260 }
261 }
262}