pub(crate) fn string_body(text: &str) -> String {
use std::fmt::Write as _;
let mut out = String::with_capacity(text.len());
for c in text.chars() {
match c {
'"' => out.push_str("\\\""),
'\\' => out.push_str("\\\\"),
'<' => out.push_str("\\3c "),
c if c.is_control() => {
let _ = write!(out, "\\{:x} ", u32::from(c));
}
c => out.push(c),
}
}
out
}
pub(crate) fn keyword(value: &str) -> Option<&str> {
let bare = !value.trim().is_empty()
&& value
.chars()
.all(|c| c.is_ascii_alphanumeric() || matches!(c, ' ' | '-' | '.' | '%'));
bare.then_some(value)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_string_body_cannot_close_its_string() {
assert_eq!(string_body(r#"Quasi "Mono""#), r#"Quasi \"Mono\""#);
assert_eq!(string_body(r"a\b"), r"a\\b");
assert_eq!(string_body("a\nb"), "a\\a b");
assert_eq!(string_body("plain name"), "plain name");
assert_eq!(string_body("</style>"), r"\3c /style>");
assert_eq!(string_body("</style><script>"), r"\3c /style>\3c script>");
}
#[test]
fn only_a_bare_keyword_is_a_keyword() {
assert_eq!(keyword("700"), Some("700"));
assert_eq!(keyword("100 900"), Some("100 900"));
assert_eq!(keyword("oblique 10deg"), Some("oblique 10deg"));
for hostile in ["700; } body { x: y", "normal}", "", " ", "a/*"] {
assert_eq!(keyword(hostile), None, "{hostile:?}");
}
}
}