1use proc_macro2::{Span, TokenStream};
4use quote::ToTokens;
5use std::fmt;
6use syn::{
7 punctuated::Punctuated, spanned::Spanned, token::Colon, Expr, ExprBlock, ExprPath, Ident, Lit,
8};
9
10use crate::punctuation::Dash;
11
12#[derive(Debug)]
14pub struct Node {
15 pub name: Option<NodeName>,
25
26 pub node_type: NodeType,
28
29 pub value: Option<Expr>,
39
40 pub attributes: Vec<Node>,
42
43 pub children: Vec<Node>,
45}
46
47impl Node {
48 pub fn name_as_string(&self) -> Option<String> {
50 match self.name.as_ref() {
51 Some(NodeName::Block(_)) => None,
52 Some(name) => Some(name.to_string()),
53 None => None,
54 }
55 }
56
57 pub fn name_as_block(&self) -> Option<ExprBlock> {
59 match self.name.as_ref() {
60 Some(NodeName::Block(Expr::Block(expr))) => Some(expr.to_owned()),
61 _ => None,
62 }
63 }
64
65 pub fn name_span(&self) -> Option<Span> {
67 self.name.as_ref().map(|name| name.span())
68 }
69
70 pub fn value_as_string(&self) -> Option<String> {
72 match self.value.as_ref() {
73 Some(Expr::Lit(expr)) => match &expr.lit {
74 Lit::Str(lit_str) => Some(lit_str.value()),
75 _ => None,
76 },
77 Some(Expr::Path(expr)) => Some(path_to_string(expr)),
78 _ => None,
79 }
80 }
81
82 pub fn value_as_block(&self) -> Option<ExprBlock> {
84 match self.value.as_ref() {
85 Some(Expr::Block(expr)) => Some(expr.to_owned()),
86 _ => None,
87 }
88 }
89}
90
91#[derive(Debug, Clone, PartialEq)]
94pub enum NodeType {
95 Element,
98
99 Attribute,
101
102 Text,
108
109 Comment,
112
113 Doctype,
116
117 Fragment,
119
120 Block,
122}
123
124impl fmt::Display for NodeType {
125 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
126 write!(
127 f,
128 "{}",
129 match self {
130 Self::Element => "NodeType::Element",
131 Self::Attribute => "NodeType::Attribute",
132 Self::Text => "NodeType::Text",
133 Self::Comment => "NodeType::Comment",
134 Self::Doctype => "NodeType::Doctype",
135 Self::Fragment => "NodeType::Fragment",
136 Self::Block => "NodeType::Block",
137 }
138 )
139 }
140}
141
142#[derive(Debug)]
144pub enum NodeName {
145 Path(ExprPath),
148
149 Dash(Punctuated<Ident, Dash>),
151
152 Colon(Punctuated<Ident, Colon>),
154
155 Block(Expr),
157}
158
159impl NodeName {
160 pub fn span(&self) -> Span {
162 match self {
163 NodeName::Path(name) => name.span(),
164 NodeName::Dash(name) => name.span(),
165 NodeName::Colon(name) => name.span(),
166 NodeName::Block(name) => name.span(),
167 }
168 }
169}
170
171impl fmt::Display for NodeName {
172 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
173 write!(
174 f,
175 "{}",
176 match self {
177 NodeName::Path(expr) => path_to_string(expr),
178 NodeName::Dash(name) => name
179 .iter()
180 .map(|ident| ident.to_string())
181 .collect::<Vec<String>>()
182 .join("-"),
183 NodeName::Colon(name) => name
184 .iter()
185 .map(|ident| ident.to_string())
186 .collect::<Vec<String>>()
187 .join(":"),
188 NodeName::Block(_) => String::from("{}"),
189 }
190 )
191 }
192}
193
194impl PartialEq for NodeName {
195 fn eq(&self, other: &NodeName) -> bool {
196 match self {
197 Self::Path(this) => match other {
198 Self::Path(other) => this == other,
199 _ => false,
200 },
201 Self::Dash(this) => match other {
202 Self::Dash(other) => this == other,
203 _ => false,
204 },
205 Self::Colon(this) => match other {
206 Self::Colon(other) => this == other,
207 _ => false,
208 },
209 Self::Block(this) => match other {
210 Self::Block(other) => this == other,
211 _ => false,
212 },
213 }
214 }
215}
216
217impl ToTokens for NodeName {
218 fn to_tokens(&self, tokens: &mut TokenStream) {
219 match self {
220 NodeName::Path(name) => name.to_tokens(tokens),
221 NodeName::Dash(name) => name.to_tokens(tokens),
222 NodeName::Colon(name) => name.to_tokens(tokens),
223 NodeName::Block(name) => name.to_tokens(tokens),
224 }
225 }
226}
227
228fn path_to_string(expr: &ExprPath) -> String {
229 expr.path
230 .segments
231 .iter()
232 .map(|segment| segment.ident.to_string())
233 .collect::<Vec<String>>()
234 .join("::")
235}