//! XML escaping. /// Escape text for an element's content. pub fn text(value: &str) -> String { let mut out = String::with_capacity(value.len()); for ch in value.chars() { match ch { '&' => out.push_str("&"), '<' => out.push_str("<"), '>' => out.push_str(">"), _ => out.push(ch), } } out } /// Escape text for an attribute value, which is always double-quoted here. pub fn attr(value: &str) -> String { let mut out = String::with_capacity(value.len()); for ch in value.chars() { match ch { '&' => out.push_str("&"), '<' => out.push_str("<"), '>' => out.push_str(">"), '"' => out.push_str("""), _ => out.push(ch), } } out } /// Escape text for WML element content. #[cfg(feature = "wml")] /// /// WML reads `$` as the start of a variable reference, so a literal one has to be /// doubled; otherwise a handset would substitute something or fail to parse. pub fn wml(value: &str) -> String { text(value).replace('$', "$$") } /// Escape text for a WML attribute value. #[cfg(feature = "wml")] pub fn wml_attr(value: &str) -> String { attr(value).replace('$', "$$") } #[cfg(test)] mod tests { use super::*; #[test] fn escapes_markup_characters() { assert_eq!(text("a & b < c > d"), "a & b < c > d"); // A quote is only significant inside an attribute, so content keeps it. assert_eq!(text("say \"hi\""), "say \"hi\""); assert_eq!(attr("say \"hi\" & bye"), "say "hi" & bye"); } #[test] #[cfg(feature = "wml")] fn wml_doubles_a_literal_dollar() { assert_eq!(wml("Permit fee: $5"), "Permit fee: $$5"); assert_eq!(wml("a & $b"), "a & $$b"); assert_eq!(wml_attr("/x?a=1&b=$2"), "/x?a=1&b=$$2"); // The doubling runs after the markup escape, so an escaped entity is not // itself rewritten. assert_eq!(wml("<$>"), "<$$>"); } #[test] fn leaves_ordinary_text_alone() { assert_eq!(text("plain text 123"), "plain text 123"); } }