windjammer_ui/
simple_renderer.rs1use crate::vdom::{VElement, VNode};
5
6pub struct SimpleRenderer {
9 output: Vec<String>,
10}
11
12impl SimpleRenderer {
13 pub fn new() -> Self {
14 Self { output: Vec::new() }
15 }
16
17 pub fn render(&mut self, vnode: &VNode) {
19 self.output.clear();
20 self.render_node(vnode, 0);
21 }
22
23 fn render_node(&mut self, vnode: &VNode, depth: usize) {
24 let indent = " ".repeat(depth);
25
26 match vnode {
27 VNode::Element(element) => {
28 self.render_element(element, depth);
29 }
30 VNode::Text(text) => {
31 self.output
32 .push(format!("{}Text: {}", indent, text.content));
33 }
34 VNode::Component(component) => {
35 self.output
36 .push(format!("{}Component: {}", indent, component.name));
37 }
38 VNode::Empty => {
39 }
41 }
42 }
43
44 fn render_element(&mut self, element: &VElement, depth: usize) {
45 let indent = " ".repeat(depth);
46
47 let mut tag_str = format!("{}<{}", indent, element.tag);
49
50 for (key, value) in &element.attrs {
52 tag_str.push_str(&format!(" {}=\"{}\"", key, value));
53 }
54 tag_str.push('>');
55
56 self.output.push(tag_str);
57
58 for child in &element.children {
60 self.render_node(child, depth + 1);
61 }
62
63 self.output.push(format!("{}</{}>", indent, element.tag));
65 }
66
67 pub fn get_output(&self) -> String {
69 self.output.join("\n")
70 }
71
72 pub fn print(&self) {
74 println!("{}", self.get_output());
75 }
76}
77
78impl Default for SimpleRenderer {
79 fn default() -> Self {
80 Self::new()
81 }
82}
83
84#[cfg(test)]
85mod tests {
86 use super::*;
87 use crate::vdom::{VComponent, VElement, VText};
88 use std::collections::HashMap;
89
90 #[test]
91 fn test_render_text() {
92 let mut renderer = SimpleRenderer::new();
93 let vnode = VNode::Text(VText::new("Hello, World!"));
94
95 renderer.render(&vnode);
96 let output = renderer.get_output();
97
98 assert!(output.contains("Hello, World!"));
99 }
100
101 #[test]
102 fn test_render_element() {
103 let mut renderer = SimpleRenderer::new();
104 let element = VElement {
105 tag: "div".to_string(),
106 attrs: HashMap::new(),
107 children: vec![VNode::Text(VText::new("Content"))],
108 };
109
110 renderer.render(&VNode::Element(element));
111 let output = renderer.get_output();
112
113 assert!(output.contains("<div>"));
114 assert!(output.contains("Content"));
115 assert!(output.contains("</div>"));
116 }
117
118 #[test]
119 fn test_render_component() {
120 let mut renderer = SimpleRenderer::new();
121 let component = VComponent {
122 name: "Counter".to_string(),
123 props: HashMap::new(),
124 };
125
126 renderer.render(&VNode::Component(component));
127 let output = renderer.get_output();
128
129 assert!(output.contains("Component: Counter"));
130 }
131}
132
133#[cfg(target_arch = "wasm32")]
135pub fn render_to_html(vnode: &crate::simple_vnode::VNode) -> String {
136 use crate::simple_vnode::{VAttr, VNode};
137
138 match vnode {
139 VNode::Element {
140 tag,
141 attrs,
142 children,
143 } => {
144 let mut html = format!("<{}", tag);
145 for (key, value) in attrs {
146 match value {
147 VAttr::Static(v) | VAttr::Dynamic(v) => {
148 html.push_str(&format!(" {}=\"{}\"", key, v));
149 }
150 VAttr::Event(_) => {
151 }
153 }
154 }
155 html.push('>');
156 for child in children {
157 html.push_str(&render_to_html(child));
158 }
159 html.push_str(&format!("</{}>", tag));
160 html
161 }
162 VNode::Text(text) => text.clone(),
163 }
164}