Repository: dotenv-linter/dotenv-linter
Pull request #255 (2020-08-10 20:31:08)

Add trailing whitespace fix
Add `TrailingWhitespaceFixer` and included it in the `fixlist`.

Update `unfixed_warnings` and `multiple_files` tests to reflect the new
fix and to use `ExtraBlankLine` as an unfixed warning example.

<!--
Thank you for your pull request. Please provide a description above and review
the requirements below.

Bug fixes and new features should include tests.
-->

<!-- _Please make sure to review and check all of these items:_ -->

#### ✔ Checklist:
<!-- Remove items that do not apply. For completed items, change [ ] to [x]. -->

- [x] This PR has been added to [CHANGELOG.md](https://github.com/dotenv-linter/dotenv-linter/blob/master/CHANGELOG.md) (at the top of the list);
- [x] Tests for the changes have been added (for bug fixes / features);
- [ ] Docs have been added / updated (for bug fixes / features).

<!-- _NOTE: these things are not required to open a PR and can be done afterwards / while the PR is open._ -->


gregcline: I'm willing to add something to the docs, but I'm not sure what the vision for that looks like since there isn't a doc for the `LowerCaseKey` fix yet.

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 363013b..96d5a9a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,7 @@ # Changelog
 
 ## [Unreleased]
 ### 🚀 Added
+- Add autofix for TrailingWhitespace [#255](https://github.com/dotenv-linter/dotenv-linter/pull/255) ([@gregcline](https://github.com/gregcline))
 - Add fixer: KeyWithoutValueFixer [#254](https://github.com/dotenv-linter/dotenv-linter/pull/254) ([@unexge](https://github.com/unexge))
 - Add fixer: SpaceCharacterFixer [#253](https://github.com/dotenv-linter/dotenv-linter/pull/253) ([@DDtKey](https://github.com/DDtKey))
 - Add total problems to output and `--quiet` argument [#242](https://github.com/dotenv-linter/dotenv-linter/pull/242) ([@wesleimp](https://github.com/wesleimp), [@mgrachev](https://github.com/mgrachev))
diff --git a/src/fixes.rs b/src/fixes.rs
index 9020edd..edb5ecf 100644
--- a/src/fixes.rs
+++ b/src/fixes.rs
@@ -3,6 +3,7 @@
 mod key_without_value;
 mod lowercase_key;
 mod space_character;
+mod trailing_whitespace;
 
 trait Fix {
     fn name(&self) -> &str;
@@ -38,6 +39,7 @@ fn fixlist() -> Vec<Box<dyn Fix>> {
         Box::new(key_without_value::KeyWithoutValueFixer::default()),
         Box::new(lowercase_key::LowercaseKeyFixer::default()),
         Box::new(space_character::SpaceCharacterFixer::default()),
+        Box::new(trailing_whitespace::TrailingWhitespaceFixer::default()),
         // Then we should run the fixers that handle the line entry collection at whole.
         // And at the end we should run the fixer for ExtraBlankLine check (because the previous
         // fixers can create additional extra blank lines).
diff --git a/src/fixes/trailing_whitespace.rs b/src/fixes/trailing_whitespace.rs
new file mode 100644
index 0000000..3b12c3f
--- /dev/null
+++ b/src/fixes/trailing_whitespace.rs
@@ -0,0 +1,93 @@
+use super::Fix;
+use crate::common::*;
+
+pub(crate) struct TrailingWhitespaceFixer<'a> {
+    name: &'a str,
+}
+
+impl Default for TrailingWhitespaceFixer<'_> {
+    fn default() -> Self {
+        Self {
+            name: "TrailingWhitespace",
+        }
+    }
+}
+
+impl Fix for TrailingWhitespaceFixer<'_> {
+    fn name(&self) -> &str {
+        self.name
+    }
+
+    fn fix_line(&self, line: &mut LineEntry) -> Option<()> {
+        line.raw_string = line.raw_string.trim_end().to_string();
+
+        Some(())
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use std::path::PathBuf;
+
+    #[test]
+    fn fix_line_test() {
+        let fixer = TrailingWhitespaceFixer::default();
+
+        let mut line = LineEntry {
+            number: 1,
+            file: FileEntry {
+                path: PathBuf::from(".env"),
+                file_name: ".env".to_string(),
+                total_lines: 1,
+            },
+            raw_string: String::from("DEBUG_HTTP=true  "),
+        };
+
+        assert_eq!(Some(()), fixer.fix_line(&mut line));
+        assert_eq!("DEBUG_HTTP=true", line.raw_string);
+    }
+
+    #[test]
+    fn fix_warnings_test() {
+        let fixer = TrailingWhitespaceFixer::default();
+        let mut lines = vec![
+            LineEntry {
+                number: 1,
+                file: FileEntry {
+                    path: PathBuf::from(".env"),
+                    file_name: ".env".to_string(),
+                    total_lines: 3,
+                },
+                raw_string: String::from("FOO=BAR "),
+            },
+            LineEntry {
+                number: 2,
+                file: FileEntry {
+                    path: PathBuf::from(".env"),
+                    file_name: ".env".to_string(),
+                    total_lines: 3,
+                },
+                raw_string: String::from("Z=Y"),
+            },
+            LineEntry {
+                number: 3,
+                file: FileEntry {
+                    path: PathBuf::from(".env"),
+                    file_name: ".env".to_string(),
+                    total_lines: 3,
+                },
+                raw_string: String::from("\n"),
+            },
+        ];
+        let mut warning = Warning::new(
+            lines[0].clone(),
+            "TrailingWhitespace",
+            String::from("Trailing whitespace detected"),
+        );
+
+        assert_eq!(Some(1), fixer.fix_warnings(vec![&mut warning], &mut lines));
+        assert_eq!("FOO=BAR", lines[0].raw_string);
+        assert!(warning.is_fixed);
+    }
+}


Test patch:
diff --git a/tests/fixes/mod.rs b/tests/fixes/mod.rs
index f7d2475..0cdff80 100644
--- a/tests/fixes/mod.rs
+++ b/tests/fixes/mod.rs
@@ -1,6 +1,7 @@
 use crate::common::TestDir;
 
 mod space_character;
+mod trailing_whitespace;
 
 #[test]
 fn correct_file() {
@@ -48,18 +49,19 @@ fn key_without_value() {
 #[test]
 fn unfixed_warnings() {
     let testdir = TestDir::new();
-    let testfile = testdir.create_testfile(".env", "A=DEF\nB=BAR \nf=BAR\n");
+    let testfile = testdir.create_testfile(".env", "A=DEF\nB=BAR \nf=BAR\n\n");
 
     let expected_output = String::from(
         "Fixed warnings:\n\
+        .env:2 TrailingWhitespace: Trailing whitespace detected\n\
         .env:3 LowercaseKey: The f key should be in uppercase\n\
         \n\
         Unfixed warnings:\n\
-        .env:2 TrailingWhitespace: Trailing whitespace detected\n",
+        .env:5 ExtraBlankLine: Extra blank line detected\n",
     );
     testdir.test_command_fix_fail(expected_output);
 
-    assert_eq!(testfile.contents().as_str(), "A=DEF\nB=BAR \nF=BAR\n");
+    assert_eq!(testfile.contents().as_str(), "A=DEF\nB=BAR\nF=BAR\n\n");
 
     testdir.close();
 }
@@ -70,21 +72,22 @@ fn multiple_files() {
 
     let testfile1 = testdir.create_testfile("1.env", "AB=DEF\nD=BAR\n\nF=BAR\n");
     let testfile2 = testdir.create_testfile("2.env", "abc=DEF\n\nF=BAR\n");
-    let testfile3 = testdir.create_testfile("3.env", "A=b \nab=DEF\n");
+    let testfile3 = testdir.create_testfile("3.env", "A=b \nab=DEF\n\n");
 
     let expected_output = String::from(
         "Fixed warnings:\n\
         2.env:1 LowercaseKey: The abc key should be in uppercase\n\
+        3.env:1 TrailingWhitespace: Trailing whitespace detected\n\
         3.env:2 LowercaseKey: The ab key should be in uppercase\n\
         \n\
         Unfixed warnings:\n\
-        3.env:1 TrailingWhitespace: Trailing whitespace detected\n",
+        3.env:4 ExtraBlankLine: Extra blank line detected\n",
     );
     testdir.test_command_fix_fail(expected_output);
 
     assert_eq!(testfile1.contents().as_str(), "AB=DEF\nD=BAR\n\nF=BAR\n");
     assert_eq!(testfile2.contents().as_str(), "ABC=DEF\n\nF=BAR\n");
-    assert_eq!(testfile3.contents().as_str(), "A=b \nAB=DEF\n");
+    assert_eq!(testfile3.contents().as_str(), "A=b\nAB=DEF\n\n");
 
     testdir.close();
 }
diff --git a/tests/fixes/trailing_whitespace.rs b/tests/fixes/trailing_whitespace.rs
new file mode 100644
index 0000000..6a73d5a
--- /dev/null
+++ b/tests/fixes/trailing_whitespace.rs
@@ -0,0 +1,17 @@
+use crate::common::TestDir;
+
+#[test]
+fn trailing_whitespace() {
+    let testdir = TestDir::new();
+    let testfile = testdir.create_testfile(".env", "ABC=DEF \n\nFOO=BAR   \n");
+    let expected_output = String::from(
+        "Fixed warnings:\n\
+        .env:1 TrailingWhitespace: Trailing whitespace detected\n\
+        .env:3 TrailingWhitespace: Trailing whitespace detected\n",
+    );
+    testdir.test_command_fix_success(expected_output);
+
+    assert_eq!(testfile.contents().as_str(), "ABC=DEF\n\nFOO=BAR\n");
+
+    testdir.close();
+}