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