1use crate::parsing::is_component_node;
2use anyhow::Result;
3use quote::ToTokens;
4use rstml::node::{Node, NodeAttribute};
5use serde::{Deserialize, Serialize};
6
7#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
13pub enum LNode {
14 Fragment(Vec<LNode>),
15 Text(String),
16 Element {
17 name: String,
18 attrs: Vec<(String, LAttributeValue)>,
19 children: Vec<LNode>,
20 },
21 Component {
24 name: String,
25 props: Vec<(String, String)>,
26 children: Vec<LNode>,
27 },
28 DynChild(String),
29}
30
31#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
32pub enum LAttributeValue {
33 Boolean,
34 Static(String),
35 Dynamic,
37 Noop,
38}
39
40impl LNode {
41 pub fn parse_view(nodes: Vec<Node>) -> Result<LNode> {
42 let mut out = Vec::new();
43 for node in nodes {
44 LNode::parse_node(node, &mut out)?;
45 }
46 if out.len() == 1 {
47 Ok(out.pop().unwrap())
48 } else {
49 Ok(LNode::Fragment(out))
50 }
51 }
52
53 pub fn parse_node(node: Node, views: &mut Vec<LNode>) -> Result<()> {
54 match node {
55 Node::Fragment(frag) => {
56 for child in frag.children {
57 LNode::parse_node(child, views)?;
58 }
59 }
60 Node::Text(text) => {
61 views.push(LNode::Text(text.value_string()));
62 }
63 Node::Block(block) => {
64 let code = block.into_token_stream();
65 let code = code.to_string();
66 views.push(LNode::DynChild(code));
67 }
68 Node::Element(el) => {
69 if is_component_node(&el) {
70 let name = el.name().to_string();
71 let mut children = Vec::new();
72 for child in el.children {
73 LNode::parse_node(child, &mut children)?;
74 }
75 views.push(LNode::Component {
76 name,
77 props: el
78 .open_tag
79 .attributes
80 .into_iter()
81 .filter_map(|attr| match attr {
82 NodeAttribute::Attribute(attr) => Some((attr.key.to_string(), format!("{:#?}", attr.value()))),
83 _ => None,
84 })
85 .collect(),
86 children,
87 });
88 } else {
89 let name = el.name().to_string();
90 let mut attrs = Vec::new();
91
92 for attr in el.open_tag.attributes {
93 if let NodeAttribute::Attribute(attr) = attr {
94 let name = attr.key.to_string();
95 if let Some(value) = attr.value_literal_string() {
96 attrs.push((name, LAttributeValue::Static(value)));
97 } else {
98 attrs.push((name, LAttributeValue::Dynamic));
99 }
100 }
101 }
102
103 let mut children = Vec::new();
104 for child in el.children {
105 LNode::parse_node(child, &mut children)?;
106 }
107
108 views.push(LNode::Element { name, attrs, children });
109 }
110 }
111 _ => {}
112 }
113 Ok(())
114 }
115
116 pub fn to_html(&self) -> String {
117 match self {
118 LNode::Fragment(frag) => frag.iter().map(LNode::to_html).collect(),
119 LNode::Text(text) => text.to_owned(),
120 LNode::Component { name, .. } => format!(
121 "<!--<{name}>--><pre><{name}/> will load once Rust code \
122 has been compiled.</pre><!--</{name}>-->"
123 ),
124 LNode::DynChild(_) => "<!--<DynChild>--><pre>Dynamic content will \
125 load once Rust code has been \
126 compiled.</pre><!--</DynChild>-->"
127 .to_string(),
128 LNode::Element { name, attrs, children } => {
129 let is_self_closing = children.is_empty();
132
133 let attrs = attrs
134 .iter()
135 .filter_map(|(name, value)| match value {
136 LAttributeValue::Boolean => Some(format!("{name} ")),
137 LAttributeValue::Static(value) => Some(format!("{name}=\"{value}\" ")),
138 LAttributeValue::Dynamic => None,
139 LAttributeValue::Noop => None,
140 })
141 .collect::<String>();
142
143 let children = children.iter().map(LNode::to_html).collect::<String>();
144
145 if is_self_closing {
146 format!("<{name} {attrs}/>")
147 } else {
148 format!("<{name} {attrs}>{children}</{name}>")
149 }
150 }
151 }
152 }
153}