use std::borrow::Cow;
use std::fmt;
#[inline]
pub fn xml_escape(s: &str) -> String {
let mut out = String::with_capacity(s.len());
write_xml_escaped(&mut out, s)
.unwrap_or_else(|_| unreachable!("fmt::Write for String is infallible"));
out
}
pub fn write_xml_escaped<W: fmt::Write>(w: &mut W, s: &str) -> fmt::Result {
let bytes = s.as_bytes();
let mut last = 0;
for (i, &b) in bytes.iter().enumerate() {
let esc = match b {
b'&' => "&",
b'<' => "<",
b'>' => ">",
b'"' => """,
b'\'' => "'",
_ => continue,
};
if last < i {
w.write_str(&s[last..i])?;
}
w.write_str(esc)?;
last = i + 1;
}
if last < s.len() {
w.write_str(&s[last..])?;
}
Ok(())
}
pub fn xml_escape_char(c: char) -> Cow<'static, str> {
match c {
'&' => Cow::Borrowed("&"),
'<' => Cow::Borrowed("<"),
'>' => Cow::Borrowed(">"),
'"' => Cow::Borrowed("""),
'\'' => Cow::Borrowed("'"),
_ => {
let mut s = String::with_capacity(c.len_utf8());
s.push(c);
Cow::Owned(s)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn xml_escape_empty_string() {
assert_eq!(xml_escape(""), "");
}
#[test]
fn xml_escape_normal_text() {
assert_eq!(xml_escape("Hello World"), "Hello World");
}
#[test]
fn xml_escape_ampersand() {
assert_eq!(xml_escape("a & b"), "a & b");
}
#[test]
fn xml_escape_less_than() {
assert_eq!(xml_escape("a < b"), "a < b");
}
#[test]
fn xml_escape_greater_than() {
assert_eq!(xml_escape("a > b"), "a > b");
}
#[test]
fn xml_escape_double_quote() {
assert_eq!(xml_escape(r#"say "hello""#), "say "hello"");
}
#[test]
fn xml_escape_single_quote() {
assert_eq!(xml_escape("it's"), "it's");
}
#[test]
fn xml_escape_all_special_chars_combined() {
assert_eq!(
xml_escape(r#"<tag attr="val's" & more>"#),
"<tag attr="val's" & more>"
);
}
#[test]
fn xml_escape_unicode_emoji() {
assert_eq!(xml_escape("Hello 🌍"), "Hello 🌍");
}
#[test]
fn xml_escape_unicode_with_special_chars() {
assert_eq!(xml_escape("🌍 & 🌎"), "🌍 & 🌎");
}
#[test]
fn xml_escape_char_special_chars() {
assert_eq!(&*xml_escape_char('&'), "&");
assert_eq!(&*xml_escape_char('<'), "<");
assert_eq!(&*xml_escape_char('>'), ">");
assert_eq!(&*xml_escape_char('"'), """);
assert_eq!(&*xml_escape_char('\''), "'");
}
#[test]
fn xml_escape_char_normal_char() {
assert_eq!(&*xml_escape_char('a'), "a");
assert_eq!(&*xml_escape_char('Z'), "Z");
}
}