1#[allow(clippy::missing_panics_doc)]
4pub fn escape_string(input: &str, out: &mut String) {
5 for ch in input.chars() {
6 match ch {
7 '\\' => out.push_str("\\\\"),
8 '"' => out.push_str("\\\""),
9 '\n' => out.push_str("\\n"),
10 '\r' => out.push_str("\\r"),
11 '\t' => out.push_str("\\t"),
12 '\x20'..='\x7e' => out.push(ch),
13 _ => {
14 let mut buf = [0; 2];
15 for &mut code_unit in ch.encode_utf16(&mut buf) {
16 out.push_str("\\u");
17 out.push(char::from_digit(u32::from(code_unit) / 0x1000, 16).unwrap());
18 out.push(char::from_digit(u32::from(code_unit) % 0x1000 / 0x100, 16).unwrap());
19 out.push(char::from_digit(u32::from(code_unit) % 0x100 / 0x10, 16).unwrap());
20 out.push(char::from_digit(u32::from(code_unit) % 0x10, 16).unwrap());
21 }
22 }
23 }
24 }
25}
26
27#[cfg(test)]
28mod tests {
29 use super::*;
30
31 #[track_caller]
32 fn test_json_str(input: &str, expected: &str) {
33 let mut out = String::new();
34 escape_string(input, &mut out);
35 assert_eq!(out, expected);
36 }
37
38 #[test]
39 fn json_string_encoding() {
40 test_json_str("abc", "abc");
41 test_json_str("fancy string\n", "fancy string\\n");
42 test_json_str("\n\t\r\0\\\'\"", "\\n\\t\\r\\u0000\\\\'\\\"");
43 test_json_str("\u{1d54a}", "\\ud835\\udd4a");
44 }
45}