use crate::String;
pub fn push_js_string_literal(output: &mut String, value: &str) {
output.push('"');
let mut segment_start = 0usize;
for (index, char) in value.char_indices() {
let escaped = match char {
'"' => Some("\\\""),
'\\' => Some("\\\\"),
'\n' => Some("\\n"),
'\r' => Some("\\r"),
'\t' => Some("\\t"),
'\u{08}' => Some("\\b"),
'\u{0c}' => Some("\\f"),
'\u{00}'..='\u{1f}' => {
output.push_str(value.get(segment_start..index).unwrap_or_default());
push_control_escape(output, char as u8);
segment_start = index + char.len_utf8();
None
}
_ => None,
};
if let Some(escaped) = escaped {
output.push_str(value.get(segment_start..index).unwrap_or_default());
output.push_str(escaped);
segment_start = index + char.len_utf8();
}
}
output.push_str(value.get(segment_start..).unwrap_or_default());
output.push('"');
}
fn push_control_escape(output: &mut String, byte: u8) {
output.push_str("\\u00");
for nibble in [byte >> 4, byte & 0x0f] {
output.push(char::from_digit(u32::from(nibble), 16).unwrap_or('0'));
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn escapes_js_string_literal_controls() {
let mut output = String::default();
push_js_string_literal(&mut output, "a\"b\\c\n\u{01}");
assert_eq!(output.as_str(), r#""a\"b\\c\n\u0001""#);
}
}