cargo_cargofmt/formatting/
space_separators.rs

1use 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            // TODO
265            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            // TODO
280            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}