cargo_cargofmt/formatting/
trailing_spaces.rs

1use crate::toml::TomlToken;
2use std::borrow::Cow;
3
4#[tracing::instrument]
5pub fn trim_trailing_spaces(tokens: &mut crate::toml::TomlTokens<'_>) {
6    if let Some(last) = tokens.tokens.last_mut() {
7        if last.kind == crate::toml::TokenKind::Whitespace {
8            *last = TomlToken::EMPTY;
9        }
10    }
11    for i in tokens.indices() {
12        if tokens.tokens[i].kind != crate::toml::TokenKind::Newline {
13            continue;
14        }
15        let Some(prev_i) = i.checked_sub(1) else {
16            continue;
17        };
18        if tokens.tokens[prev_i].kind != crate::toml::TokenKind::Whitespace {
19            continue;
20        }
21        tokens.tokens[prev_i] = TomlToken::EMPTY;
22    }
23    tokens.trim_empty_whitespace();
24
25    for i in tokens.indices() {
26        if tokens.tokens[i].kind != crate::toml::TokenKind::Comment {
27            continue;
28        }
29        tokens.tokens[i].raw = match std::mem::take(&mut tokens.tokens[i].raw) {
30            Cow::Borrowed(s) => Cow::Borrowed(s.trim_end()),
31            Cow::Owned(mut s) => {
32                let trimmed = s.trim_end();
33                s.replace_range(0..trimmed.len(), "");
34                Cow::Owned(s)
35            }
36        }
37    }
38}
39
40#[cfg(test)]
41mod test {
42    use snapbox::assert_data_eq;
43    use snapbox::str;
44    use snapbox::IntoData;
45
46    #[track_caller]
47    fn valid(input: &str, expected: impl IntoData) {
48        let mut tokens = crate::toml::TomlTokens::parse(input);
49        super::trim_trailing_spaces(&mut tokens);
50        let actual = tokens.to_string();
51
52        assert_data_eq!(&actual, expected);
53
54        let (_, errors) = toml::de::DeTable::parse_recoverable(&actual);
55        if !errors.is_empty() {
56            use std::fmt::Write as _;
57            let mut result = String::new();
58            writeln!(&mut result, "---").unwrap();
59            for error in errors {
60                writeln!(&mut result, "{error}").unwrap();
61                writeln!(&mut result, "---").unwrap();
62            }
63            panic!("failed to parse\n---\n{actual}\n{result}");
64        }
65    }
66
67    #[test]
68    fn empty() {
69        valid("", str![]);
70    }
71
72    #[test]
73    fn whitespace() {
74        valid("    ", str![""]);
75    }
76
77    #[test]
78    fn leading_newline() {
79        valid(
80            "\n  \n  ",
81            str![[r#"
82
83
84
85"#]],
86        );
87    }
88
89    #[test]
90    fn trailing_newline() {
91        valid(
92            "  \n  \n",
93            str![[r#"
94
95
96
97"#]],
98        );
99    }
100
101    #[test]
102    fn trailing() {
103        valid(
104            r#"
105  
106after_value = "value"  
107after_comment = "value"  # Hello  
108[after_table]  
109after_array_bits = [  
110  1  ,  
111  2  ,  
112]  
113"#,
114            str![[r#"
115
116
117after_value = "value"
118after_comment = "value"  # Hello
119[after_table]
120after_array_bits = [
121  1  ,
122  2  ,
123]
124
125"#]],
126        );
127    }
128}