time_macros/helpers/
string.rs1use std::ops::{Index, RangeFrom};
2
3use proc_macro::Span;
4
5use crate::Error;
6
7pub(crate) fn parse(
8 permit_byte_strings: bool,
9 token: &proc_macro::Literal,
10) -> Result<(Span, Vec<u8>), Error> {
11 let span = token.span();
12 let repr = token.to_string();
13
14 match repr.as_bytes() {
15 [b'"', ..] => Ok((span, parse_lit_str_cooked(&repr[1..]))),
16 [b'b', b'"', ..] if !permit_byte_strings => Err(Error::ByteStringNotPermitted {
17 span_start: Some(span),
18 span_end: Some(span),
19 }),
20 [b'b', b'"', rest @ ..] => Ok((span, parse_lit_byte_str_cooked(rest))),
21 [b'r', rest @ ..] => Ok((span, parse_lit_str_raw(rest))),
22 [b'b', b'r', ..] if !permit_byte_strings => Err(Error::ByteStringNotPermitted {
23 span_start: Some(span),
24 span_end: Some(span),
25 }),
26 [b'b', b'r', rest @ ..] => Ok((span, parse_lit_str_raw(rest))),
27 _ => Err(Error::ExpectedString {
28 span_start: Some(span),
29 span_end: Some(span),
30 }),
31 }
32}
33
34fn byte(s: impl AsRef<[u8]>, idx: usize) -> u8 {
35 s.as_ref().get(idx).copied().unwrap_or_default()
36}
37
38fn parse_lit_str_cooked(mut s: &str) -> Vec<u8> {
39 let mut content = String::new();
40 'outer: loop {
41 let ch = match byte(s, 0) {
42 b'"' => break,
43 b'\\' => {
44 let b = byte(s, 1);
45 s = &s[2..];
46 match b {
47 b'x' => {
48 let (byte, rest) = backslash_x(s);
49 s = rest;
50 char::from_u32(u32::from(byte)).expect("byte was just validated")
51 }
52 b'u' => {
53 let (chr, rest) = backslash_u(s);
54 s = rest;
55 chr
56 }
57 b'n' => '\n',
58 b'r' => '\r',
59 b't' => '\t',
60 b'\\' => '\\',
61 b'0' => '\0',
62 b'\'' => '\'',
63 b'"' => '"',
64 b'\r' | b'\n' => loop {
65 let ch = s.chars().next().unwrap_or_default();
66 if ch.is_whitespace() {
67 s = &s[ch.len_utf8()..];
68 } else {
69 continue 'outer;
70 }
71 },
72 _ => bug!("invalid escape"),
73 }
74 }
75 b'\r' => {
76 s = &s[2..];
78 '\n'
79 }
80 _ => {
81 let ch = s.chars().next().unwrap_or_default();
82 s = &s[ch.len_utf8()..];
83 ch
84 }
85 };
86 content.push(ch);
87 }
88
89 content.into_bytes()
90}
91
92fn parse_lit_str_raw(s: &[u8]) -> Vec<u8> {
93 let mut pounds = 0;
94 while byte(s, pounds) == b'#' {
95 pounds += 1;
96 }
97 let close = s
98 .iter()
99 .rposition(|&b| b == b'"')
100 .expect("had a string without trailing \"");
101
102 s[pounds + 1..close].to_owned()
103}
104
105fn parse_lit_byte_str_cooked(mut v: &[u8]) -> Vec<u8> {
106 let mut out = Vec::new();
107 'outer: loop {
108 let byte = match byte(v, 0) {
109 b'"' => break,
110 b'\\' => {
111 let b = byte(v, 1);
112 v = &v[2..];
113 match b {
114 b'x' => {
115 let (byte, rest) = backslash_x(v);
116 v = rest;
117 byte
118 }
119 b'n' => b'\n',
120 b'r' => b'\r',
121 b't' => b'\t',
122 b'\\' => b'\\',
123 b'0' => b'\0',
124 b'\'' => b'\'',
125 b'"' => b'"',
126 b'\r' | b'\n' => loop {
127 let byte = byte(v, 0);
128 let ch = char::from_u32(u32::from(byte)).expect("invalid byte");
129 if ch.is_whitespace() {
130 v = &v[1..];
131 } else {
132 continue 'outer;
133 }
134 },
135 _ => bug!("invalid escape"),
136 }
137 }
138 b'\r' => {
139 v = &v[2..];
141 b'\n'
142 }
143 b => {
144 v = &v[1..];
145 b
146 }
147 };
148 out.push(byte);
149 }
150
151 out
152}
153
154fn backslash_x<S>(s: &S) -> (u8, &S)
155where
156 S: Index<RangeFrom<usize>, Output = S> + AsRef<[u8]> + ?Sized,
157{
158 let mut ch = 0;
159 let b0 = byte(s, 0);
160 let b1 = byte(s, 1);
161 ch += 0x10 * (b0 - b'0');
162 ch += match b1 {
163 b'0'..=b'9' => b1 - b'0',
164 b'a'..=b'f' => 10 + (b1 - b'a'),
165 b'A'..=b'F' => 10 + (b1 - b'A'),
166 _ => bug!("invalid hex escape"),
167 };
168 (ch, &s[2..])
169}
170
171fn backslash_u(mut s: &str) -> (char, &str) {
172 s = &s[1..];
173
174 let mut ch = 0;
175 let mut digits = 0;
176 loop {
177 let b = byte(s, 0);
178 let digit = match b {
179 b'0'..=b'9' => b - b'0',
180 b'a'..=b'f' => 10 + b - b'a',
181 b'A'..=b'F' => 10 + b - b'A',
182 b'_' if digits > 0 => {
183 s = &s[1..];
184 continue;
185 }
186 b'}' if digits != 0 => break,
187 _ => bug!("invalid unicode escape"),
188 };
189 ch *= 0x10;
190 ch += u32::from(digit);
191 digits += 1;
192 s = &s[1..];
193 }
194 s = &s[1..];
195
196 (
197 char::from_u32(ch).expect("invalid unicode escape passed by compiler"),
198 s,
199 )
200}