use std::fmt::Write;
pub(crate) struct CssString<'a, W: ?Sized>(pub(crate) &'a mut W);
impl<W: Write + ?Sized> Write for CssString<'_, W> {
fn write_str(&mut self, s: &str) -> std::fmt::Result {
s.chars().try_for_each(|c| self.write_char(c))
}
fn write_char(&mut self, c: char) -> std::fmt::Result {
match c {
'"' => self.0.write_str("\\\""),
'\\' => self.0.write_str("\\\\"),
c if c.is_control() => write!(self.0, "\\{:x} ", c as u32),
c => self.0.write_char(c),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn escape(s: &str) -> String {
let mut out = String::new();
CssString(&mut out).write_str(s).unwrap();
out
}
#[test]
fn passes_through_ordinary_text() {
assert_eq!(escape("Helvetica Neue"), "Helvetica Neue");
}
#[test]
fn passes_through_non_ascii() {
assert_eq!(escape("Ã¥ ä ö æ¼¢å\u{AD}"), "Ã¥ ä ö æ¼¢å\u{AD}");
}
#[test]
fn escapes_quotes_and_backslashes() {
assert_eq!(escape("a\"b\\c"), "a\\\"b\\\\c");
}
#[test]
fn escapes_control_characters_with_a_terminating_space() {
assert_eq!(escape("a\nb"), "a\\a b");
assert_eq!(escape("\t"), "\\9 ");
}
}