amiss_wire/human.rs
1pub const ATOM_SCALAR_BOUND: usize = 200;
2
3/// `human-atom-v1`: every repository-derived scalar is rendered as a
4/// double-quoted ASCII JSON-style string. At most the first two hundred
5/// Unicode scalar values are kept, with a literal `...` appended inside the
6/// quotes when any were omitted. Quote and backslash escape as `\"` and
7/// `\\`, printable ASCII stays literal, and every other scalar becomes a
8/// lowercase `\uXXXX` escape, non-BMP scalars as a UTF-16 surrogate pair, so
9/// CR, LF, tab, ESC, bidi controls, and ANSI bytes are never active terminal
10/// syntax.
11#[must_use]
12pub fn atom(text: &str) -> String {
13 let mut out = String::with_capacity(text.len().saturating_add(2));
14 out.push('"');
15 let mut omitted = false;
16 for (index, scalar) in text.chars().enumerate() {
17 if index >= ATOM_SCALAR_BOUND {
18 omitted = true;
19 break;
20 }
21 match scalar {
22 '"' => out.push_str("\\\""),
23 '\\' => out.push_str("\\\\"),
24 ' '..='~' => out.push(scalar),
25 _ => {
26 let mut units = [0_u16; 2];
27 for unit in scalar.encode_utf16(&mut units) {
28 out.push_str("\\u");
29 for shift in [12_u32, 8, 4, 0] {
30 let nibble = (u32::from(*unit) >> shift) & 0xf;
31 out.push(char::from_digit(nibble, 16).unwrap_or('0'));
32 }
33 }
34 }
35 }
36 }
37 if omitted {
38 out.push_str("...");
39 }
40 out.push('"');
41 out
42}
43
44/// `human-atom-bytes-v1`: a repository-derived value that is raw bytes, not
45/// text, rendered under the same law as [`atom`]. At most the first two
46/// hundred bytes are kept with a literal `...` appended inside the quotes
47/// when any were omitted; printable ASCII stays literal with quote and
48/// backslash escaped, and every other byte becomes the lowercase `\u00xx`
49/// escape of its value, so no byte is ever active terminal syntax and no
50/// Unicode scalar is invented for bytes that never encoded one.
51#[must_use]
52pub fn atom_bytes(bytes: &[u8]) -> String {
53 let mut out = String::with_capacity(bytes.len().saturating_add(2));
54 out.push('"');
55 let mut omitted = false;
56 for (index, byte) in bytes.iter().enumerate() {
57 if index >= ATOM_SCALAR_BOUND {
58 omitted = true;
59 break;
60 }
61 match byte {
62 b'"' => out.push_str("\\\""),
63 b'\\' => out.push_str("\\\\"),
64 b' '..=b'~' => out.push(char::from(*byte)),
65 _ => {
66 out.push_str("\\u00");
67 for shift in [4_u32, 0] {
68 let nibble = (u32::from(*byte) >> shift) & 0xf;
69 out.push(char::from_digit(nibble, 16).unwrap_or('0'));
70 }
71 }
72 }
73 }
74 if omitted {
75 out.push_str("...");
76 }
77 out.push('"');
78 out
79}