1use std::fmt::Write as _;
2
3#[derive(Debug, Clone, PartialEq)]
4pub(crate) enum Node {
5 Text {
6 content: String,
7 },
8 Element {
9 name: String,
10 attrs: Vec<(String, String)>,
11 children: Vec<Node>,
12 },
13 Comment {
14 content: String,
15 },
16 Document {
17 children: Vec<Node>,
18 },
19 Doctype {
20 doctype: String,
21 },
22}
23
24fn html_text_safe(s: &str) -> String {
25 s.replace('&', "&")
26 .replace('<', "<")
27 .replace('>', ">")
28}
29
30fn push_attr_value(s: &mut String, value: &String) {
31 match (value.contains('\''), value.contains('"')) {
33 (true, true) => {
34 s.push('\'');
35 s.push_str(&value.replace("'", "'"));
36 s.push('\'');
37 }
38 (true, false) => {
39 s.push('"');
40 s.push_str(&value);
41 s.push('"');
42 }
43 (false, true) => {
44 s.push('\'');
45 s.push_str(&value);
46 s.push('\'');
47 }
48 (false, false) => {
49 s.push('\'');
50 s.push_str(&value);
51 s.push('\'');
52 }
53 };
54}
55
56fn push_node_as_string(s: &mut String, n: &Node) {
57 match n {
58 Node::Doctype { doctype } => {
59 s.push_str("<!DOCTYPE ");
60 s.push_str(&html_text_safe(doctype));
61 s.push('>')
62 }
63 Node::Comment { content } => write!(s, "<!--{}-->", html_text_safe(content)).unwrap(),
64 Node::Text { content } => s.push_str(&html_text_safe(content)),
65 Node::Element {
66 name,
67 attrs,
68 children,
69 } => {
70 if name == "pl-template" {
71 for child in children {
72 push_node_as_string(s, child);
73 }
74 return;
75 }
76 s.push('<');
77 s.push_str(name);
78
79 for (key, value) in attrs {
80 s.push(' ');
81 s.push_str(key);
82 s.push('=');
83 push_attr_value(s, value);
84 }
85
86 match name.as_str() {
87 "area" | "base" | "br" | "col" | "embed" | "hr" | "img" | "input" | "link"
88 | "meta" | "param" | "source" | "track" | "wbr" => {
89 s.push('>');
90 }
91 _ => {
92 s.push('>');
93 for child in children {
94 push_node_as_string(s, child);
95 }
96 s.push('<');
97 s.push('/');
98 s.push_str(name);
99 s.push('>');
100 }
101 }
102 }
103 Node::Document { children } => {
104 for child in children {
105 push_node_as_string(s, child);
106 }
107 }
108 }
109}
110
111impl Node {
112 pub(crate) fn to_string(&self) -> String {
113 let mut buf = String::with_capacity(1000);
114 push_node_as_string(&mut buf, self);
115 buf
116 }
117}