1pub fn wrap_xml(inner: &str) -> String {
3 format!("<?xml version=\"1.0\" encoding=\"UTF-8\"?>{inner}")
4}
5
6pub fn xml_escape(s: &str) -> String {
11 let mut out = String::with_capacity(s.len());
12 for c in s.chars() {
13 match c {
14 '&' => out.push_str("&"),
15 '<' => out.push_str("<"),
16 '>' => out.push_str(">"),
17 '"' => out.push_str("""),
18 '\'' => out.push_str("'"),
19 c if (c as u32) < 0x20 && c != '\t' && c != '\n' && c != '\r' => {
22 out.push_str(&format!("&#x{:X};", c as u32));
23 }
24 c => out.push(c),
25 }
26 }
27 out
28}
29
30#[cfg(test)]
31mod tests {
32 use super::*;
33
34 #[test]
35 fn wrap_xml_prepends_declaration() {
36 let out = wrap_xml("<foo/>");
37 assert!(out.starts_with("<?xml"));
38 assert!(out.contains("<foo/>"));
39 }
40
41 #[test]
42 fn xml_escape_standard_entities() {
43 assert_eq!(
44 xml_escape("a&b<c>d\"e'f"),
45 "a&b<c>d"e'f"
46 );
47 }
48
49 #[test]
50 fn xml_escape_preserves_whitespace() {
51 assert_eq!(xml_escape("a\tb\nc\rd"), "a\tb\nc\rd");
52 }
53
54 #[test]
55 fn xml_escape_control_chars() {
56 let input = "a\x01b";
57 let out = xml_escape(input);
58 assert_eq!(out, "ab");
59 }
60
61 #[test]
62 fn xml_escape_plain_chars() {
63 assert_eq!(xml_escape("hello"), "hello");
64 }
65}