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, 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 ImplicitClone for VNode {}
40
41impl VNode {
42 pub fn key(&self) -> Option<&Key> {
43 match self {
44 VNode::VComp(vcomp) => vcomp.key.as_ref(),
45 VNode::VList(vlist) => vlist.key.as_ref(),
46 VNode::VRef(_) => None,
47 VNode::VTag(vtag) => vtag.key.as_ref(),
48 VNode::VText(_) => None,
49 VNode::VPortal(vportal) => vportal.node.key(),
50 VNode::VSuspense(vsuspense) => vsuspense.key.as_ref(),
51 VNode::VRaw(_) => None,
52 }
53 }
54
55 pub fn has_key(&self) -> bool {
57 self.key().is_some()
58 }
59
60 pub fn to_vlist_mut(&mut self) -> &mut VList {
64 loop {
65 match *self {
66 Self::VList(ref mut m) => return Rc::make_mut(m),
67 _ => {
68 *self =
69 VNode::VList(Rc::new(VList::with_children(vec![mem::take(self)], None)));
70 }
71 }
72 }
73 }
74
75 pub fn from_html_unchecked(html: AttrValue) -> Self {
106 VNode::VRaw(VRaw { html })
107 }
108}
109
110impl Default for VNode {
111 fn default() -> Self {
112 VNode::VList(Rc::new(VList::default()))
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::with_children(
176 iter.into_iter().map(|n| n.into()).collect(),
177 None,
178 )))
179 }
180}
181
182impl fmt::Debug for VNode {
183 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
184 match *self {
185 VNode::VTag(ref vtag) => vtag.fmt(f),
186 VNode::VText(ref vtext) => vtext.fmt(f),
187 VNode::VComp(ref vcomp) => vcomp.fmt(f),
188 VNode::VList(ref vlist) => vlist.fmt(f),
189 VNode::VRef(ref vref) => write!(f, "VRef ( \"{}\" )", crate::utils::print_node(vref)),
190 VNode::VPortal(ref vportal) => vportal.fmt(f),
191 VNode::VSuspense(ref vsuspense) => vsuspense.fmt(f),
192 VNode::VRaw(ref vraw) => write!(f, "VRaw {{ {} }}", vraw.html),
193 }
194 }
195}
196
197#[cfg(feature = "ssr")]
198mod feat_ssr {
199 use futures::future::{FutureExt, LocalBoxFuture};
200
201 use super::*;
202 use crate::feat_ssr::VTagKind;
203 use crate::html::AnyScope;
204 use crate::platform::fmt::BufWriter;
205
206 impl VNode {
207 pub(crate) fn render_into_stream<'a>(
208 &'a self,
209 w: &'a mut BufWriter,
210 parent_scope: &'a AnyScope,
211 hydratable: bool,
212 parent_vtag_kind: VTagKind,
213 ) -> LocalBoxFuture<'a, ()> {
214 async fn render_into_stream_(
215 this: &VNode,
216 w: &mut BufWriter,
217 parent_scope: &AnyScope,
218 hydratable: bool,
219 parent_vtag_kind: VTagKind,
220 ) {
221 match this {
222 VNode::VTag(vtag) => vtag.render_into_stream(w, parent_scope, hydratable).await,
223 VNode::VText(vtext) => {
224 vtext
225 .render_into_stream(w, parent_scope, hydratable, parent_vtag_kind)
226 .await
227 }
228 VNode::VComp(vcomp) => {
229 vcomp
230 .render_into_stream(w, parent_scope, hydratable, parent_vtag_kind)
231 .await
232 }
233 VNode::VList(vlist) => {
234 vlist
235 .render_into_stream(w, parent_scope, hydratable, parent_vtag_kind)
236 .await
237 }
238 VNode::VRef(_) => {
244 panic!("VRef is not possible to be rendered in to a string.")
245 }
246 VNode::VPortal(_) => {}
248 VNode::VSuspense(vsuspense) => {
249 vsuspense
250 .render_into_stream(w, parent_scope, hydratable, parent_vtag_kind)
251 .await
252 }
253
254 VNode::VRaw(vraw) => vraw.render_into_stream(w, parent_scope, hydratable).await,
255 }
256 }
257
258 async move {
259 render_into_stream_(self, w, parent_scope, hydratable, parent_vtag_kind).await
260 }.boxed_local()
261 }
262 }
263}