pulldown_html_ext/
utils.rs1use pulldown_cmark_escape::StrWrite;
4pub fn escape_html(output: &mut String, text: &str) {
19 for c in text.chars() {
21 match c {
22 '<' => output.push_str("<"),
23 '>' => output.push_str(">"),
24 '"' => output.push_str("""),
25 '&' => output.push_str("&"),
26 '\'' => output.push_str("'"),
27 _ => output.push(c),
28 }
29 }
30}
31
32pub fn escape_href(output: &mut String, href: &str) {
47 for c in href.chars() {
48 match c {
49 '<' | '>' | '"' | '\'' | ' ' | '\n' | '\r' | '\t' => {
50 write!(output, "%{:02X}", c as u32).unwrap();
51 }
52 c => output.push(c),
53 }
54 }
55}
56
57pub fn sanitize_id(text: &str) -> String {
73 text.chars()
74 .map(|c| {
75 if c.is_alphanumeric() {
76 c.to_ascii_lowercase()
77 } else {
78 '-'
79 }
80 })
81 .collect::<String>()
82 .split('-')
83 .filter(|s| !s.is_empty())
84 .collect::<Vec<&str>>()
85 .join("-")
86}
87
88pub fn unicode_length(text: &str) -> usize {
104 text.chars().count()
105}
106
107#[cfg(test)]
108mod tests {
109 use super::*;
110
111 #[test]
112 fn test_escape_html() {
113 let mut output = String::new();
114 escape_html(&mut output, "<div class=\"test\">&");
115 assert_eq!(output, "<div class="test">&");
116 }
117
118 #[test]
119 fn test_escape_href() {
120 let mut output = String::new();
121 escape_href(
122 &mut output,
123 "https://example.com/path with spaces?q=test&x=1",
124 );
125 assert!(output.contains("%20"));
126 assert!(!output.contains(' '));
127 assert!(output.contains('&')); }
129
130 #[test]
131 fn test_sanitize_id() {
132 assert_eq!(sanitize_id("Hello World!"), "hello-world");
133 assert_eq!(sanitize_id("Test 123"), "test-123");
134 assert_eq!(sanitize_id("Multiple Spaces"), "multiple-spaces");
135 assert_eq!(sanitize_id("special@#chars"), "special-chars");
136 assert_eq!(sanitize_id("--multiple---dashes--"), "multiple-dashes");
137 }
138
139 #[test]
140 fn test_unicode_length() {
141 assert_eq!(unicode_length("Hello"), 5);
142 assert_eq!(unicode_length("👋 Hello"), 7);
143 assert_eq!(unicode_length("汉å—"), 2);
144 assert_eq!(unicode_length(""), 0);
145 }
146
147 #[test]
148 fn test_complex_escaping() {
149 let mut output = String::new();
150 escape_html(&mut output, "<script>alert('xss')</script>");
151 assert_eq!(
152 output,
153 "<script>alert('xss')</script>"
154 );
155 }
156
157 #[test]
158 fn test_href_special_chars() {
159 let mut output = String::new();
160 escape_href(&mut output, "/path/with\"quotes'and<brackets>");
161 assert!(output.contains("%22")); assert!(output.contains("%27")); assert!(output.contains("%3C")); assert!(output.contains("%3E")); }
166}