substrate_data/forms/text/writer/
string.rs1use super::super::scan;
4use super::sink::Sink;
5
6pub fn write_string(s: &str, out: &mut String) {
8 push_string(s, out);
9}
10
11pub(super) fn push_string<S: Sink>(s: &str, out: &mut S) {
13 out.text("\"");
14 write_body(s, out);
15 out.text("\"");
16}
17
18pub(super) fn write_body<S: Sink>(s: &str, out: &mut S) {
20 let bytes = s.as_bytes();
21 let mut run = 0;
22 while let Some(n) = scan::string_stop(&bytes[run..]) {
23 let i = run + n;
24 out.text(&s[run..i]);
25 escape(bytes[i], out);
26 run = i + 1;
27 }
28 out.text(&s[run..]);
29}
30
31fn escape<S: Sink>(b: u8, out: &mut S) {
33 const HEX: &[u8; 16] = b"0123456789abcdef";
34 match b {
35 b'"' => out.text("\\\""),
36 b'\\' => out.text("\\\\"),
37 b'\n' => out.text("\\n"),
38 b'\r' => out.text("\\r"),
39 b'\t' => out.text("\\t"),
40 b => {
41 let digits = [HEX[usize::from(b >> 4)], HEX[usize::from(b & 0xf)]];
42 out.text("\\u00");
43 out.text(std::str::from_utf8(&digits).unwrap_or_default());
44 }
45 }
46}
47
48#[cfg(test)]
49mod tests {
50 use super::{push_string, write_string};
51
52 fn text(s: &str) -> String {
53 let mut out = String::new();
54 write_string(s, &mut out);
55 out
56 }
57
58 #[test]
59 fn escapes_what_json_does() {
60 assert_eq!(text("plain"), "\"plain\"");
61 assert_eq!(text("a\"b\\c\nd\re\tf"), r#""a\"b\\c\nd\re\tf""#);
62 assert_eq!(text("\u{0}\u{1f}\u{8}"), r#""\u0000\u001f\u0008""#);
63 assert_eq!(text("日本語 naïve"), "\"日本語 naïve\"");
64 }
65
66 #[test]
67 fn short_and_long_strings_escape_alike() {
68 for len in [0, 1, 15, 16, 17, 40, 100] {
69 for at in 0..len {
70 let mut s = "x".repeat(len);
71 s.replace_range(at..=at, "\"");
72 let want = format!("\"{}\\\"{}\"", "x".repeat(at), "x".repeat(len - at - 1));
73 assert_eq!(text(&s), want);
74 let mut bytes = Vec::new();
75 push_string(&s, &mut bytes);
76 assert_eq!(bytes, want.as_bytes());
77 }
78 }
79 }
80}