cargo_cargofmt/formatting/
space_separators.rs1use crate::toml::TokenKind;
2use crate::toml::TomlToken;
3
4#[tracing::instrument]
5pub fn normalize_space_separators(tokens: &mut crate::toml::TomlTokens<'_>) {
6 let mut indices = crate::toml::TokenIndices::new();
7 while let Some(mut i) = indices.next_index(tokens) {
8 match tokens.tokens[i].kind {
9 TokenKind::StdTableOpen | TokenKind::ArrayTableOpen | TokenKind::ArrayOpen => {
10 let next_i = i + 1;
11 if let Some(next) = tokens.tokens.get(next_i) {
12 if matches!(next.kind, TokenKind::Whitespace) {
13 tokens.tokens[next_i] = TomlToken::EMPTY;
14 }
15 }
16 }
17 TokenKind::StdTableClose | TokenKind::ArrayTableClose | TokenKind::ArrayClose => {
18 if let Some(prev_i) = i.checked_sub(1) {
19 if matches!(tokens.tokens[prev_i].kind, TokenKind::Whitespace) {
20 tokens.tokens[prev_i] = TomlToken::EMPTY;
21 }
22 }
23 }
24 TokenKind::InlineTableOpen => {
25 let next_i = i + 1;
26 if let Some(next) = tokens.tokens.get(next_i) {
27 if matches!(next.kind, TokenKind::Whitespace) {
28 tokens.tokens[next_i] = TomlToken::SPACE;
29 } else if matches!(next.kind, TokenKind::SimpleKey) {
30 tokens.tokens.insert(next_i, TomlToken::SPACE);
31 }
32 }
33 }
34 TokenKind::InlineTableClose => {
35 if let Some(prev_i) = i.checked_sub(1) {
36 let prev = &tokens.tokens[prev_i];
37 if matches!(prev.kind, TokenKind::Whitespace) {
38 if prev_i
39 .checked_sub(1)
40 .map(|prev_prev_i| {
41 matches!(
42 tokens.tokens[prev_prev_i].kind,
43 TokenKind::InlineTableOpen
44 )
45 })
46 .unwrap_or(false)
47 {
48 tokens.tokens[prev_i] = TomlToken::EMPTY;
49 } else {
50 tokens.tokens[prev_i] = TomlToken::SPACE;
51 }
52 } else if matches!(prev.kind, TokenKind::Scalar | TokenKind::ValueSep) {
53 tokens.tokens.insert(i, TomlToken::SPACE);
54 }
55 }
56 }
57 TokenKind::SimpleKey => {}
58 TokenKind::KeySep => {
59 if let Some(prev_i) = i.checked_sub(1) {
60 if matches!(tokens.tokens[prev_i].kind, TokenKind::Whitespace) {
61 tokens.tokens[prev_i] = TomlToken::EMPTY;
62 }
63 }
64 let next_i = i + 1;
65 if let Some(next) = tokens.tokens.get(next_i) {
66 if matches!(next.kind, TokenKind::Whitespace) {
67 tokens.tokens[next_i] = TomlToken::EMPTY;
68 }
69 }
70 }
71 TokenKind::KeyValSep => {
72 if let Some(key_i) = indices.rev().skip(1).find(|i| {
73 !matches!(
74 tokens.tokens[*i].kind,
75 TokenKind::Whitespace | TokenKind::Newline | TokenKind::Comment
76 )
77 }) {
78 let mut new_i = key_i + 1;
79 if matches!(tokens.tokens[new_i].kind, TokenKind::Whitespace) {
80 new_i += 1;
81 }
82 let token = tokens.tokens.remove(i);
83 tokens.tokens.insert(new_i, token);
84 indices.set_next_index(new_i + 1);
85 i = new_i;
86 }
87 if let Some(prev_i) = i.checked_sub(1) {
88 if matches!(tokens.tokens[prev_i].kind, TokenKind::Whitespace) {
89 tokens.tokens[prev_i] = TomlToken::SPACE;
90 } else if matches!(tokens.tokens[prev_i].kind, TokenKind::SimpleKey) {
91 tokens.tokens.insert(i, TomlToken::SPACE);
92 }
93 }
94 let next_i = i + 1;
95 if let Some(next) = tokens.tokens.get(next_i) {
96 if matches!(next.kind, TokenKind::Whitespace) {
97 tokens.tokens[next_i] = TomlToken::SPACE;
98 } else if matches!(next.kind, TokenKind::Scalar) {
99 tokens.tokens.insert(next_i, TomlToken::SPACE);
100 }
101 }
102 }
103 TokenKind::Scalar => {}
104 TokenKind::ValueSep => {
105 if let Some(value_i) = indices.rev().skip(1).find(|i| {
106 !matches!(
107 tokens.tokens[*i].kind,
108 TokenKind::Whitespace | TokenKind::Newline | TokenKind::Comment
109 )
110 }) {
111 let mut new_i = value_i + 1;
112 if matches!(tokens.tokens[new_i].kind, TokenKind::Whitespace) {
113 new_i += 1;
114 }
115 let token = tokens.tokens.remove(i);
116 tokens.tokens.insert(new_i, token);
117 indices.set_next_index(new_i + 1);
118 i = new_i;
119 }
120 if let Some(prev_i) = i.checked_sub(1) {
121 if matches!(tokens.tokens[prev_i].kind, TokenKind::Whitespace) {
122 tokens.tokens[prev_i] = TomlToken::EMPTY;
123 }
124 }
125 let next_i = i + 1;
126 if let Some(next) = tokens.tokens.get(next_i) {
127 if matches!(next.kind, TokenKind::Whitespace) {
128 tokens.tokens[next_i] = TomlToken::SPACE;
129 } else if matches!(next.kind, TokenKind::SimpleKey | TokenKind::Scalar) {
130 tokens.tokens.insert(next_i, TomlToken::SPACE);
131 }
132 }
133 }
134 TokenKind::Whitespace => {}
135 TokenKind::Comment => {
136 if let Some(prev_i) = i.checked_sub(1) {
137 if matches!(tokens.tokens[prev_i].kind, TokenKind::Whitespace) {
138 tokens.tokens[prev_i] = TomlToken::SPACE;
139 } else if !matches!(tokens.tokens[prev_i].kind, TokenKind::Newline) {
140 tokens.tokens.insert(i, TomlToken::SPACE);
141 }
142 }
143 }
144 TokenKind::Newline => {}
145 TokenKind::Error => {}
146 }
147 }
148 tokens.trim_empty_whitespace();
149}
150
151#[cfg(test)]
152mod test {
153 use snapbox::assert_data_eq;
154 use snapbox::str;
155 use snapbox::IntoData;
156
157 #[track_caller]
158 fn valid(input: &str, expected: impl IntoData) {
159 let mut tokens = crate::toml::TomlTokens::parse(input);
160 super::normalize_space_separators(&mut tokens);
161 let actual = tokens.to_string();
162
163 assert_data_eq!(&actual, expected);
164
165 let (_, errors) = toml::de::DeTable::parse_recoverable(&actual);
166 if !errors.is_empty() {
167 use std::fmt::Write as _;
168 let mut result = String::new();
169 writeln!(&mut result, "---").unwrap();
170 for error in errors {
171 writeln!(&mut result, "{error}").unwrap();
172 writeln!(&mut result, "---").unwrap();
173 }
174 panic!("failed to parse\n---\n{actual}\n{result}");
175 }
176 }
177
178 #[test]
179 fn empty() {
180 valid("", str![]);
181 }
182
183 #[test]
184 fn key_value_without_spaces() {
185 valid("key=5", str!["key = 5"]);
186 }
187
188 #[test]
189 fn key_value_with_extra_spaces() {
190 valid("key = 5", str!["key = 5"]);
191 }
192
193 #[test]
194 fn key_value_with_tab() {
195 valid("key\t=\t5", str!["key = 5"]);
196 }
197
198 #[test]
199 fn comment_without_spaces() {
200 valid("key = 5#Hello", str!["key = 5 #Hello"]);
201 }
202
203 #[test]
204 fn comment_with_extra_spaces() {
205 valid("key = 5 # Hello", str!["key = 5 # Hello"]);
206 }
207
208 #[test]
209 fn comment_with_tab() {
210 valid("key = 5\t#\tHello", str!["key = 5 # Hello"]);
211 }
212
213 #[test]
214 fn array_empty() {
215 valid("key = []", str!["key = []"]);
216 }
217
218 #[test]
219 fn array_spaces() {
220 valid("key = [ ]", str!["key = []"]);
221 }
222
223 #[test]
224 fn array_tab() {
225 valid("key = [\t]", str!["key = []"]);
226 }
227
228 #[test]
229 fn array_value_without_spaces() {
230 valid("key = [5]", str!["key = [5]"]);
231 }
232
233 #[test]
234 fn array_value_with_extra_spaces() {
235 valid("key = [ 5 ]", str!["key = [5]"]);
236 }
237
238 #[test]
239 fn array_value_with_tab() {
240 valid("key = [\t5\t]", str!["key = [5]"]);
241 }
242
243 #[test]
244 fn value_sep_without_spaces() {
245 valid("key = [5,6]", str!["key = [5, 6]"]);
246 }
247
248 #[test]
249 fn value_sep_with_extra_spaces() {
250 valid("key = [5 , 6]", str!["key = [5, 6]"]);
251 }
252
253 #[test]
254 fn value_sep_with_tab() {
255 valid("key = [5\t,\t6]", str!["key = [5, 6]"]);
256 }
257
258 #[test]
259 fn value_sep_with_newline() {
260 valid(
261 "key = [5
262,
2636]",
264 str![[r#"
266key = [5,
267
2686]
269"#]],
270 );
271 }
272
273 #[test]
274 fn value_sep_with_comment() {
275 valid(
276 "key = [5 # hello
277, # goodbye
2786]",
279 str![[r#"
281key = [5, # hello
282 # goodbye
2836]
284"#]],
285 );
286 }
287
288 #[test]
289 fn value_sep_trailing_without_spaces() {
290 valid("key = [5,]", str!["key = [5,]"]);
291 }
292
293 #[test]
294 fn value_sep_trailing_with_extra_spaces() {
295 valid("key = [5 , ]", str!["key = [5,]"]);
296 }
297
298 #[test]
299 fn value_sep_trailing_with_tab() {
300 valid("key = [5\t,\t]", str!["key = [5,]"]);
301 }
302
303 #[test]
304 fn inline_table_empty() {
305 valid("key = {}", str!["key = {}"]);
306 }
307
308 #[test]
309 fn inline_table_spaces() {
310 valid("key = { }", str!["key = {}"]);
311 }
312
313 #[test]
314 fn inline_table_tab() {
315 valid("key = {\t}", str!["key = {}"]);
316 }
317
318 #[test]
319 fn inline_table_value_without_spaces() {
320 valid("key = {key=5}", str!["key = { key = 5 }"]);
321 }
322
323 #[test]
324 fn inline_table_value_with_extra_spaces() {
325 valid("key = { key = 5 }", str!["key = { key = 5 }"]);
326 }
327
328 #[test]
329 fn inline_table_value_with_tab() {
330 valid("key = {\tkey\t=\t5\t}", str!["key = { key = 5 }"]);
331 }
332
333 #[test]
334 fn inline_table_sep_without_spaces() {
335 valid("key = {a=5,b=6}", str!["key = { a = 5, b = 6 }"]);
336 }
337
338 #[test]
339 fn inline_table_sep_with_extra_spaces() {
340 valid("key = {a=5 , b=6}", str!["key = { a = 5, b = 6 }"]);
341 }
342
343 #[test]
344 fn inline_table_sep_with_tab() {
345 valid("key = {a=5\t,\tb=6}", str!["key = { a = 5, b = 6 }"]);
346 }
347
348 #[test]
349 fn table_without_spaces() {
350 valid("[key]", str!["[key]"]);
351 }
352
353 #[test]
354 fn table_with_extra_spaces() {
355 valid("[ key ]", str!["[key]"]);
356 }
357
358 #[test]
359 fn table_with_tab() {
360 valid("[\tkey\t]", str!["[key]"]);
361 }
362
363 #[test]
364 fn array_of_tables_without_spaces() {
365 valid("[[key]]", str!["[[key]]"]);
366 }
367
368 #[test]
369 fn array_of_tables_with_extra_spaces() {
370 valid("[[ key ]]", str!["[[key]]"]);
371 }
372
373 #[test]
374 fn array_of_tables_with_tab() {
375 valid("[[\tkey\t]]", str!["[[key]]"]);
376 }
377
378 #[test]
379 fn key_sep_without_spaces() {
380 valid("a.b = 5", str!["a.b = 5"]);
381 }
382
383 #[test]
384 fn key_sep_with_extra_spaces() {
385 valid("a . b = 5", str!["a.b = 5"]);
386 }
387
388 #[test]
389 fn key_sep_with_tab() {
390 valid("a\t.\tb = 5", str!["a.b = 5"]);
391 }
392}