use std::pin::Pin; use std::collections::HashMap; use std::future::Future; use futures::future::join_all; pub trait Component { fn render(self: Box) -> Vec; } pub enum RenderNode { Suspense { fallback: Box, children: Pin>>> }, Component(Box), Element { name: String, attributes: HashMap, children: Vec }, Fragment { children: Vec }, TextNode { content: String, }, Null, } impl RenderNode { pub(crate) fn render_to_string(self) -> Pin>> { match self { RenderNode::Component(component) => { let elements = component.render(); Box::pin((async move || { join_all(elements.into_iter() .map(|child| child.render_to_string()) .collect::>()).await.join("") })()) }, RenderNode::Suspense {fallback: _, children} => { Box::pin((async move || { join_all(children.await.into_iter() .map(|child| child.render_to_string())).await .join("") })()) }, RenderNode::Element { name, attributes, children } => { let text_attributes = attributes.into_iter() .map(|(key, value)| format!(" {key}=\"{value}\"")) .collect::>().join(""); Box::pin((async move || { let rendered_children = join_all(children.into_iter() .map(|child| child.render_to_string()) .collect::>()).await.join(""); format!("<{name}{text_attributes}>{rendered_children}") })()) }, RenderNode::Fragment { children } => { Box::pin((async move || { join_all(children.into_iter() .map(|child| child.render_to_string())).await .join("") })()) } RenderNode::TextNode { content } => Box::pin((async move || content)()), RenderNode::Null => Box::pin((async move || "".to_string())()), } } }