/rust/registry/src/index.crates.io-1949cf8c6b5b557f/csv-1.4.0/src/debug.rs
Line | Count | Source |
1 | | /// A type that provides a human readable debug impl for arbitrary bytes. |
2 | | /// |
3 | | /// This generally works best when the bytes are presumed to be mostly UTF-8, |
4 | | /// but will work for anything. |
5 | | /// |
6 | | /// N.B. This is copied nearly verbatim from regex-automata. Sigh. |
7 | | pub(crate) struct Bytes<'a>(pub(crate) &'a [u8]); |
8 | | |
9 | | impl<'a> core::fmt::Debug for Bytes<'a> { |
10 | 0 | fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { |
11 | 0 | write!(f, "\"")?; |
12 | | // This is a sad re-implementation of a similar impl found in bstr. |
13 | 0 | let mut bytes = self.0; |
14 | 0 | while let Some(result) = utf8_decode(bytes) { |
15 | 0 | let ch = match result { |
16 | 0 | Ok(ch) => ch, |
17 | 0 | Err(byte) => { |
18 | 0 | write!(f, r"\x{:02x}", byte)?; |
19 | 0 | bytes = &bytes[1..]; |
20 | 0 | continue; |
21 | | } |
22 | | }; |
23 | 0 | bytes = &bytes[ch.len_utf8()..]; |
24 | 0 | match ch { |
25 | 0 | '\0' => write!(f, "\\0")?, |
26 | | // ASCII control characters except \0, \n, \r, \t |
27 | 0 | '\x01'..='\x08' |
28 | | | '\x0b' |
29 | | | '\x0c' |
30 | 0 | | '\x0e'..='\x19' |
31 | | | '\x7f' => { |
32 | 0 | write!(f, "\\x{:02x}", u32::from(ch))?; |
33 | | } |
34 | | '\n' | '\r' | '\t' | _ => { |
35 | 0 | write!(f, "{}", ch.escape_debug())?; |
36 | | } |
37 | | } |
38 | | } |
39 | 0 | write!(f, "\"")?; |
40 | 0 | Ok(()) |
41 | 0 | } |
42 | | } |
43 | | |
44 | | /// Decodes the next UTF-8 encoded codepoint from the given byte slice. |
45 | | /// |
46 | | /// If no valid encoding of a codepoint exists at the beginning of the given |
47 | | /// byte slice, then the first byte is returned instead. |
48 | | /// |
49 | | /// This returns `None` if and only if `bytes` is empty. |
50 | 0 | pub(crate) fn utf8_decode(bytes: &[u8]) -> Option<Result<char, u8>> { |
51 | 0 | fn len(byte: u8) -> Option<usize> { |
52 | 0 | if byte <= 0x7F { |
53 | 0 | Some(1) |
54 | 0 | } else if byte & 0b1100_0000 == 0b1000_0000 { |
55 | 0 | None |
56 | 0 | } else if byte <= 0b1101_1111 { |
57 | 0 | Some(2) |
58 | 0 | } else if byte <= 0b1110_1111 { |
59 | 0 | Some(3) |
60 | 0 | } else if byte <= 0b1111_0111 { |
61 | 0 | Some(4) |
62 | | } else { |
63 | 0 | None |
64 | | } |
65 | 0 | } |
66 | | |
67 | 0 | if bytes.is_empty() { |
68 | 0 | return None; |
69 | 0 | } |
70 | 0 | let len = match len(bytes[0]) { |
71 | 0 | None => return Some(Err(bytes[0])), |
72 | 0 | Some(len) if len > bytes.len() => return Some(Err(bytes[0])), |
73 | 0 | Some(1) => return Some(Ok(char::from(bytes[0]))), |
74 | 0 | Some(len) => len, |
75 | | }; |
76 | 0 | match core::str::from_utf8(&bytes[..len]) { |
77 | 0 | Ok(s) => Some(Ok(s.chars().next().unwrap())), |
78 | 0 | Err(_) => Some(Err(bytes[0])), |
79 | | } |
80 | 0 | } |