Skip to main content

ferrum_email_render/
html_emitter.rs

1//! HTML string building utilities — entity escaping and tag emission.
2
3/// Escape text content for safe inclusion in HTML.
4///
5/// Escapes `&`, `<`, `>` which are the characters that would break HTML parsing.
6pub fn escape_text(input: &str) -> String {
7    let mut output = String::with_capacity(input.len());
8    for ch in input.chars() {
9        match ch {
10            '&' => output.push_str("&amp;"),
11            '<' => output.push_str("&lt;"),
12            '>' => output.push_str("&gt;"),
13            _ => output.push(ch),
14        }
15    }
16    output
17}
18
19/// Escape an attribute value for safe inclusion in a quoted HTML attribute.
20///
21/// Escapes `&`, `<`, `>`, `"`, and `'`.
22pub fn escape_attr(input: &str) -> String {
23    let mut output = String::with_capacity(input.len());
24    for ch in input.chars() {
25        match ch {
26            '&' => output.push_str("&amp;"),
27            '<' => output.push_str("&lt;"),
28            '>' => output.push_str("&gt;"),
29            '"' => output.push_str("&quot;"),
30            '\'' => output.push_str("&#x27;"),
31            _ => output.push(ch),
32        }
33    }
34    output
35}
36
37/// Emit the HTML5 DOCTYPE declaration.
38pub fn doctype() -> &'static str {
39    "<!DOCTYPE html>"
40}
41
42#[cfg(test)]
43mod tests {
44    use super::*;
45
46    #[test]
47    fn test_escape_text() {
48        assert_eq!(escape_text("Hello & World"), "Hello &amp; World");
49        assert_eq!(escape_text("<script>"), "&lt;script&gt;");
50        assert_eq!(escape_text("plain text"), "plain text");
51    }
52
53    #[test]
54    fn test_escape_attr() {
55        assert_eq!(escape_attr("say \"hello\""), "say &quot;hello&quot;");
56        assert_eq!(escape_attr("it's"), "it&#x27;s");
57        assert_eq!(escape_attr("a&b"), "a&amp;b");
58    }
59}