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 VNode::VList(Rc::new(VList::default()))
110 }
111}
112
113impl From<VText> for VNode {
114 #[inline]
115 fn from(vtext: VText) -> Self {
116 VNode::VText(vtext)
117 }
118}
119
120impl From<VList> for VNode {
121 #[inline]
122 fn from(vlist: VList) -> Self {
123 VNode::VList(Rc::new(vlist))
124 }
125}
126
127impl From<VTag> for VNode {
128 #[inline]
129 fn from(vtag: VTag) -> Self {
130 VNode::VTag(Rc::new(vtag))
131 }
132}
133
134impl From<VComp> for VNode {
135 #[inline]
136 fn from(vcomp: VComp) -> Self {
137 VNode::VComp(Rc::new(vcomp))
138 }
139}
140
141impl From<VSuspense> for VNode {
142 #[inline]
143 fn from(vsuspense: VSuspense) -> Self {
144 VNode::VSuspense(Rc::new(vsuspense))
145 }
146}
147
148impl From<VPortal> for VNode {
149 #[inline]
150 fn from(vportal: VPortal) -> Self {
151 VNode::VPortal(Rc::new(vportal))
152 }
153}
154
155impl<COMP> From<VChild<COMP>> for VNode
156where
157 COMP: BaseComponent,
158{
159 fn from(vchild: VChild<COMP>) -> Self {
160 VNode::VComp(Rc::new(VComp::from(vchild)))
161 }
162}
163
164impl<T: ToString> From<T> for VNode {
165 fn from(value: T) -> Self {
166 VNode::VText(VText::new(value.to_string()))
167 }
168}
169
170impl<A: Into<VNode>> FromIterator<A> for VNode {
171 fn from_iter<T: IntoIterator<Item = A>>(iter: T) -> Self {
172 VNode::VList(Rc::new(VList::from_iter(
173 iter.into_iter().map(|n| n.into()),
174 )))
175 }
176}
177
178impl fmt::Debug for VNode {
179 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
180 match *self {
181 VNode::VTag(ref vtag) => vtag.fmt(f),
182 VNode::VText(ref vtext) => vtext.fmt(f),
183 VNode::VComp(ref vcomp) => vcomp.fmt(f),
184 VNode::VList(ref vlist) => vlist.fmt(f),
185 VNode::VRef(ref vref) => write!(f, "VRef ( \"{}\" )", crate::utils::print_node(vref)),
186 VNode::VPortal(ref vportal) => vportal.fmt(f),
187 VNode::VSuspense(ref vsuspense) => vsuspense.fmt(f),
188 VNode::VRaw(ref vraw) => write!(f, "VRaw {{ {} }}", vraw.html),
189 }
190 }
191}
192
193#[cfg(feature = "ssr")]
194mod feat_ssr {
195 use futures::future::{FutureExt, LocalBoxFuture};
196
197 use super::*;
198 use crate::feat_ssr::VTagKind;
199 use crate::html::AnyScope;
200 use crate::platform::fmt::BufWriter;
201
202 impl VNode {
203 pub(crate) fn render_into_stream<'a>(
204 &'a self,
205 w: &'a mut BufWriter,
206 parent_scope: &'a AnyScope,
207 hydratable: bool,
208 parent_vtag_kind: VTagKind,
209 ) -> LocalBoxFuture<'a, ()> {
210 async fn render_into_stream_(
211 this: &VNode,
212 w: &mut BufWriter,
213 parent_scope: &AnyScope,
214 hydratable: bool,
215 parent_vtag_kind: VTagKind,
216 ) {
217 match this {
218 VNode::VTag(vtag) => vtag.render_into_stream(w, parent_scope, hydratable).await,
219 VNode::VText(vtext) => {
220 vtext
221 .render_into_stream(w, parent_scope, hydratable, parent_vtag_kind)
222 .await
223 }
224 VNode::VComp(vcomp) => {
225 vcomp
226 .render_into_stream(w, parent_scope, hydratable, parent_vtag_kind)
227 .await
228 }
229 VNode::VList(vlist) => {
230 vlist
231 .render_into_stream(w, parent_scope, hydratable, parent_vtag_kind)
232 .await
233 }
234 VNode::VRef(_) => {
240 panic!("VRef is not possible to be rendered in to a string.")
241 }
242 VNode::VPortal(_) => {}
244 VNode::VSuspense(vsuspense) => {
245 vsuspense
246 .render_into_stream(w, parent_scope, hydratable, parent_vtag_kind)
247 .await
248 }
249
250 VNode::VRaw(vraw) => vraw.render_into_stream(w, parent_scope, hydratable).await,
251 }
252 }
253
254 async move {
255 render_into_stream_(self, w, parent_scope, hydratable, parent_vtag_kind).await
256 }.boxed_local()
257 }
258 }
259}