Skip to main content

substrate_data/forms/text/writer/
string.rs

1//! A string as a JSON string literal.
2
3use super::super::scan;
4use super::sink::Sink;
5
6/// `s` as a JSON string literal.
7pub fn write_string(s: &str, out: &mut String) {
8    push_string(s, out);
9}
10
11/// `s` as a JSON string literal, into `out`.
12pub(super) fn push_string<S: Sink>(s: &str, out: &mut S) {
13    out.text("\"");
14    write_body(s, out);
15    out.text("\"");
16}
17
18/// `s` as the inside of a JSON string literal, into `out`.
19pub(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
31/// The escape of `b`, a byte `needs_escape` is true of.
32fn 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}