1use std::{convert::TryFrom, fmt, ops::Deref};
4
5use proc_macro2::{Punct, TokenStream};
6use quote::ToTokens;
7use syn::{
8 punctuated::{Pair, Punctuated},
9 Expr, ExprBlock, ExprLit, ExprPath, Ident, Lit,
10};
11
12use crate::Error;
13
14#[derive(Debug, PartialEq, Eq)]
16pub enum NodeType {
17 Element,
18 Attribute,
19 Text,
20 Comment,
21 Doctype,
22 Block,
23 Fragment,
24}
25
26impl fmt::Display for NodeType {
27 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
28 write!(
29 f,
30 "{}",
31 match self {
32 Self::Element => "NodeType::Element",
33 Self::Attribute => "NodeType::Attribute",
34 Self::Text => "NodeType::Text",
35 Self::Comment => "NodeType::Comment",
36 Self::Doctype => "NodeType::Doctype",
37 Self::Block => "NodeType::Block",
38 Self::Fragment => "NodeType::Fragment",
39 }
40 )
41 }
42}
43
44#[derive(Debug)]
46pub enum Node {
47 Element(NodeElement),
48 Attribute(NodeAttribute),
49 Text(NodeText),
50 Comment(NodeComment),
51 Doctype(NodeDoctype),
52 Block(NodeBlock),
53 Fragment(NodeFragment),
54}
55
56impl Node {
57 pub fn r#type(&self) -> NodeType {
59 match &self {
60 Self::Element(_) => NodeType::Element,
61 Self::Attribute(_) => NodeType::Attribute,
62 Self::Text(_) => NodeType::Text,
63 Self::Comment(_) => NodeType::Comment,
64 Self::Doctype(_) => NodeType::Element,
65 Self::Block(_) => NodeType::Block,
66 Self::Fragment(_) => NodeType::Fragment,
67 }
68 }
69
70 pub fn children(&self) -> Option<&Vec<Node>> {
72 match self {
73 Self::Fragment(NodeFragment { children })
74 | Self::Element(NodeElement { children, .. }) => Some(children),
75 _ => None,
76 }
77 }
78
79 pub fn children_mut(&mut self) -> Option<&mut Vec<Node>> {
81 match self {
82 Self::Fragment(NodeFragment { children })
83 | Self::Element(NodeElement { children, .. }) => Some(children),
84 _ => None,
85 }
86 }
87}
88
89impl fmt::Display for Node {
90 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
91 write!(
92 f,
93 "{}",
94 match self {
95 Self::Element(_) => "Node::Element",
96 Self::Attribute(_) => "Node::Attribute",
97 Self::Text(_) => "Node::Text",
98 Self::Comment(_) => "Node::Comment",
99 Self::Doctype(_) => "Node::Doctype",
100 Self::Block(_) => "Node::Block",
101 Self::Fragment(_) => "Node::Fragment",
102 }
103 )
104 }
105}
106
107#[derive(Debug)]
112pub struct NodeElement {
113 pub name: NodeName,
115 pub attributes: Vec<Node>,
117 pub children: Vec<Node>,
119}
120
121impl fmt::Display for NodeElement {
122 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
123 write!(f, "NodeElement")
124 }
125}
126
127#[derive(Debug)]
131pub struct NodeAttribute {
132 pub key: NodeName,
134 pub value: Option<NodeValueExpr>,
136}
137
138impl fmt::Display for NodeAttribute {
139 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
140 write!(f, "NodeAttribute")
141 }
142}
143
144#[derive(Debug)]
152pub struct NodeText {
153 pub value: NodeValueExpr,
155}
156
157impl fmt::Display for NodeText {
158 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
159 write!(f, "NodeText")
160 }
161}
162
163#[derive(Debug)]
168pub struct NodeComment {
169 pub value: NodeValueExpr,
171}
172
173impl fmt::Display for NodeComment {
174 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
175 write!(f, "NodeComment")
176 }
177}
178
179#[derive(Debug)]
184pub struct NodeDoctype {
185 pub value: NodeValueExpr,
187}
188
189impl fmt::Display for NodeDoctype {
190 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
191 write!(f, "NodeDoctype")
192 }
193}
194
195#[derive(Debug)]
199pub struct NodeFragment {
200 pub children: Vec<Node>,
202}
203
204impl fmt::Display for NodeFragment {
205 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
206 write!(f, "NodeFragment")
207 }
208}
209
210#[derive(Debug)]
214pub struct NodeBlock {
215 pub value: NodeValueExpr,
217}
218
219impl fmt::Display for NodeBlock {
220 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
221 write!(f, "NodeBlock")
222 }
223}
224
225#[derive(Debug)]
227pub enum NodeName {
228 Path(ExprPath),
231
232 Punctuated(Punctuated<Ident, Punct>),
235
236 Block(Expr),
238}
239
240impl TryFrom<&NodeName> for ExprBlock {
241 type Error = Error;
242
243 fn try_from(node: &NodeName) -> Result<Self, Self::Error> {
244 match node {
245 NodeName::Block(Expr::Block(expr)) => Ok(expr.to_owned()),
246 _ => Err(Error::TryFrom(
247 "NodeName does not match NodeName::Block(Expr::Block(_))".into(),
248 )),
249 }
250 }
251}
252
253impl PartialEq for NodeName {
254 fn eq(&self, other: &NodeName) -> bool {
255 match self {
256 Self::Path(this) => match other {
257 Self::Path(other) => this == other,
258 _ => false,
259 },
260 Self::Punctuated(this) => match other {
262 Self::Punctuated(other) => {
263 this.pairs()
264 .zip(other.pairs())
265 .all(|(this, other)| match (this, other) {
266 (
267 Pair::Punctuated(this_ident, this_punct),
268 Pair::Punctuated(other_ident, other_punct),
269 ) => {
270 this_ident == other_ident
271 && this_punct.as_char() == other_punct.as_char()
272 }
273 (Pair::End(this), Pair::End(other)) => this == other,
274 _ => false,
275 })
276 }
277 _ => false,
278 },
279 Self::Block(this) => match other {
280 Self::Block(other) => this == other,
281 _ => false,
282 },
283 }
284 }
285}
286
287impl ToTokens for NodeName {
288 fn to_tokens(&self, tokens: &mut TokenStream) {
289 match self {
290 NodeName::Path(name) => name.to_tokens(tokens),
291 NodeName::Punctuated(name) => name.to_tokens(tokens),
292 NodeName::Block(name) => name.to_tokens(tokens),
293 }
294 }
295}
296
297impl fmt::Display for NodeName {
298 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
299 write!(
300 f,
301 "{}",
302 match self {
303 NodeName::Path(expr) => path_to_string(expr),
304 NodeName::Punctuated(name) => {
305 name.pairs()
306 .flat_map(|pair| match pair {
307 Pair::Punctuated(ident, punct) => {
308 [ident.to_string(), punct.to_string()]
309 }
310 Pair::End(ident) => [ident.to_string(), "".to_string()],
311 })
312 .collect::<String>()
313 }
314 NodeName::Block(_) => String::from("{}"),
315 }
316 )
317 }
318}
319
320#[derive(Debug)]
322pub struct NodeValueExpr {
323 expr: Expr,
324}
325
326impl NodeValueExpr {
327 pub fn new(expr: Expr) -> Self {
329 Self { expr }
330 }
331}
332
333impl AsRef<Expr> for NodeValueExpr {
334 fn as_ref(&self) -> &Expr {
335 &self.expr
336 }
337}
338
339impl Deref for NodeValueExpr {
340 type Target = Expr;
341
342 fn deref(&self) -> &Self::Target {
343 &self.expr
344 }
345}
346
347impl From<Expr> for NodeValueExpr {
348 fn from(expr: Expr) -> Self {
349 Self { expr }
350 }
351}
352
353impl From<ExprLit> for NodeValueExpr {
354 fn from(expr: ExprLit) -> Self {
355 Self { expr: expr.into() }
356 }
357}
358
359impl From<ExprBlock> for NodeValueExpr {
360 fn from(expr: ExprBlock) -> Self {
361 Self { expr: expr.into() }
362 }
363}
364
365impl From<NodeValueExpr> for Expr {
366 fn from(value: NodeValueExpr) -> Self {
367 value.expr
368 }
369}
370
371impl<'a> From<&'a NodeValueExpr> for &'a Expr {
372 fn from(value: &'a NodeValueExpr) -> Self {
373 &value.expr
374 }
375}
376
377impl TryFrom<NodeValueExpr> for ExprBlock {
378 type Error = Error;
379
380 fn try_from(value: NodeValueExpr) -> Result<Self, Self::Error> {
381 if let Expr::Block(block) = value.expr {
382 Ok(block)
383 } else {
384 Err(Error::TryFrom(
385 "NodeValueExpr does not match Expr::Block(_)".into(),
386 ))
387 }
388 }
389}
390
391impl TryFrom<NodeValueExpr> for ExprLit {
392 type Error = Error;
393
394 fn try_from(value: NodeValueExpr) -> Result<Self, Self::Error> {
395 if let Expr::Lit(lit) = value.expr {
396 Ok(lit)
397 } else {
398 Err(Error::TryFrom(
399 "NodeValueExpr does not match Expr::Lit(_)".into(),
400 ))
401 }
402 }
403}
404
405impl TryFrom<&NodeValueExpr> for String {
406 type Error = Error;
407
408 fn try_from(value: &NodeValueExpr) -> Result<Self, Self::Error> {
409 match &value.expr {
410 Expr::Lit(expr) => match &expr.lit {
411 Lit::Str(lit_str) => Some(lit_str.value()),
412 _ => None,
413 },
414 Expr::Path(expr) => Some(path_to_string(&expr)),
415 _ => None,
416 }
417 .ok_or_else(|| {
418 Error::TryFrom(
419 "NodeValueExpr does not match Expr::Lit(Lit::Str(_)) or Expr::Path(_)".into(),
420 )
421 })
422 }
423}
424
425fn path_to_string(expr: &ExprPath) -> String {
426 expr.path
427 .segments
428 .iter()
429 .map(|segment| segment.ident.to_string())
430 .collect::<Vec<String>>()
431 .join("::")
432}