1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
use std::fmt::Debug;
use crate::Node;
use crate::common::utils::escape_html;
pub trait Renderer {
fn open(&mut self, tag: &str, attrs: &[(&str, String)]);
fn close(&mut self, tag: &str);
fn self_close(&mut self, tag: &str, attrs: &[(&str, String)]);
fn contents(&mut self, nodes: &[Node]);
fn cr(&mut self);
fn text(&mut self, text: &str);
fn text_raw(&mut self, text: &str);
}
#[derive(Debug, Default)]
pub(crate) struct HTMLRenderer<const XHTML: bool> {
result: String,
}
impl<const XHTML: bool> HTMLRenderer<XHTML> {
pub fn new() -> Self {
Self {
result: String::new(),
}
}
pub fn render(&mut self, node: &Node) {
node.node_value.render(node, self);
}
fn make_attr(&mut self, name: &str, value: &str) {
self.result.push(' ');
self.result.push_str(&escape_html(name));
self.result.push('=');
self.result.push('"');
self.result.push_str(&escape_html(value));
self.result.push('"');
}
fn make_attrs(&mut self, attrs: &[(&str, String)]) {
for (name, value) in attrs {
self.make_attr(name, value);
}
}
}
impl<const XHTML: bool> From<HTMLRenderer<XHTML>> for String {
fn from(f: HTMLRenderer<XHTML>) -> Self {
#[cold]
fn replace_null(input: String) -> String {
input.replace('\0', "\u{FFFD}")
}
if f.result.contains('\0') {
replace_null(f.result)
} else {
f.result
}
}
}
impl<const XHTML: bool> Renderer for HTMLRenderer<XHTML> {
fn open(&mut self, tag: &str, attrs: &[(&str, String)]) {
self.result.push('<');
self.result.push_str(tag);
self.make_attrs(attrs);
self.result.push('>');
}
fn close(&mut self, tag: &str) {
self.result.push('<');
self.result.push('/');
self.result.push_str(tag);
self.result.push('>');
}
fn self_close(&mut self, tag: &str, attrs: &[(&str, String)]) {
self.result.push('<');
self.result.push_str(tag);
self.make_attrs(attrs);
if XHTML {
self.result.push(' ');
self.result.push('/');
}
self.result.push('>');
}
fn contents(&mut self, nodes: &[Node]) {
for node in nodes.iter() {
self.render(node);
}
}
fn cr(&mut self) {
match self.result.as_bytes().last() {
Some(b'\n') | None => {}
Some(_) => self.result.push('\n')
}
}
fn text(&mut self, text: &str) {
self.result.push_str(&escape_html(text));
}
fn text_raw(&mut self, text: &str) {
self.result.push_str(text);
}
}