use std::fmt::Write as _;
pub(super) fn write_json_string(out: &mut String, s: &str) {
out.push('"');
for ch in s.chars() {
match ch {
'"' => out.push_str("\\\""),
'\\' => out.push_str("\\\\"),
'\n' => out.push_str("\\n"),
'\r' => out.push_str("\\r"),
'\t' => out.push_str("\\t"),
c if (c as u32) < 0x20 => {
let _ = write!(out, "\\u{:04x}", c as u32);
}
c => out.push(c),
}
}
out.push('"');
}
pub(super) fn write_key(out: &mut String, key: &str) {
write_json_string(out, key);
out.push(':');
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn escapes_quote_and_backslash() {
let mut buf = String::new();
write_json_string(&mut buf, "a\"b\\c");
assert_eq!(buf, "\"a\\\"b\\\\c\"");
}
#[test]
fn escapes_control_chars() {
let mut buf = String::new();
write_json_string(&mut buf, "x\ny\rz\t\x01end");
assert_eq!(buf, "\"x\\ny\\rz\\t\\u0001end\"");
}
#[test]
fn passes_through_unicode_at_or_above_0x20() {
let mut buf = String::new();
write_json_string(&mut buf, "héllo ✓");
assert_eq!(buf, "\"héllo ✓\"");
}
#[test]
fn empty_string_emits_quoted_empty() {
let mut buf = String::new();
write_json_string(&mut buf, "");
assert_eq!(buf, "\"\"");
}
#[test]
fn write_key_appends_colon() {
let mut buf = String::new();
write_key(&mut buf, "tb");
assert_eq!(buf, "\"tb\":");
}
}