1use super::Node;
7
8#[derive(Debug, Clone, PartialEq)]
10pub struct HtmlAttribute {
11 pub name: String,
13 pub value: String,
15}
16
17#[derive(Debug, Clone, PartialEq)]
19pub struct HtmlElement {
20 pub tag: String,
22 pub attributes: Vec<HtmlAttribute>,
24 pub children: Vec<Node>,
26 pub self_closing: bool,
28}
29
30impl HtmlElement {
31 pub fn new(tag: &str) -> Self {
33 Self {
34 tag: tag.to_string(),
35 attributes: Vec::new(),
36 children: Vec::new(),
37 self_closing: false,
38 }
39 }
40
41 pub fn with_attribute(mut self, name: &str, value: &str) -> Self {
43 self.attributes.push(HtmlAttribute {
44 name: name.to_string(),
45 value: value.to_string(),
46 });
47 self
48 }
49
50 pub fn with_attributes(mut self, attrs: Vec<(&str, &str)>) -> Self {
52 for (name, value) in attrs {
53 self.attributes.push(HtmlAttribute {
54 name: name.to_string(),
55 value: value.to_string(),
56 });
57 }
58 self
59 }
60
61 pub fn with_children(mut self, children: Vec<Node>) -> Self {
63 self.children = children;
64 self
65 }
66
67 pub fn self_closing(mut self, is_self_closing: bool) -> Self {
69 self.self_closing = is_self_closing;
70 self
71 }
72
73 pub fn tag_matches_any(&self, tags: &[String]) -> bool {
75 tags.iter().any(|tag| tag.eq_ignore_ascii_case(&self.tag))
76 }
77}
78
79pub fn escape_html(content: &str) -> String {
90 content
91 .replace('&', "&")
92 .replace('<', "<")
93 .replace('>', ">")
94 .replace('"', """)
95 .replace('\'', "'")
96}
97
98pub fn safe_html(element: HtmlElement, disallowed_tags: &[String]) -> Node {
112 if element.tag_matches_any(disallowed_tags) {
113 let mut html_text = String::new();
115 html_text.push_str(&format!("<{}", element.tag));
116
117 for attr in &element.attributes {
118 html_text.push_str(&format!(" {}=\"{}\"", attr.name, attr.value));
119 }
120
121 if element.self_closing {
122 html_text.push_str(" />");
123 } else {
124 html_text.push_str(">");
125
126 for child in &element.children {
128 html_text.push_str(&format!("{}", child));
129 }
130
131 html_text.push_str(&format!("</{}>", element.tag));
132 }
133
134 Node::Text(html_text)
135 } else {
136 Node::HtmlElement(element)
137 }
138}