summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorAshelyn Rose <git@ashen.earth>2025-04-19 18:39:22 -0600
committerAshelyn Rose <git@ashen.earth>2025-04-19 18:39:22 -0600
commit55dd47aff347ee882f375b5cf880a299e633f556 (patch)
treeb643473071db0ae1023d8b9856395ee1aeaaa507 /src
parenta35d336dc9a61fda931f4a9158205d590af87bd5 (diff)
Async rendering
Diffstat (limited to 'src')
-rw-r--r--src/lib.rs9
-rw-r--r--src/render.rs41
2 files changed, 36 insertions, 14 deletions
diff --git a/src/lib.rs b/src/lib.rs
index 717d715..0178d60 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -2,6 +2,11 @@ mod render;
 
 pub use render::{Component, RenderNode};
 
-pub fn render_tree(parent_node: RenderNode) -> String {
-    parent_node.render_to_string()
+pub async fn render_tree(parent_node: RenderNode) -> String {
+    parent_node.render_to_string().await
+}
+
+#[cfg(feature = "blocking")]
+pub fn render_tree_blocking(parent_node: RenderNode) -> String {
+    futures::executor::block_on(render_tree(parent_node))
 }
diff --git a/src/render.rs b/src/render.rs
index 33469e0..3bb6e41 100644
--- a/src/render.rs
+++ b/src/render.rs
@@ -1,10 +1,17 @@
+use std::pin::Pin;
 use std::collections::HashMap;
+use std::future::Future;
+use futures::future::join_all;
 
 pub trait Component {
     fn render(self: Box<Self>) -> Vec<RenderNode>;
 }
 
 pub enum RenderNode {
+    Suspense {
+        fallback: Box<RenderNode>,
+        children: Pin<Box<dyn Future<Output = Vec<RenderNode>>>>
+    },
     Component(Box<dyn Component>),
     Element {
         name: String,
@@ -14,19 +21,27 @@ pub enum RenderNode {
     TextNode {
         content: String,
     },
-    Portal,
     Null,
 }
 
 impl RenderNode {
-    pub(crate) fn render_to_string(self) -> String {
+    pub(crate) fn render_to_string(self) -> Pin<Box<dyn Future<Output = String>>> {
         match self {
             RenderNode::Component(component) => {
                 let elements = component.render();
-                elements.into_iter()
-                    .map(|child| child.render_to_string())
-                    .collect::<Vec<_>>().join("")
+                Box::pin((async move || {
+                    join_all(elements.into_iter()
+                        .map(|child| child.render_to_string())
+                        .collect::<Vec<_>>()).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 } => {
@@ -34,16 +49,18 @@ impl RenderNode {
                     .map(|(key, value)| format!(" {key}=\"{value}\""))
                     .collect::<Vec<_>>().join("");
 
-                let rendered_children = children.into_iter()
-                    .map(|child| child.render_to_string())
-                    .collect::<Vec<_>>().join("");
+                Box::pin((async move || {
+                    let rendered_children = join_all(children.into_iter()
+                        .map(|child| child.render_to_string())
+                        .collect::<Vec<_>>()).await.join("");
+
+                    format!("<{name}{text_attributes}>{rendered_children}</{name}>")
 
-                format!("<{name}{text_attributes}>{rendered_children}</{name}>")
+                })())
             },
 
-            RenderNode::TextNode { content } => content,
-            RenderNode::Portal => todo!(),
-            RenderNode::Null => "".to_string(),
+            RenderNode::TextNode { content } => Box::pin((async move || content)()),
+            RenderNode::Null => Box::pin((async move || "".to_string())()),
         }
     }
 }