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